. "$BST_LIB" # ldns 1.8.3 (meta-oe recipes-devtools/ldns) -- NLnet Labs' DNS library: the # wire-format layer under drill, unbound's tooling and nsd's tests. What it # offers is a typed representation of the DNS on both sides of the wire -- # resource records, packets, zone files -- with parsers into it and composers # out of it. # # WHAT SHIPS, AND WHY THAT SHAPES THIS SUITE # # The drill tool is off (the stock recipe's PACKAGECONFIG ??= "" leaves # PACKAGECONFIG[drill] unselected, so configure takes --without-drill), so the # whole installed payload is libldns.a, 33 headers, ldns.pc and ldns-config. # There is NO ldns binary on this image and no on-target C compiler, so a # shell-only suite could assert that those files exist and nothing more. # # For a wire-format library that is not coverage. Its failure mode is answering # WRONGLY -- an off-by-one in a length-prefixed label walk, a byte-order slip in # a 16-bit count, a rdata field stored as text where the wire wants four octets # -- and every one of those compiles, packages and installs perfectly. # BRINTOS-217 is the precedent this layer keeps citing: oniguruma did exactly # that on this arm and returned silently wrong offsets to every caller, so a # payload-only suite would have gone green on the one defect the port existed to # find. # # So data/ldns-probe is cross-linked with wasmcc-glibc against THE VERY ARCHIVE # THAT SHIPS (/usr/lib/libldns.a) and run here. Its source is src/ldns_probe.c. # It covers the two halves BRINTOS-944 scopes, both entirely offline -- there is # no resolver call and no socket anywhere in it: # # zone-* data/ldns-zone.txt is parsed with ldns_zone_new_frm_fp_l, its # record count and per-type census checked, the four representative # records the ticket names (A, AAAA, MX, TXT) checked field by field, # and the zone then WRITTEN BACK OUT and compared with the input. # That comparison is byte-exact rather than normalised, because the # fixture IS ldns's own canonical rendering -- generated by running # the parse/print cycle over a hand-written zone until it reached a # fixed point. A round-trip that reorders records, drops a TTL, # re-quotes a TXT string or emits a relative name fails it. # # a-rdf-size / aaaa-rdf-size / mx-pref-size # the on-the-wire width of the parsed rdata -- 4, 16 and 2 octets. # These are the cases that separate a real parse from a stringly-typed # one: an implementation that stored "192.0.2.80" as text would pass # a-addr, which renders the rdf back to text, and fail a-rdf-size. # # pkt* / hdr-* / q-* / wire-* # a query composed with ldns_pkt_query_new, rendered with # ldns_pkt2wire, parsed back with ldns_wire2pkt, and every header # field plus the question section compared against what was sent. # pkt2wire-qname-wire asserts the label encoding octet by octet # (3 www 7 example 3 com 0) and pkt2wire-id-bytes asserts the ID is # big-endian on the wire -- both are byte-order and framing checks # that a same-endian host round-trip would not catch on its own. # wire-roundtrip-bytes then re-renders the PARSED packet and memcmps # it against the original 33 bytes. # # EVERY EXPECTED VALUE IS THE VALUE A NATIVE x86_64 BUILD OF THE IDENTICAL # RELEASE PRODUCES -- same tarball sha256 c3f72dd1..7860, same three CVE # patches, configured with the same switches. That control scored 93/93, and it # was then mutation-tested rather than merely run: a changed A address reddens # only a-addr, a deleted MX reddens the count trio, a TXT collapsed to one # string reddens txt-rdcount and txt-str1, a changed TTL reddens aaaa-ttl, and # feeding the NON-canonical hand-written zone reddens exactly the two write-back # cases and nothing else. A suite that cannot fail is not evidence, so each of # those was checked to fail in the right place. # # WHY BUILTIN MATCHING INSTEAD OF check_out: the probe runs ONCE and its output # is read into one shell variable; every assertion is a `case` glob against it, # so there is no fork per case. On this runtime the forking is the cost, not the # work -- the cjson suite next door was killed by the per-suite bound at 156 # cases written as check_out+cat. incd=/usr/include/ldns lib=/usr/lib/libldns.a pc=/usr/lib/pkgconfig/ldns.pc cfg=/usr/bin/ldns-config nl=' ' # hasfile -- ok iff is a non-empty regular file. # has -- ok iff contains the literal . # hasline -- ok iff carries as a WHOLE line. hasfile() { if [ -s "$2" ]; then bst_ok "$1"; else bst_notok "$1" "absent or empty: $2"; fi; } has() { case "$3" in *"$2"*) bst_ok "$1" ;; *) bst_notok "$1" "not found: $2" ;; esac; } hasline() { case "$nl$3$nl" in *"$nl$2$nl"*) bst_ok "$1" ;; *) bst_notok "$1" "not in archive: $2" ;; esac; } # --- the shipped payload ----------------------------------------------------- hasfile ldns/archive "$lib" hasfile ldns/pkgconfig "$pc" hasfile ldns/ldns-config "$cfg" hasfile ldns/header-ldns "$incd/ldns.h" hasfile ldns/header-zone "$incd/zone.h" hasfile ldns/header-packet "$incd/packet.h" hasfile ldns/header-rr "$incd/rr.h" hasfile ldns/header-host2wire "$incd/host2wire.h" hasfile ldns/header-wire2host "$incd/wire2host.h" hasfile ldns/header-host2str "$incd/host2str.h" # 33 headers is the whole public surface of this release; a partial do_install # would still satisfy every hasfile above. nhdr=0 for h in "$incd"/*.h; do [ -f "$h" ] && nhdr=$((nhdr + 1)); done check_eq ldns/header-count 33 "$nhdr" # There is deliberately no ldns/no-shared-object case: shared objects are # refused distro-wide on this arm, so an absent libldns.so is the distro's # property and not this port's, and every sibling library would assert it too. pctext=$(cat "$pc" 2>/dev/null) has ldns/pc-name "Name: ldns" "$pctext" has ldns/pc-version "Version: 1.8.3" "$pctext" has ldns/pc-libs "-lldns" "$pctext" has ldns/pc-libdir "libdir=/usr/lib" "$pctext" # ldns-config is a /bin/sh script and this image's sh is busybox ash, so this is # also the one case that proves the shipped script actually runs here rather # than merely being present. do_install:append in the recipe rewrites its # CFLAGS to strip the build host's sysroot and prefix-map flags, so a leaked # build path would show up in --cflags. cfgver=$("$cfg" --version 2>&1) check_eq ldns/config-version "1.8.3" "$cfgver" cfgcflags=$("$cfg" --cflags 2>&1) check_eq ldns/config-cflags "-I/usr/include" "$cfgcflags" cfglibs=$("$cfg" --libs 2>&1) has ldns/config-libs "-lldns" "$cfglibs" case "$cfgcflags$cfglibs" in *recipe-sysroot*|*/tmp-glibc/*|*prefix-map*) bst_notok ldns/config-no-buildpath "build path leaked: $cfgcflags $cfglibs" ;; *) bst_ok ldns/config-no-buildpath ;; esac # --- symbol completeness, tracked from the INSTALLED headers ------------------ # Every ldns_* function the installed headers DECLARE must be present in the # installed archive, so a partial compile, a visibility mistake or a # header/library skew all fail this. The list is derived from the headers rather # than written here, so it survives a version bump. # # THREE EXCLUSIONS, EACH BY CONSTRUCTION AND NONE OF THEM TO MAKE THIS PASS. # All three were found by running this derivation against a native x86_64 # control build of the identical release, and each is asserted separately below # rather than merely dropped. # # 1. The INLINE set (53 functions). ldns marks a family of buffer, rdf and # output-format helpers `INLINE`, which is `static inline` -- they exist only # in the header and are compiled into each caller, so no archive symbol is # owed. This is upstream's own arrangement, not a build artifact, and the set # is derived from the headers below rather than listed, for the same reason # the whole list is. # # 2. ldns_gost2pkey_raw. dnssec.c compiles it under USE_GOST, and this port # passes --disable-gost -- see the ldns bbappend for why: the GOST gate is an # AC_RUN_IFELSE that no cross build can run, so it defaults to on and then # compiles ENGINE code against an OpenSSL that has none. Its absence is # therefore the on-target evidence that our switch took effect, which is what # ldns/gost-symbol-absent asserts. # # 3. ldns_key_new_frm_engine, AND THIS ONE IS AN UPSTREAM GUARD ASYMMETRY worth # naming because a consumer can trip over it. keys.c:110 guards the # DEFINITION with `#if defined(HAVE_SSL) && !defined(OPENSSL_NO_ENGINE)`, # while keys.h:226 guards the DECLARATION with HAVE_SSL alone. Against an # OpenSSL built no-engine -- which poky's is -- the header therefore promises # a function the archive does not carry, so code calling it COMPILES and # fails at link. Nothing about this arm participates: the same skew appears # on any platform whose OpenSSL is no-engine. Per # docs/conventions/no-public-upstream-reports.md it is not reported to NLnet # Labs, and it is not a brintOS defect either, so there is no brintos.io # issue -- it is recorded here and asserted, so a future ldns that fixes it # turns ldns/engine-symbol-absent red and tells us. arcsyms=$(grep -ao 'ldns_[a-z0-9_]*' "$lib" 2>/dev/null | sort -u) # Declarations only: drop preprocessor lines and comment bodies. Without the # comment filter, prose mentions of a function name ("see ldns_deep_free()") are # counted as declarations and report as missing symbols. decl=$(cat "$incd"/*.h 2>/dev/null | grep -vE '^[[:space:]]*[*]|^[[:space:]]*/\*|^[[:space:]]*#') hdrsyms=$(printf '%s\n' "$decl" | grep -oE '\bldns_[a-z0-9_]*\(' | sed 's/(//' | sort -u) # The INLINE set: the name sits on the INLINE line or the one after it. inlsyms=$(printf '%s\n' "$decl" | awk '/INLINE/{p=2} p>0{print; p--}' \ | grep -oE '\bldns_[a-z0-9_]*\(' | sed 's/(//' | sort -u) nsym=0; nmiss=0; missing="" for f in $hdrsyms; do case "$nl$inlsyms$nl" in *"$nl$f$nl"*) continue ;; esac case "$f" in ldns_gost2pkey_raw|ldns_key_new_frm_engine) continue ;; esac nsym=$((nsym + 1)) case "$nl$arcsyms$nl" in *"$nl$f$nl"*) ;; *) nmiss=$((nmiss + 1)); missing="$missing $f" ;; esac done if [ "$nmiss" -eq 0 ]; then bst_ok ldns/symbols-complete else bst_notok ldns/symbols-complete "$nmiss of $nsym missing:$missing" fi # Pins the derivation against going vacuous: a filter that accidentally matched # everything would leave nsym at 0 and report symbols-complete green. check_eq ldns/symbols-count 869 "$nsym" check_eq ldns/symbols-inline-count 53 "$(printf '%s\n' "$inlsyms" | wc -l | tr -d ' ')" # The two deliberate absences, asserted rather than assumed (see 2 and 3 above). case "$nl$arcsyms$nl" in *"${nl}ldns_gost2pkey_raw$nl"*) bst_notok ldns/gost-symbol-absent "ldns_gost2pkey_raw is in the archive -- --disable-gost did not take" ;; *) bst_ok ldns/gost-symbol-absent ;; esac case "$nl$arcsyms$nl" in *"${nl}ldns_key_new_frm_engine$nl"*) bst_notok ldns/engine-symbol-absent "ldns_key_new_frm_engine is present -- the OpenSSL under this build is no longer no-engine, so revisit exclusion 3" ;; *) bst_ok ldns/engine-symbol-absent ;; esac # A spot-check that the derivation above is really reading the archive and not # an empty string that would make the loop vacuously green. These five are the # exact entry points BRINTOS-944 names. hasline ldns/sym-zone_new_frm_fp ldns_zone_new_frm_fp "$arcsyms" hasline ldns/sym-pkt_query_new ldns_pkt_query_new "$arcsyms" hasline ldns/sym-pkt2wire ldns_pkt2wire "$arcsyms" hasline ldns/sym-wire2pkt ldns_wire2pkt "$arcsyms" hasline ldns/sym-zone_print ldns_zone_print "$arcsyms" # --- the bool ABI tripwire, which reaches every consumer of ldns-dev ---------- # ldns/common.h cannot know how the library was configured -- its own comment # says "HAVE_STDBOOL_H is not available when distributed as a library" -- so # when __bool_true_false_are_defined is unset it falls back to # `#define _Bool signed char`. The toolchain here defaults to C17, where bool is # not a keyword, so any consumer that includes WITHOUT # first declares every bool-taking entry point with `signed char` # while libldns.a defines it with `_Bool`. Under -flto on a strongly-typed # target, wasm-ld resolves that collision by pointing the call at a trap stub, # and the first such call dies with `RuntimeError: unreachable` at # .Lldns_pkt_set_cd_bitcast_invalid -- after compiling and linking without one # diagnostic. On a native platform the same mistake is invisible, which is why # this is worth a case rather than a comment. # # src/ldns_probe.c includes for exactly this reason. The case below # is a TRIPWIRE on the upstream text, not on our fix: it goes red the day a # future ldns stops shipping the fallback, which is the day that include # becomes unnecessary and this whole hazard is gone. It is deliberately not an # assertion that the hazard is absent -- it is present, and correct. if grep -q 'define _Bool signed char' "$incd/common.h" 2>/dev/null; then bst_ok ldns/bool-fallback-present else bst_notok ldns/bool-fallback-present "common.h no longer defines _Bool as signed char -- upstream changed the fallback, so revisit the include in src/ldns_probe.c and this case" fi # --- the library RUNNING, via the cross-linked probe -------------------------- # data/ is installed 0644 and the rootfs is read-only at browser runtime, so the # payload is copied to a writable path and made executable there. /tmp is a # tmpfs with no noexec. tmp=${TMPDIR:-/tmp} hasfile ldns/probe-payload "$BST_DATA/ldns-probe" hasfile ldns/zone-fixture "$BST_DATA/ldns-zone.txt" probe=$tmp/bst-ldns-probe.$$ if cp "$BST_DATA/ldns-probe" "$probe" 2>/dev/null && chmod +x "$probe" 2>/dev/null; then out=$("$probe" "$BST_DATA/ldns-zone.txt" "$tmp" 2>&1) # the library's own identity has ldns/run-version "LDNS-version OK" "$out" # zone: parse has ldns/run-origin-parse "LDNS-origin-parse OK" "$out" has ldns/run-zone-open "LDNS-zone-open OK" "$out" has ldns/run-zone-parse "LDNS-zone-parse OK" "$out" has ldns/run-zone-rr-count "LDNS-zone-rr-count OK" "$out" # zone: the SOA, which ldns holds apart from the RR list has ldns/run-soa-present "LDNS-zone-soa-present OK" "$out" has ldns/run-soa-type "LDNS-zone-soa-type OK" "$out" has ldns/run-soa-owner "LDNS-zone-soa-owner OK" "$out" has ldns/run-soa-rdcount "LDNS-zone-soa-rdcount OK" "$out" has ldns/run-soa-mname "LDNS-zone-soa-mname OK" "$out" has ldns/run-soa-rname "LDNS-zone-soa-rname OK" "$out" has ldns/run-soa-serial "LDNS-zone-soa-serial OK" "$out" has ldns/run-soa-minimum "LDNS-zone-soa-minimum OK" "$out" # zone: the per-type census -- the parse found every record or it did not has ldns/run-count-a "LDNS-zone-count-a OK" "$out" has ldns/run-count-aaaa "LDNS-zone-count-aaaa OK" "$out" has ldns/run-count-mx "LDNS-zone-count-mx OK" "$out" has ldns/run-count-txt "LDNS-zone-count-txt OK" "$out" has ldns/run-count-ns "LDNS-zone-count-ns OK" "$out" has ldns/run-count-cname "LDNS-zone-count-cname OK" "$out" has ldns/run-count-srv "LDNS-zone-count-srv OK" "$out" # zone: the representative A record, field by field has ldns/run-a-present "LDNS-a-present OK" "$out" has ldns/run-a-owner "LDNS-a-owner OK" "$out" has ldns/run-a-ttl "LDNS-a-ttl OK" "$out" has ldns/run-a-class "LDNS-a-class OK" "$out" has ldns/run-a-type "LDNS-a-type OK" "$out" has ldns/run-a-rdcount "LDNS-a-rdcount OK" "$out" has ldns/run-a-addr "LDNS-a-addr OK" "$out" has ldns/run-a-rdf-size "LDNS-a-rdf-size OK" "$out" has ldns/run-a-rdf-type "LDNS-a-rdf-type OK" "$out" # zone: the representative AAAA record has ldns/run-aaaa-present "LDNS-aaaa-present OK" "$out" has ldns/run-aaaa-owner "LDNS-aaaa-owner OK" "$out" has ldns/run-aaaa-ttl "LDNS-aaaa-ttl OK" "$out" has ldns/run-aaaa-class "LDNS-aaaa-class OK" "$out" has ldns/run-aaaa-type "LDNS-aaaa-type OK" "$out" has ldns/run-aaaa-rdcount "LDNS-aaaa-rdcount OK" "$out" has ldns/run-aaaa-addr "LDNS-aaaa-addr OK" "$out" has ldns/run-aaaa-rdf-size "LDNS-aaaa-rdf-size OK" "$out" has ldns/run-aaaa-rdf-type "LDNS-aaaa-rdf-type OK" "$out" # zone: the representative MX record -- two rdfs, one numeric, one a name has ldns/run-mx-present "LDNS-mx-present OK" "$out" has ldns/run-mx-owner "LDNS-mx-owner OK" "$out" has ldns/run-mx-ttl "LDNS-mx-ttl OK" "$out" has ldns/run-mx-class "LDNS-mx-class OK" "$out" has ldns/run-mx-type "LDNS-mx-type OK" "$out" has ldns/run-mx-rdcount "LDNS-mx-rdcount OK" "$out" has ldns/run-mx-pref "LDNS-mx-pref OK" "$out" has ldns/run-mx-exchange "LDNS-mx-exchange OK" "$out" has ldns/run-mx-pref-size "LDNS-mx-pref-size OK" "$out" # zone: the representative TXT record -- two character-strings, each quoted has ldns/run-txt-present "LDNS-txt-present OK" "$out" has ldns/run-txt-owner "LDNS-txt-owner OK" "$out" has ldns/run-txt-ttl "LDNS-txt-ttl OK" "$out" has ldns/run-txt-class "LDNS-txt-class OK" "$out" has ldns/run-txt-type "LDNS-txt-type OK" "$out" has ldns/run-txt-rdcount "LDNS-txt-rdcount OK" "$out" has ldns/run-txt-str0 "LDNS-txt-str0 OK" "$out" has ldns/run-txt-str1 "LDNS-txt-str1 OK" "$out" has ldns/run-txt-rdf-type "LDNS-txt-rdf-type OK" "$out" # zone: written back out and compared with the input, byte for byte has ldns/run-writeback-open "LDNS-zone-writeback-open OK" "$out" has ldns/run-writeback-size "LDNS-zone-writeback-size OK" "$out" has ldns/run-writeback-identical "LDNS-zone-writeback-identical OK" "$out" has ldns/run-reparse "LDNS-zone-reparse OK" "$out" has ldns/run-reparse-count "LDNS-zone-reparse-count OK" "$out" # packet: composed, and its counts before it goes to the wire has ldns/run-pkt-new "LDNS-pkt-new OK" "$out" has ldns/run-pkt-qdcount "LDNS-pkt-qdcount OK" "$out" has ldns/run-pkt-ancount "LDNS-pkt-ancount OK" "$out" has ldns/run-pkt-arcount "LDNS-pkt-arcount OK" "$out" # packet: rendered to wire format, asserted octet by octet has ldns/run-pkt2wire "LDNS-pkt2wire OK" "$out" has ldns/run-pkt2wire-size "LDNS-pkt2wire-size OK" "$out" has ldns/run-pkt2wire-id-bytes "LDNS-pkt2wire-id-bytes OK" "$out" has ldns/run-pkt2wire-qname-wire "LDNS-pkt2wire-qname-wire OK" "$out" # packet: parsed back, and every header field compared with what was sent has ldns/run-wire2pkt "LDNS-wire2pkt OK" "$out" has ldns/run-hdr-id "LDNS-hdr-id OK" "$out" has ldns/run-hdr-qr "LDNS-hdr-qr OK" "$out" has ldns/run-hdr-aa "LDNS-hdr-aa OK" "$out" has ldns/run-hdr-tc "LDNS-hdr-tc OK" "$out" has ldns/run-hdr-rd "LDNS-hdr-rd OK" "$out" has ldns/run-hdr-cd "LDNS-hdr-cd OK" "$out" has ldns/run-hdr-ra "LDNS-hdr-ra OK" "$out" has ldns/run-hdr-ad "LDNS-hdr-ad OK" "$out" has ldns/run-hdr-opcode "LDNS-hdr-opcode OK" "$out" has ldns/run-hdr-rcode "LDNS-hdr-rcode OK" "$out" has ldns/run-hdr-qdcount "LDNS-hdr-qdcount OK" "$out" has ldns/run-hdr-ancount "LDNS-hdr-ancount OK" "$out" has ldns/run-hdr-nscount "LDNS-hdr-nscount OK" "$out" has ldns/run-hdr-arcount "LDNS-hdr-arcount OK" "$out" # packet: the question section survived the round trip has ldns/run-q-count "LDNS-q-count OK" "$out" has ldns/run-q-owner "LDNS-q-owner OK" "$out" has ldns/run-q-type "LDNS-q-type OK" "$out" has ldns/run-q-class "LDNS-q-class OK" "$out" has ldns/run-q-rdcount "LDNS-q-rdcount OK" "$out" # packet: re-rendering the parsed packet reproduces the original bytes, and a # truncated buffer is refused rather than read past its end has ldns/run-wire-rt-status "LDNS-wire-roundtrip-status OK" "$out" has ldns/run-wire-rt-size "LDNS-wire-roundtrip-size OK" "$out" has ldns/run-wire-rt-bytes "LDNS-wire-roundtrip-bytes OK" "$out" has ldns/run-short-refused "LDNS-wire2pkt-short-refused OK" "$out" # The probe's own tally and its exit status. 93/93 is what the same source # scores natively against a host build of the identical release. has ldns/run-summary "LDNS-SUMMARY 93/93" "$out" check_rc ldns/run-exit0 0 "$probe" "$BST_DATA/ldns-zone.txt" "$tmp" rm -f "$probe" else bst_notok ldns/probe-stage "cannot stage $BST_DATA/ldns-probe" fi