From 8d35d07b3113a994a0d961a46fb8d4a561ca893e Mon Sep 17 00:00:00 2001 From: eabdullin Date: Thu, 11 Apr 2024 13:20:20 +0000 Subject: [PATCH] import UBI bind-9.11.36-11.el8_9.1 --- SOURCES/bind-9.11-CVE-2023-2828-fixup.patch | 46 + SOURCES/bind-9.11-CVE-2023-4408.patch | 3154 ++++++++++++++++++ SOURCES/bind-9.11-CVE-2023-50387-fixup.patch | 64 + SOURCES/bind-9.11-CVE-2023-50387.patch | 737 ++++ SPECS/bind.spec | 19 +- 5 files changed, 4019 insertions(+), 1 deletion(-) create mode 100644 SOURCES/bind-9.11-CVE-2023-2828-fixup.patch create mode 100644 SOURCES/bind-9.11-CVE-2023-4408.patch create mode 100644 SOURCES/bind-9.11-CVE-2023-50387-fixup.patch create mode 100644 SOURCES/bind-9.11-CVE-2023-50387.patch diff --git a/SOURCES/bind-9.11-CVE-2023-2828-fixup.patch b/SOURCES/bind-9.11-CVE-2023-2828-fixup.patch new file mode 100644 index 0000000..b1865ed --- /dev/null +++ b/SOURCES/bind-9.11-CVE-2023-2828-fixup.patch @@ -0,0 +1,46 @@ +From 6c26ede8edcb700caca12c501c6c129801989526 Mon Sep 17 00:00:00 2001 +From: Mark Andrews +Date: Fri, 23 Feb 2024 10:12:47 +1100 +Subject: [PATCH] Do not use header_prev in expire_lru_headers + +dns__cacherbt_expireheader can unlink / free header_prev underneath +it. Use ISC_LIST_TAIL after calling dns__cacherbt_expireheader +instead to get the next pointer to be processed. + +(cherry picked from commit 7ce2e86024f022decb2678963538515ca39ab4ab) +(cherry picked from commit f88f21b7d890eb80097f4bd434fedb29c2f9ff63) +--- + lib/dns/rbtdb.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c +index cc40eaec60..ee59c1b18b 100644 +--- a/lib/dns/rbtdb.c ++++ b/lib/dns/rbtdb.c +@@ -10667,19 +10667,19 @@ update_header(dns_rbtdb_t *rbtdb, rdatasetheader_t *header, + static size_t + expire_lru_headers(dns_rbtdb_t *rbtdb, unsigned int locknum, size_t purgesize, + bool tree_locked) { +- rdatasetheader_t *header, *header_prev; ++ rdatasetheader_t *header; + size_t purged = 0; + + for (header = ISC_LIST_TAIL(rbtdb->rdatasets[locknum]); +- header != NULL && purged <= purgesize; header = header_prev) ++ header != NULL && purged <= purgesize; ++ header = ISC_LIST_TAIL(rbtdb->rdatasets[locknum])) + { +- header_prev = ISC_LIST_PREV(header, link); + /* + * Unlink the entry at this point to avoid checking it + * again even if it's currently used someone else and + * cannot be purged at this moment. This entry won't be + * referenced any more (so unlinking is safe) since the +- * TTL was reset to 0. ++ * TTL will be reset to 0. + */ + ISC_LIST_UNLINK(rbtdb->rdatasets[locknum], header, link); + size_t header_size = rdataset_size(header); +-- +2.43.2 + diff --git a/SOURCES/bind-9.11-CVE-2023-4408.patch b/SOURCES/bind-9.11-CVE-2023-4408.patch new file mode 100644 index 0000000..74188c6 --- /dev/null +++ b/SOURCES/bind-9.11-CVE-2023-4408.patch @@ -0,0 +1,3154 @@ +From 1030b5cef8643ebd692f75a8bf671d650fbcb377 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= +Date: Mon, 11 Sep 2023 10:35:28 +0200 +Subject: [PATCH] Use hashtable when parsing a message + +When parsing messages use a hashtable instead of a linear search to reduce +the amount of work done in findname when there's more than one name in +the section. + +There are two hashtables: + +1) hashtable for owner names - that's constructed for each section when we +hit the second name in the section and destroyed right after parsing +that section; + +2) per-name hashtable - for each name in the section, we construct a new +hashtable for that name if there are more than one rdataset for that +particular name. + +(cherry picked from commit b8a96317544c7b310b4f74360825a87b6402ddc2) +(cherry picked from commit 0ceed03ebea395da1a39ad1cb39205ce75a3f768) + +Backport isc_ht API changes from BIND 9.18 + +To prevent allocating large hashtable in dns_message, we need to +backport the improvements to isc_ht API from BIND 9.18+ that includes +support for case insensitive keys and incremental rehashing of the +hashtables. + +(cherry picked from commit a4baf324159ec3764195c949cb56c861d9f173ff) +(cherry picked from commit 2fc28056b33018f7f78b625409eb44c32d5c9b11) + +fix a message parsing regression + +the fix for CVE-2023-4408 introduced a regression in the message +parser, which could cause a crash if duplicate rdatasets were found +in the question section. this commit ensures that rdatasets are +correctly disassociated and freed when this occurs. + +(cherry picked from commit 4c19d35614f8cd80d8748156a5bad361e19abc28) +(cherry picked from commit 98ab8c81cc7739dc220aa3f50efa3061774de8ba) + +fix another message parsing regression + +The fix for CVE-2023-4408 introduced a regression in the message +parser, which could cause a crash if an rdata type that can only +occur in the question was found in another section. + +(cherry picked from commit 510f1de8a6add516b842a55750366944701d3d9a) +(cherry picked from commit bbbcaf8b2ec17d2cad28841ea86078168072ae2f) + +Apply various tweaks specific to BIND 9.11 + +(cherry picked from commit c6026cbbaa9d297910af350fa6cc45788cc9f397) + +Fix case insensitive matching in isc_ht hash table implementation + +The case insensitive matching in isc_ht was basically completely broken +as only the hashvalue computation was case insensitive, but the key +comparison was always case sensitive. + +(cherry picked from commit c462d65b2fd0db362947db4a18a87df78f8d8e5d) +(cherry picked from commit 418b3793598a1e1c7e391bb317866d405cd03501) + +Add a system test for mixed-case data for the same owner + +We were missing a test where a single owner name would have multiple +types with a different case. The generated RRSIGs and NSEC records will +then have different case than the signed records and message parser have +to cope with that and treat everything as the same owner. + +(cherry picked from commit c8b623d87f0fb8f9cba8dea5c6a4b600953895e7) +(cherry picked from commit 1f9bbe1fe34b7a2c9765431e8a86b460afc9b323) + +6315. [security] Speed up parsing of DNS messages with many different + names. (CVE-2023-4408) [GL #4234] +--- + .../system/dnssec/ns3/secure.example.db.in | 5 + + bin/tests/system/dnssec/ns3/sign.sh | 321 +++++----- + bin/tests/system/dnssec/tests.sh | 15 + + lib/dns/catz.c | 50 +- + lib/dns/include/dns/message.h | 39 -- + lib/dns/include/dns/name.h | 9 +- + lib/dns/message.c | 571 +++++++++++------ + lib/dns/name.c | 1 + + lib/isc/ht.c | 600 +++++++++++++----- + lib/isc/include/isc/ht.h | 52 +- + lib/isc/tests/ht_test.c | 69 +- + 11 files changed, 1097 insertions(+), 635 deletions(-) + +diff --git a/bin/tests/system/dnssec/ns3/secure.example.db.in b/bin/tests/system/dnssec/ns3/secure.example.db.in +index a097227278..be6b3f45b1 100644 +--- a/bin/tests/system/dnssec/ns3/secure.example.db.in ++++ b/bin/tests/system/dnssec/ns3/secure.example.db.in +@@ -44,3 +44,8 @@ rrsigonly A 10.0.0.29 + cnameandkey CNAME @ + cnamenokey CNAME @ + dnameandkey DNAME @ ++ ++mixedcase A 10.0.0.30 ++mixedCASE TXT "mixed case" ++MIXEDcase AAAA 2002:: ++mIxEdCaSe LOC 37 52 56.788 N 121 54 55.02 W 1120m 10m 100m 10m +diff --git a/bin/tests/system/dnssec/ns3/sign.sh b/bin/tests/system/dnssec/ns3/sign.sh +index 7caf29c3b2..9af0ca5a47 100644 +--- a/bin/tests/system/dnssec/ns3/sign.sh ++++ b/bin/tests/system/dnssec/ns3/sign.sh +@@ -15,39 +15,38 @@ SYSTEMTESTTOP=../.. + echo_i "ns3/sign.sh" + + infile=key.db.in +-for tld in managed trusted +-do ++for tld in managed trusted; do + # A secure zone to test. + zone=secure.${tld} + zonefile=${zone}.db + +- keyname1=`$KEYGEN -f KSK -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` +- cat $infile $keyname1.key > $zonefile +- $SIGNER -z -P -3 - -o $zone -r $RANDFILE -O full -f ${zonefile}.signed $zonefile > /dev/null 2>&1 +- DSFILE=dsset-`echo ${zone} |sed -e "s/\.$//g"`$TP +- $DSFROMKEY -A -f ${zonefile}.signed $zone > $DSFILE ++ keyname1=$($KEYGEN -f KSK -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) ++ cat $infile $keyname1.key >$zonefile ++ $SIGNER -z -P -3 - -o $zone -r $RANDFILE -O full -f ${zonefile}.signed $zonefile >/dev/null 2>&1 ++ DSFILE=dsset-$(echo ${zone} | sed -e "s/\.$//g")$TP ++ $DSFROMKEY -A -f ${zonefile}.signed $zone >$DSFILE + + # Zone to test trust anchor with unsupported algorithm. + zone=unsupported.${tld} + zonefile=${zone}.db + +- keyname2=`$KEYGEN -f KSK -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` +- cat $infile $keyname2.key > $zonefile +- $SIGNER -z -P -3 - -o $zone -r $RANDFILE -O full -f ${zonefile}.tmp $zonefile > /dev/null 2>&1 +- awk '$4 == "DNSKEY" { $7 = 255 } $4 == "RRSIG" { $6 = 255 } { print }' ${zonefile}.tmp > ${zonefile}.signed +- DSFILE=dsset-`echo ${zone} |sed -e "s/\.$//g"`$TP +- $DSFROMKEY -A -f ${zonefile}.signed $zone > $DSFILE ++ keyname2=$($KEYGEN -f KSK -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) ++ cat $infile $keyname2.key >$zonefile ++ $SIGNER -z -P -3 - -o $zone -r $RANDFILE -O full -f ${zonefile}.tmp $zonefile >/dev/null 2>&1 ++ awk '$4 == "DNSKEY" { $7 = 255 } $4 == "RRSIG" { $6 = 255 } { print }' ${zonefile}.tmp >${zonefile}.signed ++ DSFILE=dsset-$(echo ${zone} | sed -e "s/\.$//g")$TP ++ $DSFROMKEY -A -f ${zonefile}.signed $zone >$DSFILE + + # Make trusted-keys and managed keys conf sections for ns8. + mv ${keyname2}.key ${keyname2}.tmp +- awk '$1 == "unsupported.'"${tld}"'." { $6 = 255 } { print }' ${keyname2}.tmp > ${keyname2}.key ++ awk '$1 == "unsupported.'"${tld}"'." { $6 = 255 } { print }' ${keyname2}.tmp >${keyname2}.key + + case $tld in + "managed") +- keyfile_to_managed_keys $keyname1 $keyname2 > ../ns8/managed.conf ++ keyfile_to_managed_keys $keyname1 $keyname2 >../ns8/managed.conf + ;; + "trusted") +- keyfile_to_trusted_keys $keyname1 $keyname2 > ../ns8/trusted.conf ++ keyfile_to_trusted_keys $keyname1 $keyname2 >../ns8/trusted.conf + ;; + esac + done +@@ -58,49 +57,51 @@ zone=secure.example. + infile=secure.example.db.in + zonefile=secure.example.db + +-cnameandkey=`$KEYGEN -T KEY -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n host cnameandkey.$zone` +-dnameandkey=`$KEYGEN -T KEY -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n host dnameandkey.$zone` +-keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` ++cnameandkey=$($KEYGEN -T KEY -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n host cnameandkey.$zone) ++dnameandkey=$($KEYGEN -T KEY -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n host dnameandkey.$zone) ++keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) + + cat $infile $cnameandkey.key $dnameandkey.key $keyname.key >$zonefile + +-$SIGNER -P -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 ++$SIGNER -P -D -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 ++cat "$zonefile" "$zonefile".signed >"$zonefile".tmp ++mv "$zonefile".tmp "$zonefile".signed + + zone=bogus.example. + infile=bogus.example.db.in + zonefile=bogus.example.db + +-keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` ++keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) + + cat $infile $keyname.key >$zonefile + +-$SIGNER -P -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 ++$SIGNER -P -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 + + zone=dynamic.example. + infile=dynamic.example.db.in + zonefile=dynamic.example.db + +-keyname1=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` +-keyname2=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b 1024 -n zone -f KSK $zone` ++keyname1=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) ++keyname2=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b 1024 -n zone -f KSK $zone) + + cat $infile $keyname1.key $keyname2.key >$zonefile + +-$SIGNER -P -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 ++$SIGNER -P -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 + + zone=keyless.example. + infile=generic.example.db.in + zonefile=keyless.example.db + +-keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` ++keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) + + cat $infile $keyname.key >$zonefile + +-$SIGNER -P -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 ++$SIGNER -P -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 + + # Change the signer field of the a.b.keyless.example SIG A + # to point to a provably nonexistent KEY record. + mv $zonefile.signed $zonefile.tmp +-<$zonefile.tmp $PERL -p -e 's/ keyless.example/ b.keyless.example/ ++$PERL <$zonefile.tmp -p -e 's/ keyless.example/ b.keyless.example/ + if /^a.b.keyless.example/../NXT/;' >$zonefile.signed + rm -f $zonefile.tmp + +@@ -111,11 +112,11 @@ zone=secure.nsec3.example. + infile=secure.nsec3.example.db.in + zonefile=secure.nsec3.example.db + +-keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` ++keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) + + cat $infile $keyname.key >$zonefile + +-$SIGNER -P -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 ++$SIGNER -P -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 + + # + # NSEC3/NSEC3 test zone +@@ -124,11 +125,11 @@ zone=nsec3.nsec3.example. + infile=nsec3.nsec3.example.db.in + zonefile=nsec3.nsec3.example.db + +-keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` ++keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) + + cat $infile $keyname.key >$zonefile + +-$SIGNER -P -3 - -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 ++$SIGNER -P -3 - -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 + + # + # OPTOUT/NSEC3 test zone +@@ -137,11 +138,11 @@ zone=optout.nsec3.example. + infile=optout.nsec3.example.db.in + zonefile=optout.nsec3.example.db + +-keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` ++keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) + + cat $infile $keyname.key >$zonefile + +-$SIGNER -P -3 - -A -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 ++$SIGNER -P -3 - -A -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 + + # + # A nsec3 zone (non-optout). +@@ -150,11 +151,11 @@ zone=nsec3.example. + infile=nsec3.example.db.in + zonefile=nsec3.example.db + +-keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` ++keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) + + cat $infile $keyname.key >$zonefile + +-$SIGNER -P -g -3 - -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 ++$SIGNER -P -g -3 - -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 + + # + # OPTOUT/NSEC test zone +@@ -163,11 +164,11 @@ zone=secure.optout.example. + infile=secure.optout.example.db.in + zonefile=secure.optout.example.db + +-keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` ++keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) + + cat $infile $keyname.key >$zonefile + +-$SIGNER -P -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 ++$SIGNER -P -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 + + # + # OPTOUT/NSEC3 test zone +@@ -176,11 +177,11 @@ zone=nsec3.optout.example. + infile=nsec3.optout.example.db.in + zonefile=nsec3.optout.example.db + +-keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` ++keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) + + cat $infile $keyname.key >$zonefile + +-$SIGNER -P -3 - -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 ++$SIGNER -P -3 - -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 + + # + # OPTOUT/OPTOUT test zone +@@ -189,11 +190,11 @@ zone=optout.optout.example. + infile=optout.optout.example.db.in + zonefile=optout.optout.example.db + +-keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` ++keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) + + cat $infile $keyname.key >$zonefile + +-$SIGNER -P -3 - -A -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 ++$SIGNER -P -3 - -A -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 + + # + # A optout nsec3 zone. +@@ -202,11 +203,11 @@ zone=optout.example. + infile=optout.example.db.in + zonefile=optout.example.db + +-keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` ++keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) + + cat $infile $keyname.key >$zonefile + +-$SIGNER -P -g -3 - -A -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 ++$SIGNER -P -g -3 - -A -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 + + # + # A nsec3 zone (non-optout) with unknown nsec3 hash algorithm (-U). +@@ -215,11 +216,11 @@ zone=nsec3-unknown.example. + infile=nsec3-unknown.example.db.in + zonefile=nsec3-unknown.example.db + +-keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` ++keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) + + cat $infile $keyname.key >$zonefile + +-$SIGNER -P -3 - -U -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 ++$SIGNER -P -3 - -U -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 + + # + # A optout nsec3 zone with a unknown nsec3 hash algorithm (-U). +@@ -228,11 +229,11 @@ zone=optout-unknown.example. + infile=optout-unknown.example.db.in + zonefile=optout-unknown.example.db + +-keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` ++keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) + + cat $infile $keyname.key >$zonefile + +-$SIGNER -P -3 - -U -A -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 ++$SIGNER -P -3 - -U -A -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 + + # + # A zone that is signed with an unknown DNSKEY algorithm. +@@ -242,16 +243,16 @@ zone=dnskey-unknown.example + infile=dnskey-unknown.example.db.in + zonefile=dnskey-unknown.example.db + +-keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` ++keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) + + cat $infile $keyname.key >$zonefile + +-$SIGNER -P -3 - -r $RANDFILE -o $zone -O full -f ${zonefile}.tmp $zonefile > /dev/null 2>&1 ++$SIGNER -P -3 - -r $RANDFILE -o $zone -O full -f ${zonefile}.tmp $zonefile >/dev/null 2>&1 + +-awk '$4 == "DNSKEY" { $7 = 100 } $4 == "RRSIG" { $6 = 100 } { print }' ${zonefile}.tmp > ${zonefile}.signed ++awk '$4 == "DNSKEY" { $7 = 100 } $4 == "RRSIG" { $6 = 100 } { print }' ${zonefile}.tmp >${zonefile}.signed + + DSFILE=dsset-${zone}${TP} +-$DSFROMKEY -A -f ${zonefile}.signed $zone > $DSFILE ++$DSFROMKEY -A -f ${zonefile}.signed $zone >$DSFILE + + # + # A zone that is signed with an unsupported DNSKEY algorithm (3). +@@ -261,16 +262,16 @@ zone=dnskey-unsupported.example + infile=dnskey-unsupported.example.db.in + zonefile=dnskey-unsupported.example.db + +-keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` ++keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) + +-cat $infile $keyname.key > $zonefile ++cat $infile $keyname.key >$zonefile + +-$SIGNER -P -3 - -r $RANDFILE -o $zone -O full -f ${zonefile}.tmp $zonefile > /dev/null 2>&1 ++$SIGNER -P -3 - -r $RANDFILE -o $zone -O full -f ${zonefile}.tmp $zonefile >/dev/null 2>&1 + +-awk '$4 == "DNSKEY" { $7 = 255 } $4 == "RRSIG" { $6 = 255 } { print }' ${zonefile}.tmp > ${zonefile}.signed ++awk '$4 == "DNSKEY" { $7 = 255 } $4 == "RRSIG" { $6 = 255 } { print }' ${zonefile}.tmp >${zonefile}.signed + + DSFILE="dsset-${zone}${TP}" +-$DSFROMKEY -A -f ${zonefile}.signed $zone > $DSFILE ++$DSFROMKEY -A -f ${zonefile}.signed $zone >$DSFILE + + # + # A zone with a published unsupported DNSKEY algorithm (Reserved). +@@ -280,12 +281,12 @@ zone=dnskey-unsupported-2.example + infile=dnskey-unsupported-2.example.db.in + zonefile=dnskey-unsupported-2.example.db + +-ksk=`$KEYGEN -f KSK -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` +-zsk=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` ++ksk=$($KEYGEN -f KSK -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) ++zsk=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) + +-cat $infile $ksk.key $zsk.key unsupported-algorithm.key > $zonefile ++cat $infile $ksk.key $zsk.key unsupported-algorithm.key >$zonefile + +-$SIGNER -P -3 - -r $RANDFILE -o $zone -f ${zonefile}.signed $zonefile > /dev/null 2>&1 ++$SIGNER -P -3 - -r $RANDFILE -o $zone -f ${zonefile}.signed $zonefile >/dev/null 2>&1 + + # + # A zone with a unknown DNSKEY algorithm + unknown NSEC3 hash algorithm (-U). +@@ -295,16 +296,16 @@ zone=dnskey-nsec3-unknown.example + infile=dnskey-nsec3-unknown.example.db.in + zonefile=dnskey-nsec3-unknown.example.db + +-keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` ++keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) + + cat $infile $keyname.key >$zonefile + +-$SIGNER -P -3 - -r $RANDFILE -o $zone -U -O full -f ${zonefile}.tmp $zonefile > /dev/null 2>&1 ++$SIGNER -P -3 - -r $RANDFILE -o $zone -U -O full -f ${zonefile}.tmp $zonefile >/dev/null 2>&1 + +-awk '$4 == "DNSKEY" { $7 = 100; print } $4 == "RRSIG" { $6 = 100; print } { print }' ${zonefile}.tmp > ${zonefile}.signed ++awk '$4 == "DNSKEY" { $7 = 100; print } $4 == "RRSIG" { $6 = 100; print } { print }' ${zonefile}.tmp >${zonefile}.signed + + DSFILE=dsset-${zone}${TP} +-$DSFROMKEY -A -f ${zonefile}.signed $zone > $DSFILE ++$DSFROMKEY -A -f ${zonefile}.signed $zone >$DSFILE + + # + # A multiple parameter nsec3 zone. +@@ -313,21 +314,21 @@ zone=multiple.example. + infile=multiple.example.db.in + zonefile=multiple.example.db + +-keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` ++keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) + + cat $infile $keyname.key >$zonefile + +-$SIGNER -P -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 ++$SIGNER -P -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 + mv $zonefile.signed $zonefile +-$SIGNER -P -u3 - -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 ++$SIGNER -P -u3 - -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 + mv $zonefile.signed $zonefile +-$SIGNER -P -u3 AAAA -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 ++$SIGNER -P -u3 AAAA -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 + mv $zonefile.signed $zonefile +-$SIGNER -P -u3 BBBB -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 ++$SIGNER -P -u3 BBBB -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 + mv $zonefile.signed $zonefile +-$SIGNER -P -u3 CCCC -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 ++$SIGNER -P -u3 CCCC -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 + mv $zonefile.signed $zonefile +-$SIGNER -P -u3 DDDD -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 ++$SIGNER -P -u3 DDDD -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 + + # + # A RSASHA256 zone. +@@ -336,11 +337,11 @@ zone=rsasha256.example. + infile=rsasha256.example.db.in + zonefile=rsasha256.example.db + +-keyname=`$KEYGEN -q -r $RANDFILE -a RSASHA256 -b 1024 -n zone $zone` ++keyname=$($KEYGEN -q -r $RANDFILE -a RSASHA256 -b 1024 -n zone $zone) + + cat $infile $keyname.key >$zonefile + +-$SIGNER -P -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 ++$SIGNER -P -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 + + # + # A RSASHA512 zone. +@@ -349,11 +350,11 @@ zone=rsasha512.example. + infile=rsasha512.example.db.in + zonefile=rsasha512.example.db + +-keyname=`$KEYGEN -q -r $RANDFILE -a RSASHA512 -b 1024 -n zone $zone` ++keyname=$($KEYGEN -q -r $RANDFILE -a RSASHA512 -b 1024 -n zone $zone) + + cat $infile $keyname.key >$zonefile + +-$SIGNER -P -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 ++$SIGNER -P -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 + + # + # A zone with the DNSKEY set only signed by the KSK +@@ -362,10 +363,10 @@ zone=kskonly.example. + infile=kskonly.example.db.in + zonefile=kskonly.example.db + +-kskname=`$KEYGEN -q -r $RANDFILE -fk $zone` +-zskname=`$KEYGEN -q -r $RANDFILE $zone` ++kskname=$($KEYGEN -q -r $RANDFILE -fk $zone) ++zskname=$($KEYGEN -q -r $RANDFILE $zone) + cat $infile $kskname.key $zskname.key >$zonefile +-$SIGNER -x -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 ++$SIGNER -x -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 + + # + # A zone with the expired signatures +@@ -374,10 +375,10 @@ zone=expired.example. + infile=expired.example.db.in + zonefile=expired.example.db + +-kskname=`$KEYGEN -q -r $RANDFILE -fk $zone` +-zskname=`$KEYGEN -q -r $RANDFILE $zone` ++kskname=$($KEYGEN -q -r $RANDFILE -fk $zone) ++zskname=$($KEYGEN -q -r $RANDFILE $zone) + cat $infile $kskname.key $zskname.key >$zonefile +-$SIGNER -P -r $RANDFILE -o $zone -s -1d -e +1h $zonefile > /dev/null 2>&1 ++$SIGNER -P -r $RANDFILE -o $zone -s -1d -e +1h $zonefile >/dev/null 2>&1 + rm -f $kskname.* $zskname.* + + # +@@ -387,10 +388,10 @@ zone=update-nsec3.example. + infile=update-nsec3.example.db.in + zonefile=update-nsec3.example.db + +-kskname=`$KEYGEN -q -3 -r $RANDFILE -fk $zone` +-zskname=`$KEYGEN -q -3 -r $RANDFILE $zone` ++kskname=$($KEYGEN -q -3 -r $RANDFILE -fk $zone) ++zskname=$($KEYGEN -q -3 -r $RANDFILE $zone) + cat $infile $kskname.key $zskname.key >$zonefile +-$SIGNER -P -3 - -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 ++$SIGNER -P -3 - -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 + + # + # A NSEC signed zone that will have auto-dnssec enabled and +@@ -400,12 +401,12 @@ zone=auto-nsec.example. + infile=auto-nsec.example.db.in + zonefile=auto-nsec.example.db + +-kskname=`$KEYGEN -q -r $RANDFILE -fk $zone` +-zskname=`$KEYGEN -q -r $RANDFILE $zone` +-kskname=`$KEYGEN -q -r $RANDFILE -fk $zone` +-zskname=`$KEYGEN -q -r $RANDFILE $zone` ++kskname=$($KEYGEN -q -r $RANDFILE -fk $zone) ++zskname=$($KEYGEN -q -r $RANDFILE $zone) ++kskname=$($KEYGEN -q -r $RANDFILE -fk $zone) ++zskname=$($KEYGEN -q -r $RANDFILE $zone) + cat $infile $kskname.key $zskname.key >$zonefile +-$SIGNER -P -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 ++$SIGNER -P -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 + + # + # A NSEC3 signed zone that will have auto-dnssec enabled and +@@ -415,12 +416,12 @@ zone=auto-nsec3.example. + infile=auto-nsec3.example.db.in + zonefile=auto-nsec3.example.db + +-kskname=`$KEYGEN -q -3 -r $RANDFILE -fk $zone` +-zskname=`$KEYGEN -q -3 -r $RANDFILE $zone` +-kskname=`$KEYGEN -q -3 -r $RANDFILE -fk $zone` +-zskname=`$KEYGEN -q -3 -r $RANDFILE $zone` ++kskname=$($KEYGEN -q -3 -r $RANDFILE -fk $zone) ++zskname=$($KEYGEN -q -3 -r $RANDFILE $zone) ++kskname=$($KEYGEN -q -3 -r $RANDFILE -fk $zone) ++zskname=$($KEYGEN -q -3 -r $RANDFILE $zone) + cat $infile $kskname.key $zskname.key >$zonefile +-$SIGNER -P -3 - -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 ++$SIGNER -P -3 - -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 + + # + # Secure below cname test zone. +@@ -428,9 +429,9 @@ $SIGNER -P -3 - -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 + zone=secure.below-cname.example. + infile=secure.below-cname.example.db.in + zonefile=secure.below-cname.example.db +-keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` ++keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) + cat $infile $keyname.key >$zonefile +-$SIGNER -P -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 ++$SIGNER -P -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 + + # + # Patched TTL test zone. +@@ -441,12 +442,12 @@ zonefile=ttlpatch.example.db + signedfile=ttlpatch.example.db.signed + patchedfile=ttlpatch.example.db.patched + +-keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` ++keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) + cat $infile $keyname.key >$zonefile + +-$SIGNER -P -r $RANDFILE -f $signedfile -o $zone $zonefile > /dev/null 2>&1 +-$CHECKZONE -D -s full $zone $signedfile 2> /dev/null | \ +- awk '{$2 = "3600"; print}' > $patchedfile ++$SIGNER -P -r $RANDFILE -f $signedfile -o $zone $zonefile >/dev/null 2>&1 ++$CHECKZONE -D -s full $zone $signedfile 2>/dev/null | ++ awk '{$2 = "3600"; print}' >$patchedfile + + # + # Separate DNSSEC records. +@@ -456,11 +457,11 @@ infile=split-dnssec.example.db.in + zonefile=split-dnssec.example.db + signedfile=split-dnssec.example.db.signed + +-keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` ++keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) + cat $infile $keyname.key >$zonefile +-echo '$INCLUDE "'"$signedfile"'"' >> $zonefile +-: > $signedfile +-$SIGNER -P -r $RANDFILE -D -o $zone $zonefile > /dev/null 2>&1 ++echo '$INCLUDE "'"$signedfile"'"' >>$zonefile ++: >$signedfile ++$SIGNER -P -r $RANDFILE -D -o $zone $zonefile >/dev/null 2>&1 + + # + # Separate DNSSEC records smart signing. +@@ -470,11 +471,11 @@ infile=split-smart.example.db.in + zonefile=split-smart.example.db + signedfile=split-smart.example.db.signed + +-keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` ++keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) + cp $infile $zonefile +-echo '$INCLUDE "'"$signedfile"'"' >> $zonefile +-: > $signedfile +-$SIGNER -P -S -r $RANDFILE -D -o $zone $zonefile > /dev/null 2>&1 ++echo '$INCLUDE "'"$signedfile"'"' >>$zonefile ++: >$signedfile ++$SIGNER -P -S -r $RANDFILE -D -o $zone $zonefile >/dev/null 2>&1 + + # + # Zone with signatures about to expire, but no private key to replace them +@@ -483,10 +484,10 @@ zone="expiring.example." + infile="expiring.example.db.in" + zonefile="expiring.example.db" + signedfile="expiring.example.db.signed" +-kskname=`$KEYGEN -q -r $RANDFILE $zone` +-zskname=`$KEYGEN -q -r $RANDFILE -f KSK $zone` ++kskname=$($KEYGEN -q -r $RANDFILE $zone) ++zskname=$($KEYGEN -q -r $RANDFILE -f KSK $zone) + cp $infile $zonefile +-$SIGNER -S -r $RANDFILE -e now+1mi -o $zone $zonefile > /dev/null 2>&1 ++$SIGNER -S -r $RANDFILE -e now+1mi -o $zone $zonefile >/dev/null 2>&1 + mv -f ${zskname}.private ${zskname}.private.moved + mv -f ${kskname}.private ${kskname}.private.moved + +@@ -498,12 +499,12 @@ infile="upper.example.db.in" + zonefile="upper.example.db" + lower="upper.example.db.lower" + signedfile="upper.example.db.signed" +-kskname=`$KEYGEN -q -r $RANDFILE $zone` +-zskname=`$KEYGEN -q -r $RANDFILE -f KSK $zone` ++kskname=$($KEYGEN -q -r $RANDFILE $zone) ++zskname=$($KEYGEN -q -r $RANDFILE -f KSK $zone) + cp $infile $zonefile +-$SIGNER -P -S -r $RANDFILE -o $zone -f $lower $zonefile > /dev/null 2>/dev/null +-$CHECKZONE -D upper.example $lower 2>/dev/null | \ +- sed '/RRSIG/s/ upper.example. / UPPER.EXAMPLE. /' > $signedfile ++$SIGNER -P -S -r $RANDFILE -o $zone -f $lower $zonefile >/dev/null 2>/dev/null ++$CHECKZONE -D upper.example $lower 2>/dev/null | ++ sed '/RRSIG/s/ upper.example. / UPPER.EXAMPLE. /' >$signedfile + + # + # Check that the signer's name is in lower case when zone name is in +@@ -513,10 +514,10 @@ zone="LOWER.EXAMPLE." + infile="lower.example.db.in" + zonefile="lower.example.db" + signedfile="lower.example.db.signed" +-kskname=`$KEYGEN -q -r $RANDFILE $zone` +-zskname=`$KEYGEN -q -r $RANDFILE -f KSK $zone` ++kskname=$($KEYGEN -q -r $RANDFILE $zone) ++zskname=$($KEYGEN -q -r $RANDFILE -f KSK $zone) + cp $infile $zonefile +-$SIGNER -P -S -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 ++$SIGNER -P -S -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 + + # + # Zone with signatures about to expire, and dynamic, but configured +@@ -526,21 +527,21 @@ zone="nosign.example." + infile="nosign.example.db.in" + zonefile="nosign.example.db" + signedfile="nosign.example.db.signed" +-kskname=`$KEYGEN -q -r $RANDFILE $zone` +-zskname=`$KEYGEN -q -r $RANDFILE -f KSK $zone` ++kskname=$($KEYGEN -q -r $RANDFILE $zone) ++zskname=$($KEYGEN -q -r $RANDFILE -f KSK $zone) + cp $infile $zonefile +-$SIGNER -S -r $RANDFILE -e now+1mi -o $zone $zonefile > /dev/null 2>&1 ++$SIGNER -S -r $RANDFILE -e now+1mi -o $zone $zonefile >/dev/null 2>&1 + # preserve a normalized copy of the NS RRSIG for comparison later +-$CHECKZONE -D nosign.example nosign.example.db.signed 2>/dev/null | \ +- awk '$4 == "RRSIG" && $5 == "NS" {$2 = ""; print}' | \ +- sed 's/[ ][ ]*/ /g'> ../nosign.before ++$CHECKZONE -D nosign.example nosign.example.db.signed 2>/dev/null | ++ awk '$4 == "RRSIG" && $5 == "NS" {$2 = ""; print}' | ++ sed 's/[ ][ ]*/ /g' >../nosign.before + + # + # An inline signing zone + # + zone=inline.example. +-kskname=`$KEYGEN -q -3 -r $RANDFILE -fk $zone` +-zskname=`$KEYGEN -q -3 -r $RANDFILE $zone` ++kskname=$($KEYGEN -q -3 -r $RANDFILE -fk $zone) ++zskname=$($KEYGEN -q -3 -r $RANDFILE $zone) + + # + # publish a new key while deactivating another key at the same time. +@@ -548,13 +549,13 @@ zskname=`$KEYGEN -q -3 -r $RANDFILE $zone` + zone=publish-inactive.example + infile=publish-inactive.example.db.in + zonefile=publish-inactive.example.db +-now=`date -u +%Y%m%d%H%M%S` +-kskname=`$KEYGEN -q -r $RANDFILE -f KSK $zone` +-kskname=`$KEYGEN -P $now+90s -A $now+3600s -q -r $RANDFILE -f KSK $zone` +-kskname=`$KEYGEN -I $now+90s -q -r $RANDFILE -f KSK $zone` +-zskname=`$KEYGEN -q -r $RANDFILE $zone` ++now=$(date -u +%Y%m%d%H%M%S) ++kskname=$($KEYGEN -q -r $RANDFILE -f KSK $zone) ++kskname=$($KEYGEN -P $now+90s -A $now+3600s -q -r $RANDFILE -f KSK $zone) ++kskname=$($KEYGEN -I $now+90s -q -r $RANDFILE -f KSK $zone) ++zskname=$($KEYGEN -q -r $RANDFILE $zone) + cp $infile $zonefile +-$SIGNER -S -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 ++$SIGNER -S -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 + + # + # A zone which will change its sig-validity-interval +@@ -562,8 +563,8 @@ $SIGNER -S -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 + zone=siginterval.example + infile=siginterval.example.db.in + zonefile=siginterval.example.db +-kskname=`$KEYGEN -q -3 -r $RANDFILE -fk $zone` +-zskname=`$KEYGEN -q -3 -r $RANDFILE $zone` ++kskname=$($KEYGEN -q -3 -r $RANDFILE -fk $zone) ++zskname=$($KEYGEN -q -3 -r $RANDFILE $zone) + cp $infile $zonefile + + # +@@ -574,12 +575,12 @@ zone=badds.example. + infile=bogus.example.db.in + zonefile=badds.example.db + +-keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` ++keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) + + cat $infile $keyname.key >$zonefile + +-$SIGNER -P -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 +-sed -e 's/bogus/badds/g' < dsset-bogus.example$TP > dsset-badds.example$TP ++$SIGNER -P -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 ++sed -e 's/bogus/badds/g' dsset-badds.example$TP + + # + # A zone with future signatures. +@@ -587,10 +588,10 @@ sed -e 's/bogus/badds/g' < dsset-bogus.example$TP > dsset-badds.example$TP + zone=future.example + infile=future.example.db.in + zonefile=future.example.db +-kskname=`$KEYGEN -q -r $RANDFILE -f KSK $zone` +-zskname=`$KEYGEN -q -r $RANDFILE $zone` ++kskname=$($KEYGEN -q -r $RANDFILE -f KSK $zone) ++zskname=$($KEYGEN -q -r $RANDFILE $zone) + cat $infile $kskname.key $zskname.key >$zonefile +-$SIGNER -P -s +3600 -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 ++$SIGNER -P -s +3600 -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 + cp -f $kskname.key trusted-future.key + + # +@@ -599,10 +600,10 @@ cp -f $kskname.key trusted-future.key + zone=managed-future.example + infile=managed-future.example.db.in + zonefile=managed-future.example.db +-kskname=`$KEYGEN -q -r $RANDFILE -f KSK $zone` +-zskname=`$KEYGEN -q -r $RANDFILE $zone` ++kskname=$($KEYGEN -q -r $RANDFILE -f KSK $zone) ++zskname=$($KEYGEN -q -r $RANDFILE $zone) + cat $infile $kskname.key $zskname.key >$zonefile +-$SIGNER -P -s +3600 -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 ++$SIGNER -P -s +3600 -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 + + # + # A zone with a revoked key +@@ -611,14 +612,14 @@ zone=revkey.example. + infile=generic.example.db.in + zonefile=revkey.example.db + +-ksk1=`$KEYGEN -q -r $RANDFILE -3fk $zone` +-ksk1=`$REVOKE $ksk1` +-ksk2=`$KEYGEN -q -r $RANDFILE -3fk $zone` +-zsk1=`$KEYGEN -q -r $RANDFILE -3 $zone` ++ksk1=$($KEYGEN -q -r $RANDFILE -3fk $zone) ++ksk1=$($REVOKE $ksk1) ++ksk2=$($KEYGEN -q -r $RANDFILE -3fk $zone) ++zsk1=$($KEYGEN -q -r $RANDFILE -3 $zone) + + cat $infile ${ksk1}.key ${ksk2}.key ${zsk1}.key >$zonefile + +-$SIGNER -P -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 ++$SIGNER -P -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 + + # + # Check that NSEC3 are correctly signed and returned from below a DNAME +@@ -626,10 +627,10 @@ $SIGNER -P -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 + zone=dname-at-apex-nsec3.example + infile=dname-at-apex-nsec3.example.db.in + zonefile=dname-at-apex-nsec3.example.db +-kskname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -3fk $zone` +-zskname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -3 $zone` ++kskname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -3fk $zone) ++zskname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -3 $zone) + cat $infile $kskname.key $zskname.key >$zonefile +-$SIGNER -P -r $RANDFILE -3 - -o $zone $zonefile > /dev/null 2>&1 ++$SIGNER -P -r $RANDFILE -3 - -o $zone $zonefile >/dev/null 2>&1 + + # + # A NSEC zone with occuded data at the delegation +@@ -637,11 +638,11 @@ $SIGNER -P -r $RANDFILE -3 - -o $zone $zonefile > /dev/null 2>&1 + zone=occluded.example + infile=occluded.example.db.in + zonefile=occluded.example.db +-kskname=`"$KEYGEN" -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -fk "$zone"` +-zskname=`"$KEYGEN" -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS "$zone"` +-dnskeyname=`"$KEYGEN" -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -fk "delegation.$zone"` +-keyname=`"$KEYGEN" -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n ENTITY -T KEY "delegation.$zone"` +-$DSFROMKEY "$dnskeyname.key" > "dsset-delegation.${zone}$TP" ++kskname=$("$KEYGEN" -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -fk "$zone") ++zskname=$("$KEYGEN" -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS "$zone") ++dnskeyname=$("$KEYGEN" -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -fk "delegation.$zone") ++keyname=$("$KEYGEN" -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n ENTITY -T KEY "delegation.$zone") ++$DSFROMKEY "$dnskeyname.key" >"dsset-delegation.${zone}$TP" + cat "$infile" "${kskname}.key" "${zskname}.key" "${keyname}.key" \ +- "${dnskeyname}.key" "dsset-delegation.${zone}$TP" >"$zonefile" +-"$SIGNER" -P -r $RANDFILE -o "$zone" "$zonefile" > /dev/null 2>&1 ++ "${dnskeyname}.key" "dsset-delegation.${zone}$TP" >"$zonefile" ++"$SIGNER" -P -r $RANDFILE -o "$zone" "$zonefile" >/dev/null 2>&1 +diff --git a/bin/tests/system/dnssec/tests.sh b/bin/tests/system/dnssec/tests.sh +index 2f34b6d118..1eb97f42c5 100644 +--- a/bin/tests/system/dnssec/tests.sh ++++ b/bin/tests/system/dnssec/tests.sh +@@ -743,6 +743,21 @@ n=`expr $n + 1` + if [ $ret != 0 ]; then echo_i "failed"; fi + status=`expr $status + $ret` + ++echo_i "checking mixed-case positive validation ($n)" ++ret=0 ++for type in a txt aaaa loc; do ++ $DIG $DIGOPTS +noauth mixedcase.secure.example. \ ++ @10.53.0.3 $type >dig.out.$type.ns3.test$n || ret=1 ++ $DIG $DIGOPTS +noauth mixedcase.secure.example. \ ++ @10.53.0.4 $type >dig.out.$type.ns4.test$n || ret=1 ++ digcomp --lc dig.out.$type.ns3.test$n dig.out.$type.ns4.test$n || ret=1 ++ grep "status: NOERROR" dig.out.$type.ns4.test$n >/dev/null || ret=1 ++ grep "flags:.*ad.*QUERY" dig.out.$type.ns4.test$n >/dev/null || ret=1 ++done ++n=$((n + 1)) ++test "$ret" -eq 0 || echo_i "failed" ++status=$((status + ret)) ++ + echo_i "checking multi-stage positive validation NSEC/NSEC3 ($n)" + ret=0 + $DIG $DIGOPTS +noauth a.nsec3.example. \ +diff --git a/lib/dns/catz.c b/lib/dns/catz.c +index 304b038637..df101e3225 100644 +--- a/lib/dns/catz.c ++++ b/lib/dns/catz.c +@@ -398,33 +398,21 @@ dns_catz_zones_merge(dns_catz_zone_t *target, dns_catz_zone_t *newzone) { + + dns_name_format(&target->name, czname, DNS_NAME_FORMATSIZE); + +- result = isc_ht_init(&toadd, target->catzs->mctx, 16); +- if (result != ISC_R_SUCCESS) +- goto cleanup; ++ isc_ht_init(&toadd, target->catzs->mctx, 16, ISC_HT_CASE_SENSITIVE); + +- result = isc_ht_init(&tomod, target->catzs->mctx, 16); +- if (result != ISC_R_SUCCESS) +- goto cleanup; ++ isc_ht_init(&tomod, target->catzs->mctx, 16, ISC_HT_CASE_SENSITIVE); + +- result = isc_ht_iter_create(newzone->entries, &iter1); +- if (result != ISC_R_SUCCESS) +- goto cleanup; ++ isc_ht_iter_create(newzone->entries, &iter1); + +- result = isc_ht_iter_create(target->entries, &iter2); +- if (result != ISC_R_SUCCESS) +- goto cleanup; ++ isc_ht_iter_create(target->entries, &iter2); + + /* + * We can create those iterators now, even though toadd and tomod are + * empty + */ +- result = isc_ht_iter_create(toadd, &iteradd); +- if (result != ISC_R_SUCCESS) +- goto cleanup; ++ isc_ht_iter_create(toadd, &iteradd); + +- result = isc_ht_iter_create(tomod, &itermod); +- if (result != ISC_R_SUCCESS) +- goto cleanup; ++ isc_ht_iter_create(tomod, &itermod); + + /* + * First - walk the new zone and find all nodes that are not in the +@@ -570,7 +558,6 @@ dns_catz_zones_merge(dns_catz_zone_t *target, dns_catz_zone_t *newzone) { + + result = ISC_R_SUCCESS; + +-cleanup: + if (iter1 != NULL) + isc_ht_iter_destroy(&iter1); + if (iter2 != NULL) +@@ -610,9 +597,7 @@ dns_catz_new_zones(dns_catz_zones_t **catzsp, dns_catz_zonemodmethods_t *zmm, + if (result != ISC_R_SUCCESS) + goto cleanup_mutex; + +- result = isc_ht_init(&new_zones->zones, mctx, 4); +- if (result != ISC_R_SUCCESS) +- goto cleanup_refcount; ++ isc_ht_init(&new_zones->zones, mctx, 4, ISC_HT_CASE_SENSITIVE); + + isc_mem_attach(mctx, &new_zones->mctx); + new_zones->zmm = zmm; +@@ -629,7 +614,6 @@ dns_catz_new_zones(dns_catz_zones_t **catzsp, dns_catz_zonemodmethods_t *zmm, + + cleanup_ht: + isc_ht_destroy(&new_zones->zones); +- cleanup_refcount: + isc_refcount_destroy(&new_zones->refs); + cleanup_mutex: + isc_mutex_destroy(&new_zones->lock); +@@ -672,9 +656,7 @@ dns_catz_new_zone(dns_catz_zones_t *catzs, dns_catz_zone_t **zonep, + if (result != ISC_R_SUCCESS) + goto cleanup_newzone; + +- result = isc_ht_init(&new_zone->entries, catzs->mctx, 4); +- if (result != ISC_R_SUCCESS) +- goto cleanup_name; ++ isc_ht_init(&new_zone->entries, catzs->mctx, 4, ISC_HT_CASE_SENSITIVE); + + new_zone->updatetimer = NULL; + result = isc_timer_create(catzs->timermgr, isc_timertype_inactive, +@@ -703,7 +685,6 @@ dns_catz_new_zone(dns_catz_zones_t *catzs, dns_catz_zone_t **zonep, + + cleanup_ht: + isc_ht_destroy(&new_zone->entries); +- cleanup_name: + dns_name_free(&new_zone->name, catzs->mctx); + cleanup_newzone: + isc_mem_put(catzs->mctx, new_zone, sizeof(*new_zone)); +@@ -805,8 +786,7 @@ dns_catz_zone_detach(dns_catz_zone_t **zonep) { + if (refs == 0) { + isc_mem_t *mctx = zone->catzs->mctx; + if (zone->entries != NULL) { +- result = isc_ht_iter_create(zone->entries, &iter); +- INSIST(result == ISC_R_SUCCESS); ++ isc_ht_iter_create(zone->entries, &iter); + for (result = isc_ht_iter_first(iter); + result == ISC_R_SUCCESS; + result = isc_ht_iter_delcurrent_next(iter)) +@@ -865,8 +845,7 @@ dns_catz_catzs_detach(dns_catz_zones_t **catzsp) { + catzs->magic = 0; + DESTROYLOCK(&catzs->lock); + if (catzs->zones != NULL) { +- result = isc_ht_iter_create(catzs->zones, &iter); +- INSIST(result == ISC_R_SUCCESS); ++ isc_ht_iter_create(catzs->zones, &iter); + for (result = isc_ht_iter_first(iter); + result == ISC_R_SUCCESS;) + { +@@ -1996,8 +1975,7 @@ dns_catz_prereconfig(dns_catz_zones_t *catzs) { + + REQUIRE(DNS_CATZ_ZONES_VALID(catzs)); + +- result = isc_ht_iter_create(catzs->zones, &iter); +- INSIST(result == ISC_R_SUCCESS); ++ isc_ht_iter_create(catzs->zones, &iter); + for (result = isc_ht_iter_first(iter); + result == ISC_R_SUCCESS; + result = isc_ht_iter_next(iter)) +@@ -2019,8 +1997,7 @@ dns_catz_postreconfig(dns_catz_zones_t *catzs) { + REQUIRE(DNS_CATZ_ZONES_VALID(catzs)); + + LOCK(&catzs->lock); +- result = isc_ht_iter_create(catzs->zones, &iter); +- INSIST(result == ISC_R_SUCCESS); ++ isc_ht_iter_create(catzs->zones, &iter); + for (result = isc_ht_iter_first(iter); + result == ISC_R_SUCCESS;) + { +@@ -2062,5 +2039,6 @@ dns_catz_postreconfig(dns_catz_zones_t *catzs) { + isc_result_t + dns_catz_get_iterator(dns_catz_zone_t *catz, isc_ht_iter_t **itp) { + REQUIRE(DNS_CATZ_ZONE_VALID(catz)); +- return (isc_ht_iter_create(catz->entries, itp)); ++ isc_ht_iter_create(catz->entries, itp); ++ return (ISC_R_SUCCESS); + } +diff --git a/lib/dns/include/dns/message.h b/lib/dns/include/dns/message.h +index f64522b43a..96c5ef1f79 100644 +--- a/lib/dns/include/dns/message.h ++++ b/lib/dns/include/dns/message.h +@@ -758,45 +758,6 @@ dns_message_findtype(dns_name_t *name, dns_rdatatype_t type, + *\li #ISC_R_NOTFOUND -- the desired type does not exist. + */ + +-isc_result_t +-dns_message_find(dns_name_t *name, dns_rdataclass_t rdclass, +- dns_rdatatype_t type, dns_rdatatype_t covers, +- dns_rdataset_t **rdataset); +-/*%< +- * Search the name for the specified rdclass and type. If it is found, +- * *rdataset is filled in with a pointer to that rdataset. +- * +- * Requires: +- *\li if '**rdataset' is non-NULL, *rdataset needs to be NULL. +- * +- *\li 'type' be a valid type, and NOT dns_rdatatype_any. +- * +- *\li If 'type' is dns_rdatatype_rrsig, 'covers' must be a valid type. +- * Otherwise it should be 0. +- * +- * Returns: +- *\li #ISC_R_SUCCESS -- all is well. +- *\li #ISC_R_NOTFOUND -- the desired type does not exist. +- */ +- +-void +-dns_message_movename(dns_message_t *msg, dns_name_t *name, +- dns_section_t fromsection, +- dns_section_t tosection); +-/*%< +- * Move a name from one section to another. +- * +- * Requires: +- * +- *\li 'msg' be valid. +- * +- *\li 'name' must be a name already in 'fromsection'. +- * +- *\li 'fromsection' must be a valid section. +- * +- *\li 'tosection' must be a valid section. +- */ +- + void + dns_message_addname(dns_message_t *msg, dns_name_t *name, + dns_section_t section); +diff --git a/lib/dns/include/dns/name.h b/lib/dns/include/dns/name.h +index 0ca5d47546..2b8c0390ff 100644 +--- a/lib/dns/include/dns/name.h ++++ b/lib/dns/include/dns/name.h +@@ -68,6 +68,7 @@ + #include + #include + ++#include + #include + #include + #include /* Required for storage size of dns_label_t. */ +@@ -111,6 +112,7 @@ struct dns_name { + isc_buffer_t * buffer; + ISC_LINK(dns_name_t) link; + ISC_LIST(dns_rdataset_t) list; ++ isc_ht_t *ht; + }; + + #define DNS_NAME_MAGIC ISC_MAGIC('D','N','S','n') +@@ -171,7 +173,7 @@ LIBDNS_EXTERNAL_DATA extern dns_name_t *dns_wildcardname; + A, (sizeof(A) - 1), sizeof(B), \ + DNS_NAMEATTR_READONLY, \ + B, NULL, { (void *)-1, (void *)-1}, \ +- {NULL, NULL} \ ++ {NULL, NULL}, NULL \ + } + + #define DNS_NAME_INITABSOLUTE(A,B) { \ +@@ -179,12 +181,12 @@ LIBDNS_EXTERNAL_DATA extern dns_name_t *dns_wildcardname; + A, sizeof(A), sizeof(B), \ + DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE, \ + B, NULL, { (void *)-1, (void *)-1}, \ +- {NULL, NULL} \ ++ {NULL, NULL}, NULL \ + } + + #define DNS_NAME_INITEMPTY { \ + DNS_NAME_MAGIC, NULL, 0, 0, 0, NULL, NULL, \ +- { (void *)-1, (void *)-1 }, { NULL, NULL } \ ++ { (void *)-1, (void *)-1 }, { NULL, NULL }, NULL \ + } + + /*% +@@ -1374,6 +1376,7 @@ do { \ + _n->buffer = NULL; \ + ISC_LINK_INIT(_n, link); \ + ISC_LIST_INIT(_n->list); \ ++ _n->ht = NULL; \ + } while (0) + + #define DNS_NAME_RESET(n) \ +diff --git a/lib/dns/message.c b/lib/dns/message.c +index 2812ab5a37..a44eb2d1e9 100644 +--- a/lib/dns/message.c ++++ b/lib/dns/message.c +@@ -21,6 +21,8 @@ + #include + + #include ++#include ++#include + #include + #include + #include +@@ -189,6 +191,9 @@ msgblock_allocate(isc_mem_t *, unsigned int, unsigned int); + #define msgblock_get(block, type) \ + ((type *)msgblock_internalget(block, sizeof(type))) + ++static void ++dns__message_puttemprdataset(dns_message_t *msg, dns_rdataset_t **rdatasetp); ++ + static inline void * + msgblock_internalget(dns_msgblock_t *, unsigned int); + +@@ -502,7 +507,7 @@ msgresetopt(dns_message_t *msg) + } + INSIST(dns_rdataset_isassociated(msg->opt)); + dns_rdataset_disassociate(msg->opt); +- isc_mempool_put(msg->rdspool, msg->opt); ++ dns__message_puttemprdataset(msg, &msg->opt); + msg->opt = NULL; + msg->cc_ok = 0; + msg->cc_bad = 0; +@@ -523,10 +528,11 @@ msgresetsigs(dns_message_t *msg, bool replying) { + msg->querytsig = msg->tsig; + } else { + dns_rdataset_disassociate(msg->tsig); +- isc_mempool_put(msg->rdspool, msg->tsig); ++ dns__message_puttemprdataset(msg, &msg->tsig); + if (msg->querytsig != NULL) { + dns_rdataset_disassociate(msg->querytsig); +- isc_mempool_put(msg->rdspool, msg->querytsig); ++ dns__message_puttemprdataset(msg, ++ &msg->querytsig); + } + } + if (dns_name_dynamic(msg->tsigname)) +@@ -536,13 +542,10 @@ msgresetsigs(dns_message_t *msg, bool replying) { + msg->tsigname = NULL; + } else if (msg->querytsig != NULL && !replying) { + dns_rdataset_disassociate(msg->querytsig); +- isc_mempool_put(msg->rdspool, msg->querytsig); +- msg->querytsig = NULL; ++ dns__message_puttemprdataset(msg, &msg->querytsig); + } + if (msg->sig0 != NULL) { +- INSIST(dns_rdataset_isassociated(msg->sig0)); +- dns_rdataset_disassociate(msg->sig0); +- isc_mempool_put(msg->rdspool, msg->sig0); ++ dns__message_puttemprdataset(msg, &msg->sig0); + if (msg->sig0name != NULL) { + if (dns_name_dynamic(msg->sig0name)) + dns_name_free(msg->sig0name, msg->mctx); +@@ -863,6 +866,18 @@ dns_message_detach(dns_message_t **messagep) { + } + } + ++static isc_result_t ++name_hash_add(isc_ht_t *ht, dns_name_t *name, dns_name_t **foundp) { ++ isc_result_t result = isc_ht_find(ht, name->ndata, name->length, ++ (void **)foundp); ++ if (result == ISC_R_SUCCESS) { ++ return (ISC_R_EXISTS); ++ } ++ result = isc_ht_add(ht, name->ndata, name->length, (void *)name); ++ INSIST(result == ISC_R_SUCCESS); ++ return (ISC_R_SUCCESS); ++} ++ + static isc_result_t + findname(dns_name_t **foundname, dns_name_t *target, + dns_namelist_t *section) +@@ -882,28 +897,36 @@ findname(dns_name_t **foundname, dns_name_t *target, + return (ISC_R_NOTFOUND); + } + +-isc_result_t +-dns_message_find(dns_name_t *name, dns_rdataclass_t rdclass, +- dns_rdatatype_t type, dns_rdatatype_t covers, +- dns_rdataset_t **rdataset) +-{ +- dns_rdataset_t *curr; +- +- REQUIRE(name != NULL); +- REQUIRE(rdataset == NULL || *rdataset == NULL); ++#ifdef _WIN32 ++__pragma(pack(push, 1)) ++typedef struct rds_key { ++ dns_rdataclass_t rdclass; ++ dns_rdatatype_t type; ++ dns_rdatatype_t covers; ++} rds_key_t; ++__pragma(pack(pop)) ++#else ++typedef struct __attribute__((__packed__)) rds_key { ++ dns_rdataclass_t rdclass; ++ dns_rdatatype_t type; ++ dns_rdatatype_t covers; ++} rds_key_t; ++#endif + +- for (curr = ISC_LIST_TAIL(name->list); +- curr != NULL; +- curr = ISC_LIST_PREV(curr, link)) { +- if (curr->rdclass == rdclass && +- curr->type == type && curr->covers == covers) { +- if (rdataset != NULL) +- *rdataset = curr; +- return (ISC_R_SUCCESS); +- } ++static isc_result_t ++rds_hash_add(isc_ht_t *ht, dns_rdataset_t *rds, dns_rdataset_t **foundp) { ++ rds_key_t key = { .rdclass = rds->rdclass, ++ .type = rds->type, ++ .covers = rds->covers }; ++ isc_result_t result = isc_ht_find(ht, (const unsigned char *)&key, ++ sizeof(key), (void **)foundp); ++ if (result == ISC_R_SUCCESS) { ++ return (ISC_R_EXISTS); + } +- +- return (ISC_R_NOTFOUND); ++ result = isc_ht_add(ht, (const unsigned char *)&key, sizeof(key), ++ (void *)rds); ++ INSIST(result == ISC_R_SUCCESS); ++ return (ISC_R_SUCCESS); + } + + isc_result_t +@@ -1031,6 +1054,18 @@ getrdata(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + } \ + } while (0) + ++static void ++cleanup_name_hashmaps(dns_namelist_t *section) { ++ dns_name_t *name = NULL; ++ for (name = ISC_LIST_HEAD(*section); name != NULL; ++ name = ISC_LIST_NEXT(name, link)) ++ { ++ if (name->ht != NULL) { ++ isc_ht_destroy(&name->ht); ++ } ++ } ++} ++ + static isc_result_t + getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + unsigned int options) +@@ -1042,13 +1077,15 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + dns_offsets_t *offsets; + dns_rdataset_t *rdataset; + dns_rdatalist_t *rdatalist; +- isc_result_t result; ++ isc_result_t result = ISC_R_SUCCESS; + dns_rdatatype_t rdtype; + dns_rdataclass_t rdclass; + dns_namelist_t *section; +- bool free_name; ++ bool free_name = false; + bool best_effort; + bool seen_problem; ++ isc_ht_t *name_map = NULL; ++ bool free_ht = false; + + section = &msg->sections[DNS_SECTION_QUESTION]; + +@@ -1056,9 +1093,14 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + seen_problem = false; + + name = NULL; ++ name2 = NULL; + rdataset = NULL; + rdatalist = NULL; + ++ if (msg->counts[DNS_SECTION_QUESTION] > 1) { ++ isc_ht_init(&name_map, msg->mctx, 1, ISC_HT_CASE_INSENSITIVE); ++ } ++ + for (count = 0; count < msg->counts[DNS_SECTION_QUESTION]; count++) { + name = isc_mempool_get(msg->namepool); + if (name == NULL) +@@ -1081,13 +1123,20 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + if (result != ISC_R_SUCCESS) + goto cleanup; + ++ ++ /* If there is only one QNAME, skip the duplicity checks */ ++ if (name_map == NULL) { ++ result = ISC_R_SUCCESS; ++ goto skip_name_check; ++ } ++ + /* + * Run through the section, looking to see if this name + * is already there. If it is found, put back the allocated + * name since we no longer need it, and set our name pointer + * to point to the name we found. + */ +- result = findname(&name2, name, section); ++ result = name_hash_add(name_map, name, &name2); + + /* + * If it is the first name in the section, accept it. +@@ -1099,18 +1148,25 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + * this should be legal or not. In either case we no longer + * need this name pointer. + */ +- if (result != ISC_R_SUCCESS) { +- if (!ISC_LIST_EMPTY(*section)) ++ skip_name_check: ++ switch (result) { ++ case ISC_R_SUCCESS: ++ if (!ISC_LIST_EMPTY(*section)) { + DO_ERROR(DNS_R_FORMERR); ++ } + ISC_LIST_APPEND(*section, name, link); +- free_name = false; +- } else { +- isc_mempool_put(msg->namepool, name); ++ break; ++ case ISC_R_EXISTS: ++ dns_message_puttempname(msg, &name); + name = name2; + name2 = NULL; +- free_name = false; ++ break; ++ default: ++ ISC_UNREACHABLE(); + } + ++ free_name = false; ++ + /* + * Get type and class. + */ +@@ -1138,13 +1194,6 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + if (rdtype == dns_rdatatype_tkey) + msg->tkey = 1; + +- /* +- * Can't ask the same question twice. +- */ +- result = dns_message_find(name, rdclass, rdtype, 0, NULL); +- if (result == ISC_R_SUCCESS) +- DO_ERROR(DNS_R_FORMERR); +- + /* + * Allocate a new rdatalist. + */ +@@ -1153,7 +1202,7 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + result = ISC_R_NOMEMORY; + goto cleanup; + } +- rdataset = isc_mempool_get(msg->rdspool); ++ dns_message_gettemprdataset(msg, &rdataset); + if (rdataset == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; +@@ -1166,32 +1215,74 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + rdatalist->type = rdtype; + rdatalist->rdclass = rdclass; + +- dns_rdataset_init(rdataset); + result = dns_rdatalist_tordataset(rdatalist, rdataset); +- if (result != ISC_R_SUCCESS) +- goto cleanup; ++ RUNTIME_CHECK(result == ISC_R_SUCCESS); + + rdataset->attributes |= DNS_RDATASETATTR_QUESTION; + ++ /* ++ * Skip the duplicity check for first rdataset ++ */ ++ if (ISC_LIST_EMPTY(name->list)) { ++ goto skip_rds_check; ++ } ++ ++ /* ++ * Can't ask the same question twice. ++ */ ++ if (name->ht == NULL) { ++ isc_ht_init(&name->ht, msg->mctx, 1, ++ ISC_HT_CASE_SENSITIVE); ++ free_ht = true; ++ ++ dns_rdataset_t *old_rdataset = NULL; ++ for (old_rdataset = ISC_LIST_HEAD(name->list); ++ old_rdataset != NULL; ++ old_rdataset = ISC_LIST_NEXT(old_rdataset, link)) ++ { ++ result = rds_hash_add(name->ht, old_rdataset, ++ NULL); ++ INSIST(result == ISC_R_SUCCESS); ++ } ++ } ++ result = rds_hash_add(name->ht, rdataset, NULL); ++ if (result == ISC_R_EXISTS) { ++ DO_ERROR(DNS_R_FORMERR); ++ } ++ ++ skip_rds_check: + ISC_LIST_APPEND(name->list, rdataset, link); ++ + rdataset = NULL; + } + +- if (seen_problem) +- return (DNS_R_RECOVERABLE); +- return (ISC_R_SUCCESS); ++ ++ if (seen_problem) { ++ result = DNS_R_RECOVERABLE; ++ } + + cleanup: + if (rdataset != NULL) { +- INSIST(!dns_rdataset_isassociated(rdataset)); +- isc_mempool_put(msg->rdspool, rdataset); ++ if (dns_rdataset_isassociated(rdataset)) { ++ dns_rdataset_disassociate(rdataset); ++ } ++ dns_message_puttemprdataset(msg, &rdataset); + } + #if 0 + if (rdatalist != NULL) + isc_mempool_put(msg->rdlpool, rdatalist); + #endif +- if (free_name) +- isc_mempool_put(msg->namepool, name); ++ if (free_name) { ++ dns_message_puttempname(msg, &name); ++ } ++ ++ if (free_ht) { ++ cleanup_name_hashmaps(section); ++ } ++ ++ if (name_map != NULL) { ++ isc_ht_destroy(&name_map); ++ } + + return (result); + } +@@ -1271,24 +1362,26 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + unsigned int count, rdatalen; + dns_name_t *name = NULL; + dns_name_t *name2 = NULL; +- dns_offsets_t *offsets; +- dns_rdataset_t *rdataset; +- dns_rdatalist_t *rdatalist; +- isc_result_t result; ++ dns_offsets_t *offsets = NULL; ++ dns_rdataset_t *rdataset = NULL; ++ dns_rdataset_t *found_rdataset = NULL; ++ dns_rdatalist_t *rdatalist = NULL; ++ isc_result_t result = ISC_R_SUCCESS; + dns_rdatatype_t rdtype, covers; + dns_rdataclass_t rdclass; +- dns_rdata_t *rdata; ++ dns_rdata_t *rdata = NULL; + dns_ttl_t ttl; +- dns_namelist_t *section; +- bool free_name = false, free_rdataset = false; +- bool preserve_order, best_effort, seen_problem; ++ dns_namelist_t *section = &msg->sections[sectionid]; ++ bool free_name = false, seen_problem = false; ++ bool free_ht = false; ++ bool preserve_order = ((options & DNS_MESSAGEPARSE_PRESERVEORDER) != 0); ++ bool best_effort = ((options & DNS_MESSAGEPARSE_BESTEFFORT) != 0); + bool isedns, issigzero, istsig; ++ isc_ht_t *name_map = NULL; + +- preserve_order = ((options & DNS_MESSAGEPARSE_PRESERVEORDER) != 0); +- best_effort = ((options & DNS_MESSAGEPARSE_BESTEFFORT) != 0); +- seen_problem = false; +- +- section = &msg->sections[sectionid]; ++ if (msg->counts[sectionid] > 1) { ++ isc_ht_init(&name_map, msg->mctx, 1, ISC_HT_CASE_INSENSITIVE); ++ } + + for (count = 0; count < msg->counts[sectionid]; count++) { + int recstart = source->current; +@@ -1296,14 +1389,15 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + + skip_name_search = false; + skip_type_search = false; +- free_rdataset = false; + isedns = false; + issigzero = false; + istsig = false; ++ found_rdataset = NULL; + + name = isc_mempool_get(msg->namepool); +- if (name == NULL) ++ if (name == NULL) { + return (ISC_R_NOMEMORY); ++ } + free_name = true; + + offsets = newoffsets(msg); +@@ -1319,8 +1413,9 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + isc_buffer_remainingregion(source, &r); + isc_buffer_setactive(source, r.length); + result = getname(name, source, msg, dctx); +- if (result != ISC_R_SUCCESS) ++ if (result != ISC_R_SUCCESS) { + goto cleanup; ++ } + + /* + * Get type, class, ttl, and rdatalen. Verify that at least +@@ -1340,9 +1435,10 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + * established a class. Do so now. + */ + if (msg->rdclass_set == 0 && +- rdtype != dns_rdatatype_opt && /* class is UDP SIZE */ +- rdtype != dns_rdatatype_tsig && /* class is ANY */ +- rdtype != dns_rdatatype_tkey) { /* class is undefined */ ++ rdtype != dns_rdatatype_opt && /* class is UDP SIZE */ ++ rdtype != dns_rdatatype_tsig && /* class is ANY */ ++ rdtype != dns_rdatatype_tkey) /* class is undefined */ ++ { + msg->rdclass = rdclass; + msg->rdclass_set = 1; + } +@@ -1351,15 +1447,17 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + * If this class is different than the one in the question + * section, bail. + */ +- if (msg->opcode != dns_opcode_update +- && rdtype != dns_rdatatype_tsig +- && rdtype != dns_rdatatype_opt +- && rdtype != dns_rdatatype_key /* in a TKEY query */ +- && rdtype != dns_rdatatype_sig /* SIG(0) */ +- && rdtype != dns_rdatatype_tkey /* Win2000 TKEY */ +- && msg->rdclass != dns_rdataclass_any +- && msg->rdclass != rdclass) ++ if (msg->opcode != dns_opcode_update && ++ rdtype != dns_rdatatype_tsig && ++ rdtype != dns_rdatatype_opt && ++ rdtype != dns_rdatatype_key && /* in a TKEY query */ ++ rdtype != dns_rdatatype_sig && /* SIG(0) */ ++ rdtype != dns_rdatatype_tkey && /* Win2000 TKEY */ ++ msg->rdclass != dns_rdataclass_any && ++ msg->rdclass != rdclass) ++ { + DO_ERROR(DNS_R_FORMERR); ++ } + + /* + * If this is not a TKEY query/response then the KEY +@@ -1369,7 +1467,9 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + rdtype == dns_rdatatype_key && + msg->rdclass != dns_rdataclass_any && + msg->rdclass != rdclass) ++ { + DO_ERROR(DNS_R_FORMERR); ++ } + + /* + * Special type handling for TSIG, OPT, and TKEY. +@@ -1381,7 +1481,8 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + */ + if (sectionid != DNS_SECTION_ADDITIONAL || + rdclass != dns_rdataclass_any || +- count != msg->counts[sectionid] - 1) { ++ count != msg->counts[sectionid] - 1) ++ { + DO_ERROR(DNS_R_BADTSIG); + } else { + skip_name_search = true; +@@ -1396,7 +1497,8 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + */ + if (!dns_name_equal(dns_rootname, name) || + sectionid != DNS_SECTION_ADDITIONAL || +- msg->opt != NULL) { ++ msg->opt != NULL) ++ { + DO_ERROR(DNS_R_FORMERR); + } else { + skip_name_search = true; +@@ -1413,13 +1515,16 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + */ + dns_section_t tkeysection; + +- if ((msg->flags & DNS_MESSAGEFLAG_QR) == 0) ++ if ((msg->flags & DNS_MESSAGEFLAG_QR) == 0) { + tkeysection = DNS_SECTION_ADDITIONAL; +- else ++ } else { + tkeysection = DNS_SECTION_ANSWER; ++ } + if (sectionid != tkeysection && + sectionid != DNS_SECTION_ANSWER) ++ { + DO_ERROR(DNS_R_FORMERR); ++ } + } + + /* +@@ -1445,7 +1550,8 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + goto cleanup; + } + if (msg->opcode == dns_opcode_update && +- update(sectionid, rdclass)) { ++ update(sectionid, rdclass)) ++ { + if (rdatalen != 0) { + result = DNS_R_FORMERR; + goto cleanup; +@@ -1465,26 +1571,31 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + result = ISC_R_SUCCESS; + } else if (rdclass == dns_rdataclass_none && + msg->opcode == dns_opcode_update && +- sectionid == DNS_SECTION_UPDATE) { ++ sectionid == DNS_SECTION_UPDATE) ++ { + result = getrdata(source, msg, dctx, msg->rdclass, + rdtype, rdatalen, rdata); +- } else +- result = getrdata(source, msg, dctx, rdclass, +- rdtype, rdatalen, rdata); +- if (result != ISC_R_SUCCESS) ++ } else { ++ result = getrdata(source, msg, dctx, rdclass, rdtype, ++ rdatalen, rdata); ++ } ++ if (result != ISC_R_SUCCESS) { + goto cleanup; ++ } + rdata->rdclass = rdclass; +- if (rdtype == dns_rdatatype_rrsig && +- rdata->flags == 0) { ++ if (rdtype == dns_rdatatype_rrsig && rdata->flags == 0) { + covers = dns_rdata_covers(rdata); +- if (covers == 0) ++ if (covers == 0) { + DO_ERROR(DNS_R_FORMERR); ++ } + } else if (rdtype == dns_rdatatype_sig /* SIG(0) */ && +- rdata->flags == 0) { ++ rdata->flags == 0) ++ { + covers = dns_rdata_covers(rdata); + if (covers == 0) { + if (sectionid != DNS_SECTION_ADDITIONAL || +- count != msg->counts[sectionid] - 1) { ++ count != msg->counts[sectionid] - 1) ++ { + DO_ERROR(DNS_R_BADSIG0); + } else { + skip_name_search = true; +@@ -1494,17 +1605,20 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + } else { + if (msg->rdclass != dns_rdataclass_any && + msg->rdclass != rdclass) ++ { + DO_ERROR(DNS_R_FORMERR); ++ } + } +- } else ++ } else { + covers = 0; ++ } + + /* + * Check the ownername of NSEC3 records + */ + if (rdtype == dns_rdatatype_nsec3 && +- !dns_rdata_checkowner(name, msg->rdclass, rdtype, +- false)) { ++ !dns_rdata_checkowner(name, msg->rdclass, rdtype, false)) ++ { + result = DNS_R_BADOWNERNAME; + goto cleanup; + } +@@ -1515,103 +1629,152 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + * to the end of the message. + */ + if (preserve_order || msg->opcode == dns_opcode_update || +- skip_name_search) { ++ skip_name_search) ++ { + if (!isedns && !istsig && !issigzero) { + ISC_LIST_APPEND(*section, name, link); + free_name = false; + } + } else { ++ if (name_map == NULL) { ++ result = ISC_R_SUCCESS; ++ goto skip_name_check; ++ } ++ + /* + * Run through the section, looking to see if this name + * is already there. If it is found, put back the + * allocated name since we no longer need it, and set + * our name pointer to point to the name we found. + */ +- result = findname(&name2, name, section); ++ result = name_hash_add(name_map, name, &name2); + + /* + * If it is a new name, append to the section. + */ +- if (result == ISC_R_SUCCESS) { +- isc_mempool_put(msg->namepool, name); +- name = name2; +- } else { ++ skip_name_check: ++ switch (result) { ++ case ISC_R_SUCCESS: + ISC_LIST_APPEND(*section, name, link); ++ break; ++ case ISC_R_EXISTS: ++ dns_message_puttempname(msg, &name); ++ name = name2; ++ name2 = NULL; ++ break; ++ default: ++ ISC_UNREACHABLE(); + } + free_name = false; + } + ++ dns_message_gettemprdataset(msg, &rdataset); ++ if (rdataset == NULL) { ++ result = ISC_R_NOMEMORY; ++ goto cleanup; ++ } ++ ++ rdatalist = newrdatalist(msg); ++ if (rdatalist == NULL) { ++ result = ISC_R_NOMEMORY; ++ goto cleanup; ++ } ++ ++ rdatalist->type = rdtype; ++ rdatalist->covers = covers; ++ rdatalist->rdclass = rdclass; ++ rdatalist->ttl = ttl; ++ ++ RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist, rdataset) == ++ ISC_R_SUCCESS); ++ dns_rdataset_setownercase(rdataset, name); ++ rdatalist = NULL; ++ + /* + * Search name for the particular type and class. + * Skip this stage if in update mode or this is a meta-type. + */ +- if (preserve_order || msg->opcode == dns_opcode_update || +- skip_type_search) +- result = ISC_R_NOTFOUND; +- else { ++ if (isedns || istsig || issigzero) { ++ /* Skip adding the rdataset to the tables */ ++ } else if (preserve_order || msg->opcode == dns_opcode_update || ++ skip_type_search) ++ { ++ result = ISC_R_SUCCESS; ++ ++ ISC_LIST_APPEND(name->list, rdataset, link); ++ } else { + /* + * If this is a type that can only occur in + * the question section, fail. + */ +- if (dns_rdatatype_questiononly(rdtype)) ++ if (dns_rdatatype_questiononly(rdtype)) { + DO_ERROR(DNS_R_FORMERR); +- +- rdataset = NULL; +- result = dns_message_find(name, rdclass, rdtype, +- covers, &rdataset); +- } +- +- /* +- * If we found an rdataset that matches, we need to +- * append this rdata to that set. If we did not, we need +- * to create a new rdatalist, store the important bits there, +- * convert it to an rdataset, and link the latter to the name. +- * Yuck. When appending, make certain that the type isn't +- * a singleton type, such as SOA or CNAME. +- * +- * Note that this check will be bypassed when preserving order, +- * the opcode is an update, or the type search is skipped. +- */ +- if (result == ISC_R_SUCCESS) { +- if (dns_rdatatype_issingleton(rdtype)) { +- dns_rdata_t *first; +- dns_rdatalist_fromrdataset(rdataset, +- &rdatalist); +- first = ISC_LIST_HEAD(rdatalist->rdata); +- INSIST(first != NULL); +- if (dns_rdata_compare(rdata, first) != 0) +- DO_ERROR(DNS_R_FORMERR); + } +- } + +- if (result == ISC_R_NOTFOUND) { +- rdataset = isc_mempool_get(msg->rdspool); +- if (rdataset == NULL) { +- result = ISC_R_NOMEMORY; +- goto cleanup; ++ if (ISC_LIST_EMPTY(name->list)) { ++ result = ISC_R_SUCCESS; ++ goto skip_rds_check; + } +- free_rdataset = true; + +- rdatalist = newrdatalist(msg); +- if (rdatalist == NULL) { +- result = ISC_R_NOMEMORY; +- goto cleanup; +- } ++ if (name->ht == NULL) { ++ isc_ht_init(&name->ht, msg->mctx, 1, ++ ISC_HT_CASE_SENSITIVE); ++ free_ht = true; + +- rdatalist->type = rdtype; +- rdatalist->covers = covers; +- rdatalist->rdclass = rdclass; +- rdatalist->ttl = ttl; ++ INSIST(ISC_LIST_HEAD(name->list) == ++ ISC_LIST_TAIL(name->list)); + +- dns_rdataset_init(rdataset); +- RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist, +- rdataset) +- == ISC_R_SUCCESS); +- dns_rdataset_setownercase(rdataset, name); ++ dns_rdataset_t *old_rdataset = ++ ISC_LIST_HEAD(name->list); + +- if (!isedns && !istsig && !issigzero) { ++ result = rds_hash_add(name->ht, old_rdataset, ++ NULL); ++ ++ INSIST(result == ISC_R_SUCCESS); ++ } ++ result = rds_hash_add(name->ht, rdataset, ++ &found_rdataset); ++ ++ /* ++ * If we found an rdataset that matches, we need to ++ * append this rdata to that set. If we did not, we ++ * need to create a new rdatalist, store the important ++ * bits there, convert it to an rdataset, and link the ++ * latter to the name. Yuck. When appending, make ++ * certain that the type isn't a singleton type, such as ++ * SOA or CNAME. ++ * ++ * Note that this check will be bypassed when preserving ++ * order, the opcode is an update, or the type search is ++ * skipped. ++ */ ++ skip_rds_check: ++ switch (result) { ++ case ISC_R_EXISTS: ++ /* Free the rdataset we used as the key */ ++ dns_rdataset_disassociate(rdataset); ++ dns__message_puttemprdataset(msg, &rdataset); ++ result = ISC_R_SUCCESS; ++ rdataset = found_rdataset; ++ ++ if (!dns_rdatatype_issingleton(rdtype)) { ++ break; ++ } ++ ++ dns_rdatalist_fromrdataset(rdataset, ++ &rdatalist); ++ dns_rdata_t *first = ++ ISC_LIST_HEAD(rdatalist->rdata); ++ INSIST(first != NULL); ++ if (dns_rdata_compare(rdata, first) != 0) { ++ DO_ERROR(DNS_R_FORMERR); ++ } ++ break; ++ case ISC_R_SUCCESS: + ISC_LIST_APPEND(name->list, rdataset, link); +- free_rdataset = false; ++ break; ++ default: ++ ISC_UNREACHABLE(); + } + } + +@@ -1625,8 +1788,9 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + */ + if (ttl != rdataset->ttl) { + rdataset->attributes |= DNS_RDATASETATTR_TTLADJUSTED; +- if (ttl < rdataset->ttl) ++ if (ttl < rdataset->ttl) { + rdataset->ttl = ttl; ++ } + } + + /* Append this rdata to the rdataset. */ +@@ -1645,20 +1809,16 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + dns_rcode_t ercode; + + msg->opt = rdataset; +- rdataset = NULL; +- free_rdataset = false; + ercode = (dns_rcode_t) + ((msg->opt->ttl & DNS_MESSAGE_EDNSRCODE_MASK) + >> 20); + msg->rcode |= ercode; +- isc_mempool_put(msg->namepool, name); ++ dns_message_puttempname(msg, &name); + free_name = false; + } else if (issigzero) { + msg->sig0 = rdataset; + msg->sig0name = name; + msg->sigstart = recstart; +- rdataset = NULL; +- free_rdataset = false; + free_name = false; + } else if (istsig) { + msg->tsig = rdataset; +@@ -1668,20 +1828,17 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + * Windows doesn't like TSIG names to be compressed. + */ + msg->tsigname->attributes |= DNS_NAMEATTR_NOCOMPRESS; +- rdataset = NULL; +- free_rdataset = false; + free_name = false; + } ++ rdataset = NULL; + + if (seen_problem) { +- if (free_name) +- isc_mempool_put(msg->namepool, name); +- if (free_rdataset) +- isc_mempool_put(msg->rdspool, rdataset); +- free_name = free_rdataset = false; ++ if (free_name) { ++ dns_message_puttempname(msg, &name); ++ } ++ free_name = false; + } + INSIST(free_name == false); +- INSIST(free_rdataset == false); + } + + /* +@@ -1692,20 +1849,32 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + if (sectionid == DNS_SECTION_AUTHORITY && + msg->opcode == dns_opcode_query && + ((msg->flags & DNS_MESSAGEFLAG_QR) != 0) && +- ((msg->flags & DNS_MESSAGEFLAG_TC) == 0) && +- !preserve_order && ++ ((msg->flags & DNS_MESSAGEFLAG_TC) == 0) && !preserve_order && + !auth_signed(section)) ++ { + DO_ERROR(DNS_R_FORMERR); ++ } + +- if (seen_problem) +- return (DNS_R_RECOVERABLE); +- return (ISC_R_SUCCESS); ++ if (seen_problem) { ++ result = DNS_R_RECOVERABLE; ++ } + +- cleanup: +- if (free_name) +- isc_mempool_put(msg->namepool, name); +- if (free_rdataset) ++cleanup: ++ if (rdataset != NULL && rdataset != found_rdataset) { ++ dns_rdataset_disassociate(rdataset); + isc_mempool_put(msg->rdspool, rdataset); ++ } ++ if (free_name) { ++ dns_message_puttempname(msg, &name); ++ } ++ ++ if (free_ht) { ++ cleanup_name_hashmaps(section); ++ } ++ ++ if (name_map != NULL) { ++ isc_ht_destroy(&name_map); ++ } + + return (result); + } +@@ -2438,11 +2607,11 @@ dns_message_renderreset(dns_message_t *msg) { + dns_message_puttempname(msg, &msg->tsigname); + if (msg->tsig != NULL) { + dns_rdataset_disassociate(msg->tsig); +- dns_message_puttemprdataset(msg, &msg->tsig); ++ dns__message_puttemprdataset(msg, &msg->tsig); + } + if (msg->sig0 != NULL) { + dns_rdataset_disassociate(msg->sig0); +- dns_message_puttemprdataset(msg, &msg->sig0); ++ dns__message_puttemprdataset(msg, &msg->sig0); + } + } + +@@ -2535,24 +2704,6 @@ dns_message_findname(dns_message_t *msg, dns_section_t section, + return (result); + } + +-void +-dns_message_movename(dns_message_t *msg, dns_name_t *name, +- dns_section_t fromsection, +- dns_section_t tosection) +-{ +- REQUIRE(msg != NULL); +- REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER); +- REQUIRE(name != NULL); +- REQUIRE(VALID_NAMED_SECTION(fromsection)); +- REQUIRE(VALID_NAMED_SECTION(tosection)); +- +- /* +- * Unlink the name from the old section +- */ +- ISC_LIST_UNLINK(msg->sections[fromsection], name, link); +- ISC_LIST_APPEND(msg->sections[tosection], name, link); +-} +- + void + dns_message_addname(dns_message_t *msg, dns_name_t *name, + dns_section_t section) +@@ -2645,6 +2796,9 @@ dns_message_puttempname(dns_message_t *msg, dns_name_t **item) { + REQUIRE(DNS_MESSAGE_VALID(msg)); + REQUIRE(item != NULL && *item != NULL); + ++ if ((*item)->ht != NULL) { ++ isc_ht_destroy(&(*item)->ht); ++ } + if (dns_name_dynamic(*item)) + dns_name_free(*item, msg->mctx); + isc_mempool_put(msg->namepool, *item); +@@ -2660,14 +2814,19 @@ dns_message_puttemprdata(dns_message_t *msg, dns_rdata_t **item) { + *item = NULL; + } + ++static void ++dns__message_puttemprdataset(dns_message_t *msg, dns_rdataset_t **item) { ++ isc_mempool_put(msg->rdspool, *item); ++ *item = NULL; ++} ++ + void + dns_message_puttemprdataset(dns_message_t *msg, dns_rdataset_t **item) { + REQUIRE(DNS_MESSAGE_VALID(msg)); + REQUIRE(item != NULL && *item != NULL); + + REQUIRE(!dns_rdataset_isassociated(*item)); +- isc_mempool_put(msg->rdspool, *item); +- *item = NULL; ++ dns__message_puttemprdataset(msg, item); + } + + void +@@ -2832,7 +2991,7 @@ dns_message_setopt(dns_message_t *msg, dns_rdataset_t *opt) { + + cleanup: + dns_rdataset_disassociate(opt); +- dns_message_puttemprdataset(msg, &opt); ++ dns__message_puttemprdataset(msg, &opt); + return (result); + } + +diff --git a/lib/dns/name.c b/lib/dns/name.c +index 9713cf5f4e..a0a53b26d6 100644 +--- a/lib/dns/name.c ++++ b/lib/dns/name.c +@@ -215,6 +215,7 @@ dns_name_invalidate(dns_name_t *name) { + name->offsets = NULL; + name->buffer = NULL; + ISC_LINK_INIT(name, link); ++ INSIST(name->ht == NULL); + } + + bool +diff --git a/lib/isc/ht.c b/lib/isc/ht.c +index 3234ea8029..19f0b2c0b9 100644 +--- a/lib/isc/ht.c ++++ b/lib/isc/ht.c +@@ -1,6 +1,8 @@ + /* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * ++ * SPDX-License-Identifier: MPL-2.0 ++ * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. +@@ -10,208 +12,483 @@ + */ + + #include +- + #include + #include + + #include + #include +-#include + #include + #include + #include ++#include + #include + +- + typedef struct isc_ht_node isc_ht_node_t; + +-#define ISC_HT_MAGIC ISC_MAGIC('H', 'T', 'a', 'b') +-#define ISC_HT_VALID(ht) ISC_MAGIC_VALID(ht, ISC_HT_MAGIC) ++#define ISC_HT_MAGIC ISC_MAGIC('H', 'T', 'a', 'b') ++#define ISC_HT_VALID(ht) ISC_MAGIC_VALID(ht, ISC_HT_MAGIC) ++ ++#define HT_NO_BITS 0 ++#define HT_MIN_BITS 1 ++#define HT_MAX_BITS 32 ++#define HT_OVERCOMMIT 3 ++ ++#define HT_NEXTTABLE(idx) ((idx == 0) ? 1 : 0) ++#define TRY_NEXTTABLE(idx, ht) (idx == ht->hindex && rehashing_in_progress(ht)) ++ ++#define GOLDEN_RATIO_32 0x61C88647 ++ ++#define HASHSIZE(bits) (UINT64_C(1) << (bits)) + + struct isc_ht_node { + void *value; + isc_ht_node_t *next; ++ uint32_t hashval; + size_t keysize; +- unsigned char key[FLEXIBLE_ARRAY_MEMBER]; ++ unsigned char key[]; + }; + + struct isc_ht { + unsigned int magic; + isc_mem_t *mctx; +- size_t size; +- size_t mask; +- unsigned int count; +- isc_ht_node_t **table; ++ size_t count; ++ bool case_sensitive; ++ size_t size[2]; ++ uint8_t hashbits[2]; ++ isc_ht_node_t **table[2]; ++ uint8_t hindex; ++ uint32_t hiter; /* rehashing iterator */ + }; + + struct isc_ht_iter { + isc_ht_t *ht; + size_t i; ++ uint8_t hindex; + isc_ht_node_t *cur; + }; + +-isc_result_t +-isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, uint8_t bits) { +- isc_ht_t *ht = NULL; +- size_t i; ++static isc_ht_node_t * ++isc__ht_find(const isc_ht_t *ht, const unsigned char *key, ++ const uint32_t keysize, const uint32_t hashval, const uint8_t idx); ++static void ++isc__ht_add(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize, ++ const uint32_t hashval, const uint8_t idx, void *value); ++static isc_result_t ++isc__ht_delete(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize, ++ const uint32_t hashval, const uint8_t idx); ++ ++static uint32_t ++rehash_bits(isc_ht_t *ht, size_t newcount); ++ ++static void ++hashtable_new(isc_ht_t *ht, const uint8_t idx, const uint8_t bits); ++static void ++hashtable_free(isc_ht_t *ht, const uint8_t idx); ++static void ++hashtable_rehash(isc_ht_t *ht, uint32_t newbits); ++static void ++hashtable_rehash_one(isc_ht_t *ht); ++static void ++maybe_rehash(isc_ht_t *ht, size_t newcount); ++ ++static isc_result_t ++isc__ht_iter_next(isc_ht_iter_t *it); ++ ++static uint8_t maptolower[] = { ++ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, ++ 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, ++ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, ++ 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, ++ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, ++ 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, ++ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, ++ 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, ++ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, ++ 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, ++ 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, ++ 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, ++ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, ++ 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, ++ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, ++ 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, ++ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, ++ 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, ++ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, ++ 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, ++ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, ++ 0xfc, 0xfd, 0xfe, 0xff ++}; + +- REQUIRE(htp != NULL && *htp == NULL); +- REQUIRE(mctx != NULL); +- REQUIRE(bits >= 1 && bits <= (sizeof(size_t)*8 - 1)); ++static int ++memcasecmp(const void *vs1, const void *vs2, size_t len) { ++ uint8_t const *s1 = vs1; ++ uint8_t const *s2 = vs2; ++ for (size_t i = 0; i < len; i++) { ++ uint8_t u1 = s1[i]; ++ uint8_t u2 = s2[i]; ++ int U1 = maptolower[u1]; ++ int U2 = maptolower[u2]; ++ int diff = U1 - U2; ++ if (diff) { ++ return diff; ++ } ++ } ++ return 0; ++} + +- ht = isc_mem_get(mctx, sizeof(struct isc_ht)); +- if (ht == NULL) { +- return (ISC_R_NOMEMORY); ++static bool ++isc__ht_node_match(isc_ht_node_t *node, const uint32_t hashval, ++ const uint8_t *key, uint32_t keysize, bool case_sensitive) { ++ return (node->hashval == hashval && node->keysize == keysize && ++ (case_sensitive ? (memcmp(node->key, key, keysize) == 0) ++ : (memcasecmp(node->key, key, keysize) == 0))); ++} ++ ++static uint32_t ++hash_32(uint32_t val, unsigned int bits) { ++ REQUIRE(bits <= HT_MAX_BITS); ++ /* High bits are more random. */ ++ return (val * GOLDEN_RATIO_32 >> (32 - bits)); ++} ++ ++static bool ++rehashing_in_progress(const isc_ht_t *ht) { ++ return (ht->table[HT_NEXTTABLE(ht->hindex)] != NULL); ++} ++ ++static bool ++hashtable_is_overcommited(isc_ht_t *ht) { ++ return (ht->count >= (ht->size[ht->hindex] * HT_OVERCOMMIT)); ++} ++ ++static uint32_t ++rehash_bits(isc_ht_t *ht, size_t newcount) { ++ uint32_t newbits = ht->hashbits[ht->hindex]; ++ ++ while (newcount >= HASHSIZE(newbits) && newbits <= HT_MAX_BITS) { ++ newbits += 1; + } + +- ht->mctx = NULL; +- isc_mem_attach(mctx, &ht->mctx); ++ return (newbits); ++} ++ ++/* ++ * Rebuild the hashtable to reduce the load factor ++ */ ++static void ++hashtable_rehash(isc_ht_t *ht, uint32_t newbits) { ++ uint8_t oldindex = ht->hindex; ++ uint32_t oldbits = ht->hashbits[oldindex]; ++ uint8_t newindex = HT_NEXTTABLE(oldindex); + +- ht->size = ((size_t)1<mask = ((size_t)1<count = 0; ++ REQUIRE(ht->hashbits[oldindex] >= HT_MIN_BITS); ++ REQUIRE(ht->hashbits[oldindex] <= HT_MAX_BITS); ++ REQUIRE(ht->table[oldindex] != NULL); ++ ++ REQUIRE(newbits <= HT_MAX_BITS); ++ REQUIRE(ht->hashbits[newindex] == HT_NO_BITS); ++ REQUIRE(ht->table[newindex] == NULL); ++ ++ REQUIRE(newbits > oldbits); ++ ++ hashtable_new(ht, newindex, newbits); ++ ++ ht->hindex = newindex; ++ ++ hashtable_rehash_one(ht); ++} ++ ++static void ++hashtable_rehash_one(isc_ht_t *ht) { ++ isc_ht_node_t **newtable = ht->table[ht->hindex]; ++ uint32_t oldsize = ht->size[HT_NEXTTABLE(ht->hindex)]; ++ isc_ht_node_t **oldtable = ht->table[HT_NEXTTABLE(ht->hindex)]; ++ isc_ht_node_t *node = NULL; ++ isc_ht_node_t *nextnode; ++ ++ /* Find first non-empty node */ ++ while (ht->hiter < oldsize && oldtable[ht->hiter] == NULL) { ++ ht->hiter++; ++ } + +- ht->table = isc_mem_get(ht->mctx, ht->size * sizeof(isc_ht_node_t*)); +- if (ht->table == NULL) { +- isc_mem_putanddetach(&ht->mctx, ht, sizeof(struct isc_ht)); +- return (ISC_R_NOMEMORY); ++ /* Rehashing complete */ ++ if (ht->hiter == oldsize) { ++ hashtable_free(ht, HT_NEXTTABLE(ht->hindex)); ++ ht->hiter = 0; ++ return; + } + +- for (i = 0; i < ht->size; i++) { +- ht->table[i] = NULL; ++ /* Move the first non-empty node from old hashtable to new hashtable */ ++ for (node = oldtable[ht->hiter]; node != NULL; node = nextnode) { ++ uint32_t hash = hash_32(node->hashval, ++ ht->hashbits[ht->hindex]); ++ nextnode = node->next; ++ node->next = newtable[hash]; ++ newtable[hash] = node; + } + ++ oldtable[ht->hiter] = NULL; ++ ++ ht->hiter++; ++} ++ ++static void ++maybe_rehash(isc_ht_t *ht, size_t newcount) { ++ uint32_t newbits = rehash_bits(ht, newcount); ++ ++ if (ht->hashbits[ht->hindex] < newbits && newbits <= HT_MAX_BITS) { ++ hashtable_rehash(ht, newbits); ++ } ++} ++ ++static void ++hashtable_new(isc_ht_t *ht, const uint8_t idx, const uint8_t bits) { ++ size_t size; ++ REQUIRE(ht->hashbits[idx] == HT_NO_BITS); ++ REQUIRE(ht->table[idx] == NULL); ++ REQUIRE(bits >= HT_MIN_BITS); ++ REQUIRE(bits <= HT_MAX_BITS); ++ ++ ht->hashbits[idx] = bits; ++ ht->size[idx] = HASHSIZE(ht->hashbits[idx]); ++ ++ size = ht->size[idx] * sizeof(isc_ht_node_t *); ++ ++ ht->table[idx] = isc_mem_get(ht->mctx, size); ++ INSIST(ht->table[idx] != NULL); ++ memset(ht->table[idx], 0, size); ++} ++ ++static void ++hashtable_free(isc_ht_t *ht, const uint8_t idx) { ++ size_t size = ht->size[idx] * sizeof(isc_ht_node_t *); ++ ++ for (size_t i = 0; i < ht->size[idx]; i++) { ++ isc_ht_node_t *node = ht->table[idx][i]; ++ while (node != NULL) { ++ isc_ht_node_t *next = node->next; ++ ht->count--; ++ isc_mem_put(ht->mctx, node, ++ sizeof(*node) + node->keysize); ++ node = next; ++ } ++ } ++ ++ isc_mem_put(ht->mctx, ht->table[idx], size); ++ ht->hashbits[idx] = HT_NO_BITS; ++ ht->table[idx] = NULL; ++} ++ ++void ++isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, uint8_t bits, ++ unsigned int options) { ++ isc_ht_t *ht = NULL; ++ bool case_sensitive = ((options & ISC_HT_CASE_INSENSITIVE) == 0); ++ ++ REQUIRE(htp != NULL && *htp == NULL); ++ REQUIRE(mctx != NULL); ++ REQUIRE(bits >= 1 && bits <= HT_MAX_BITS); ++ ++ ht = isc_mem_get(mctx, sizeof(*ht)); ++ INSIST(ht != NULL); ++ *ht = (isc_ht_t){ ++ .case_sensitive = case_sensitive, ++ }; ++ ++ isc_mem_attach(mctx, &ht->mctx); ++ ++ hashtable_new(ht, 0, bits); ++ + ht->magic = ISC_HT_MAGIC; + + *htp = ht; +- return (ISC_R_SUCCESS); + } + + void + isc_ht_destroy(isc_ht_t **htp) { + isc_ht_t *ht; +- size_t i; + + REQUIRE(htp != NULL); +- REQUIRE(ISC_HT_VALID((*htp))); ++ REQUIRE(ISC_HT_VALID(*htp)); + + ht = *htp; ++ *htp = NULL; + ht->magic = 0; + +- for (i = 0; i < ht->size; i++) { +- isc_ht_node_t *node = ht->table[i]; +- while (node != NULL) { +- isc_ht_node_t *next = node->next; +- ht->count--; +- isc_mem_put(ht->mctx, node, +- offsetof(isc_ht_node_t, key) + +- node->keysize); +- node = next; ++ for (size_t i = 0; i <= 1; i++) { ++ if (ht->table[i] != NULL) { ++ hashtable_free(ht, i); + } + } + + INSIST(ht->count == 0); + +- isc_mem_put(ht->mctx, ht->table, ht->size * sizeof(isc_ht_node_t*)); +- isc_mem_putanddetach(&ht->mctx, ht, sizeof(struct isc_ht)); +- +- *htp = NULL; ++ isc_mem_putanddetach(&ht->mctx, ht, sizeof(*ht)); + } + +-isc_result_t +-isc_ht_add(isc_ht_t *ht, const unsigned char *key, +- uint32_t keysize, void *value) +-{ ++static void ++isc__ht_add(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize, ++ const uint32_t hashval, const uint8_t idx, void *value) { + isc_ht_node_t *node; + uint32_t hash; + ++ hash = hash_32(hashval, ht->hashbits[idx]); ++ ++ node = isc_mem_get(ht->mctx, sizeof(*node) + keysize); ++ INSIST(node != NULL); ++ *node = (isc_ht_node_t){ ++ .keysize = keysize, ++ .hashval = hashval, ++ .next = ht->table[idx][hash], ++ .value = value, ++ }; ++ ++ memmove(node->key, key, keysize); ++ ++ ht->count++; ++ ht->table[idx][hash] = node; ++} ++ ++isc_result_t ++isc_ht_add(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize, ++ void *value) { ++ uint32_t hashval; ++ + REQUIRE(ISC_HT_VALID(ht)); + REQUIRE(key != NULL && keysize > 0); + +- hash = isc_hash_function(key, keysize, true, NULL); +- node = ht->table[hash & ht->mask]; +- while (node != NULL) { +- if (keysize == node->keysize && +- memcmp(key, node->key, keysize) == 0) { +- return (ISC_R_EXISTS); +- } +- node = node->next; ++ if (rehashing_in_progress(ht)) { ++ /* Rehash in progress */ ++ hashtable_rehash_one(ht); ++ } else if (hashtable_is_overcommited(ht)) { ++ /* Rehash requested */ ++ maybe_rehash(ht, ht->count); + } + +- node = isc_mem_get(ht->mctx, offsetof(isc_ht_node_t, key) + keysize); +- if (node == NULL) +- return (ISC_R_NOMEMORY); ++ hashval = isc_hash_function(key, keysize, ht->case_sensitive, NULL); + +- memmove(node->key, key, keysize); +- node->keysize = keysize; +- node->next = ht->table[hash & ht->mask]; +- node->value = value; ++ if (isc__ht_find(ht, key, keysize, hashval, ht->hindex) != NULL) { ++ return (ISC_R_EXISTS); ++ } ++ ++ isc__ht_add(ht, key, keysize, hashval, ht->hindex, value); + +- ht->count++; +- ht->table[hash & ht->mask] = node; + return (ISC_R_SUCCESS); + } + ++static isc_ht_node_t * ++isc__ht_find(const isc_ht_t *ht, const unsigned char *key, ++ const uint32_t keysize, const uint32_t hashval, ++ const uint8_t idx) { ++ uint32_t hash; ++ uint8_t findex = idx; ++ ++nexttable: ++ hash = hash_32(hashval, ht->hashbits[findex]); ++ for (isc_ht_node_t *node = ht->table[findex][hash]; node != NULL; ++ node = node->next) ++ { ++ if (isc__ht_node_match(node, hashval, key, keysize, ++ ht->case_sensitive)) ++ { ++ return (node); ++ } ++ } ++ if (TRY_NEXTTABLE(findex, ht)) { ++ /* ++ * Rehashing in progress, check the other table ++ */ ++ findex = HT_NEXTTABLE(findex); ++ goto nexttable; ++ } ++ ++ return (NULL); ++} ++ + isc_result_t + isc_ht_find(const isc_ht_t *ht, const unsigned char *key, +- uint32_t keysize, void **valuep) +-{ ++ const uint32_t keysize, void **valuep) { ++ uint32_t hashval; + isc_ht_node_t *node; +- uint32_t hash; + + REQUIRE(ISC_HT_VALID(ht)); + REQUIRE(key != NULL && keysize > 0); + REQUIRE(valuep == NULL || *valuep == NULL); + +- hash = isc_hash_function(key, keysize, true, NULL); +- node = ht->table[hash & ht->mask]; +- while (node != NULL) { +- if (keysize == node->keysize && +- memcmp(key, node->key, keysize) == 0) { +- if (valuep != NULL) { +- *valuep = node->value; +- } +- return (ISC_R_SUCCESS); +- } +- node = node->next; ++ hashval = isc_hash_function(key, keysize, ht->case_sensitive, NULL); ++ ++ node = isc__ht_find(ht, key, keysize, hashval, ht->hindex); ++ if (node == NULL) { ++ return (ISC_R_NOTFOUND); + } + +- return (ISC_R_NOTFOUND); ++ if (valuep != NULL) { ++ *valuep = node->value; ++ } ++ return (ISC_R_SUCCESS); + } + +-isc_result_t +-isc_ht_delete(isc_ht_t *ht, const unsigned char *key, uint32_t keysize) { +- isc_ht_node_t *node, *prev; ++static isc_result_t ++isc__ht_delete(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize, ++ const uint32_t hashval, const uint8_t idx) { ++ isc_ht_node_t *prev = NULL; + uint32_t hash; + +- REQUIRE(ISC_HT_VALID(ht)); +- REQUIRE(key != NULL && keysize > 0); +- +- prev = NULL; +- hash = isc_hash_function(key, keysize, true, NULL); +- node = ht->table[hash & ht->mask]; +- while (node != NULL) { +- if (keysize == node->keysize && +- memcmp(key, node->key, keysize) == 0) { +- if (prev == NULL) +- ht->table[hash & ht->mask] = node->next; +- else ++ hash = hash_32(hashval, ht->hashbits[idx]); ++ ++ for (isc_ht_node_t *node = ht->table[idx][hash]; node != NULL; ++ prev = node, node = node->next) ++ { ++ if (isc__ht_node_match(node, hashval, key, keysize, ++ ht->case_sensitive)) ++ { ++ if (prev == NULL) { ++ ht->table[idx][hash] = node->next; ++ } else { + prev->next = node->next; ++ } + isc_mem_put(ht->mctx, node, +- offsetof(isc_ht_node_t, key) + +- node->keysize); ++ sizeof(*node) + node->keysize); + ht->count--; + + return (ISC_R_SUCCESS); + } +- +- prev = node; +- node = node->next; + } ++ + return (ISC_R_NOTFOUND); + } + + isc_result_t ++isc_ht_delete(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize) { ++ uint32_t hashval; ++ uint8_t hindex; ++ isc_result_t result; ++ ++ REQUIRE(ISC_HT_VALID(ht)); ++ REQUIRE(key != NULL && keysize > 0); ++ ++ if (rehashing_in_progress(ht)) { ++ /* Rehash in progress */ ++ hashtable_rehash_one(ht); ++ } ++ ++ hindex = ht->hindex; ++ hashval = isc_hash_function(key, keysize, ht->case_sensitive, NULL); ++nexttable: ++ result = isc__ht_delete(ht, key, keysize, hashval, hindex); ++ ++ if (result == ISC_R_NOTFOUND && TRY_NEXTTABLE(hindex, ht)) { ++ /* ++ * Rehashing in progress, check the other table ++ */ ++ hindex = HT_NEXTTABLE(hindex); ++ goto nexttable; ++ } ++ ++ return (result); ++} ++ ++void + isc_ht_iter_create(isc_ht_t *ht, isc_ht_iter_t **itp) { + isc_ht_iter_t *it; + +@@ -219,16 +496,13 @@ isc_ht_iter_create(isc_ht_t *ht, isc_ht_iter_t **itp) { + REQUIRE(itp != NULL && *itp == NULL); + + it = isc_mem_get(ht->mctx, sizeof(isc_ht_iter_t)); +- if (it == NULL) +- return (ISC_R_NOMEMORY); +- +- it->ht = ht; +- it->i = 0; +- it->cur = NULL; ++ INSIST(it != NULL); ++ *it = (isc_ht_iter_t){ ++ .ht = ht, ++ .hindex = ht->hindex, ++ }; + + *itp = it; +- +- return (ISC_R_SUCCESS); + } + + void +@@ -239,26 +513,48 @@ isc_ht_iter_destroy(isc_ht_iter_t **itp) { + REQUIRE(itp != NULL && *itp != NULL); + + it = *itp; +- ht = it->ht; +- isc_mem_put(ht->mctx, it, sizeof(isc_ht_iter_t)); +- + *itp = NULL; ++ ht = it->ht; ++ isc_mem_put(ht->mctx, it, sizeof(*it)); + } + + isc_result_t + isc_ht_iter_first(isc_ht_iter_t *it) { ++ isc_ht_t *ht; ++ + REQUIRE(it != NULL); + ++ ht = it->ht; ++ ++ it->hindex = ht->hindex; + it->i = 0; +- while (it->i < it->ht->size && it->ht->table[it->i] == NULL) ++ ++ return (isc__ht_iter_next(it)); ++} ++ ++static isc_result_t ++isc__ht_iter_next(isc_ht_iter_t *it) { ++ isc_ht_t *ht = it->ht; ++ ++ while (it->i < ht->size[it->hindex] && ++ ht->table[it->hindex][it->i] == NULL) ++ { + it->i++; ++ } + +- if (it->i == it->ht->size) +- return (ISC_R_NOMORE); ++ if (it->i < ht->size[it->hindex]) { ++ it->cur = ht->table[it->hindex][it->i]; + +- it->cur = it->ht->table[it->i]; ++ return (ISC_R_SUCCESS); ++ } + +- return (ISC_R_SUCCESS); ++ if (TRY_NEXTTABLE(it->hindex, ht)) { ++ it->hindex = HT_NEXTTABLE(it->hindex); ++ it->i = 0; ++ return (isc__ht_iter_next(it)); ++ } ++ ++ return (ISC_R_NOMORE); + } + + isc_result_t +@@ -267,58 +563,36 @@ isc_ht_iter_next(isc_ht_iter_t *it) { + REQUIRE(it->cur != NULL); + + it->cur = it->cur->next; +- if (it->cur == NULL) { +- do { +- it->i++; +- } while (it->i < it->ht->size && it->ht->table[it->i] == NULL); +- if (it->i >= it->ht->size) +- return (ISC_R_NOMORE); +- it->cur = it->ht->table[it->i]; ++ ++ if (it->cur != NULL) { ++ return (ISC_R_SUCCESS); + } + +- return (ISC_R_SUCCESS); ++ it->i++; ++ ++ return (isc__ht_iter_next(it)); + } + + isc_result_t + isc_ht_iter_delcurrent_next(isc_ht_iter_t *it) { + isc_result_t result = ISC_R_SUCCESS; +- isc_ht_node_t *to_delete = NULL; +- isc_ht_node_t *prev = NULL; +- isc_ht_node_t *node = NULL; +- uint32_t hash; ++ isc_ht_node_t *dnode = NULL; ++ uint8_t dindex; + isc_ht_t *ht; ++ isc_result_t dresult; ++ + REQUIRE(it != NULL); + REQUIRE(it->cur != NULL); +- to_delete = it->cur; +- ht = it->ht; + +- it->cur = it->cur->next; +- if (it->cur == NULL) { +- do { +- it->i++; +- } while (it->i < ht->size && ht->table[it->i] == NULL); +- if (it->i >= ht->size) +- result = ISC_R_NOMORE; +- else +- it->cur = ht->table[it->i]; +- } ++ ht = it->ht; ++ dnode = it->cur; ++ dindex = it->hindex; + +- hash = isc_hash_function(to_delete->key, to_delete->keysize, true, +- NULL); +- node = ht->table[hash & ht->mask]; +- while (node != to_delete) { +- prev = node; +- node = node->next; +- INSIST(node != NULL); +- } ++ result = isc_ht_iter_next(it); + +- if (prev == NULL) +- ht->table[hash & ht->mask] = node->next; +- else +- prev->next = node->next; +- isc_mem_put(ht->mctx, node, +- offsetof(isc_ht_node_t, key) + node->keysize); +- ht->count--; ++ dresult = isc__ht_delete(ht, dnode->key, dnode->keysize, dnode->hashval, ++ dindex); ++ INSIST(dresult == ISC_R_SUCCESS); + + return (result); + } +@@ -333,8 +607,8 @@ isc_ht_iter_current(isc_ht_iter_t *it, void **valuep) { + } + + void +-isc_ht_iter_currentkey(isc_ht_iter_t *it, unsigned char **key, size_t *keysize) +-{ ++isc_ht_iter_currentkey(isc_ht_iter_t *it, unsigned char **key, ++ size_t *keysize) { + REQUIRE(it != NULL); + REQUIRE(it->cur != NULL); + REQUIRE(key != NULL && *key == NULL); +@@ -343,9 +617,9 @@ isc_ht_iter_currentkey(isc_ht_iter_t *it, unsigned char **key, size_t *keysize) + *keysize = it->cur->keysize; + } + +-unsigned int +-isc_ht_count(isc_ht_t *ht) { ++size_t ++isc_ht_count(const isc_ht_t *ht) { + REQUIRE(ISC_HT_VALID(ht)); + +- return(ht->count); ++ return (ht->count); + } +diff --git a/lib/isc/include/isc/ht.h b/lib/isc/include/isc/ht.h +index a1315b0c5a..163fbefb79 100644 +--- a/lib/isc/include/isc/ht.h ++++ b/lib/isc/include/isc/ht.h +@@ -1,6 +1,8 @@ + /* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * ++ * SPDX-License-Identifier: MPL-2.0 ++ * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. +@@ -11,32 +13,35 @@ + + /* ! \file */ + +-#ifndef ISC_HT_H +-#define ISC_HT_H 1 ++#pragma once + + #include + #include + +-#include + #include ++#include + +-typedef struct isc_ht isc_ht_t; ++typedef struct isc_ht isc_ht_t; + typedef struct isc_ht_iter isc_ht_iter_t; + ++enum { ISC_HT_CASE_SENSITIVE = 0x00, ISC_HT_CASE_INSENSITIVE = 0x01 }; ++ + /*% + * Initialize hashtable at *htp, using memory context and size of (1<=1 and 'bits' <=32 + * +- * Returns: +- *\li #ISC_R_NOMEMORY -- not enough memory to create pool +- *\li #ISC_R_SUCCESS -- all is well. + */ +-isc_result_t +-isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, uint8_t bits); ++void ++isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, uint8_t bits, ++ unsigned int options); + + /*% + * Destroy hashtable, freeing everything +@@ -53,6 +58,7 @@ isc_ht_destroy(isc_ht_t **htp); + * + * Requires: + *\li 'ht' is a valid hashtable ++ *\li write-lock + * + * Returns: + *\li #ISC_R_NOMEMORY -- not enough memory to create pool +@@ -60,15 +66,18 @@ isc_ht_destroy(isc_ht_t **htp); + *\li #ISC_R_SUCCESS -- all is well. + */ + isc_result_t +-isc_ht_add(isc_ht_t *ht, const unsigned char *key, uint32_t keysize, +- void *value); ++isc_ht_add(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize, ++ void *value); + + /*% + * Find a node matching 'key'/'keysize' in hashtable 'ht'; +- * if found, set 'value' to its value ++ * if found, set '*valuep' to its value. (If 'valuep' is NULL, ++ * then simply return SUCCESS or NOTFOUND to indicate whether the ++ * key exists in the hashtable.) + * + * Requires: + * \li 'ht' is a valid hashtable ++ * \li read-lock + * + * Returns: + * \li #ISC_R_SUCCESS -- success +@@ -76,20 +85,21 @@ isc_ht_add(isc_ht_t *ht, const unsigned char *key, uint32_t keysize, + */ + isc_result_t + isc_ht_find(const isc_ht_t *ht, const unsigned char *key, +- uint32_t keysize, void **valuep); ++ const uint32_t keysize, void **valuep); + + /*% + * Delete node from hashtable + * + * Requires: + *\li ht is a valid hashtable ++ *\li write-lock + * + * Returns: + *\li #ISC_R_NOTFOUND -- key not found + *\li #ISC_R_SUCCESS -- all is well + */ + isc_result_t +-isc_ht_delete(isc_ht_t *ht, const unsigned char *key, uint32_t keysize); ++isc_ht_delete(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize); + + /*% + * Create an iterator for the hashtable; point '*itp' to it. +@@ -98,7 +108,7 @@ isc_ht_delete(isc_ht_t *ht, const unsigned char *key, uint32_t keysize); + *\li 'ht' is a valid hashtable + *\li 'itp' is non NULL and '*itp' is NULL. + */ +-isc_result_t ++void + isc_ht_iter_create(isc_ht_t *ht, isc_ht_iter_t **itp); + + /*% +@@ -117,7 +127,7 @@ isc_ht_iter_destroy(isc_ht_iter_t **itp); + *\li 'it' is non NULL. + * + * Returns: +- * \li #ISC_R_SUCCESS -- success ++ * \li #ISC_R_SUCCESS -- success + * \li #ISC_R_NOMORE -- no data in the hashtable + */ + isc_result_t +@@ -130,7 +140,7 @@ isc_ht_iter_first(isc_ht_iter_t *it); + *\li 'it' is non NULL. + * + * Returns: +- * \li #ISC_R_SUCCESS -- success ++ * \li #ISC_R_SUCCESS -- success + * \li #ISC_R_NOMORE -- end of hashtable reached + */ + isc_result_t +@@ -143,13 +153,12 @@ isc_ht_iter_next(isc_ht_iter_t *it); + *\li 'it' is non NULL. + * + * Returns: +- * \li #ISC_R_SUCCESS -- success ++ * \li #ISC_R_SUCCESS -- success + * \li #ISC_R_NOMORE -- end of hashtable reached + */ + isc_result_t + isc_ht_iter_delcurrent_next(isc_ht_iter_t *it); + +- + /*% + * Set 'value' to the current value under the iterator + * +@@ -178,6 +187,5 @@ isc_ht_iter_currentkey(isc_ht_iter_t *it, unsigned char **key, size_t *keysize); + * Requires: + *\li 'ht' is a valid hashtable + */ +-unsigned int +-isc_ht_count(isc_ht_t *ht); +-#endif ++size_t ++isc_ht_count(const isc_ht_t *ht); +diff --git a/lib/isc/tests/ht_test.c b/lib/isc/tests/ht_test.c +index 79e4260c2b..144d859639 100644 +--- a/lib/isc/tests/ht_test.c ++++ b/lib/isc/tests/ht_test.c +@@ -60,8 +60,7 @@ test_ht_full(int bits, uintptr_t count) { + NULL, &mctx, 0); + assert_int_equal(result, ISC_R_SUCCESS); + +- result = isc_ht_init(&ht, mctx, bits); +- assert_int_equal(result, ISC_R_SUCCESS); ++ isc_ht_init(&ht, mctx, bits, ISC_HT_CASE_SENSITIVE); + assert_non_null(ht); + + for (i = 1; i < count; i++) { +@@ -214,8 +213,7 @@ test_ht_iterator() { + NULL, &mctx, 0); + assert_int_equal(result, ISC_R_SUCCESS); + +- result = isc_ht_init(&ht, mctx, 16); +- assert_int_equal(result, ISC_R_SUCCESS); ++ isc_ht_init(&ht, mctx, 16, ISC_HT_CASE_SENSITIVE); + assert_non_null(ht); + for (i = 1; i <= count; i++) { + /* +@@ -229,8 +227,7 @@ test_ht_iterator() { + } + + walked = 0; +- result = isc_ht_iter_create(ht, &iter); +- assert_int_equal(result, ISC_R_SUCCESS); ++ isc_ht_iter_create(ht, &iter); + + for (result = isc_ht_iter_first(iter); + result == ISC_R_SUCCESS; +@@ -348,9 +345,69 @@ isc_ht_iterator_test(void **state) { + test_ht_iterator(); + } + ++static void ++isc_ht_case(void **state) { ++ UNUSED(state); ++ ++ isc_ht_t *ht = NULL; ++ void *f = NULL; ++ isc_result_t result = ISC_R_UNSET; ++ isc_mem_t *mctx = NULL; ++ ++ result = isc_mem_createx2(0, 0, default_memalloc, default_memfree, ++ NULL, &mctx, 0); ++ assert_int_equal(result, ISC_R_SUCCESS); ++ ++ unsigned char lower[16] = { "test case" }; ++ unsigned char same[16] = { "test case" }; ++ unsigned char upper[16] = { "TEST CASE" }; ++ unsigned char mixed[16] = { "tEsT CaSe" }; ++ ++ isc_ht_init(&ht, mctx, 8, ISC_HT_CASE_SENSITIVE); ++ assert_non_null(ht); ++ ++ result = isc_ht_add(ht, lower, 16, (void *)lower); ++ assert_int_equal(result, ISC_R_SUCCESS); ++ ++ result = isc_ht_add(ht, same, 16, (void *)same); ++ assert_int_equal(result, ISC_R_EXISTS); ++ ++ result = isc_ht_add(ht, upper, 16, (void *)upper); ++ assert_int_equal(result, ISC_R_SUCCESS); ++ ++ result = isc_ht_find(ht, mixed, 16, &f); ++ assert_int_equal(result, ISC_R_NOTFOUND); ++ assert_null(f); ++ ++ isc_ht_destroy(&ht); ++ assert_null(ht); ++ ++ isc_ht_init(&ht, mctx, 8, ISC_HT_CASE_INSENSITIVE); ++ assert_non_null(ht); ++ ++ result = isc_ht_add(ht, lower, 16, (void *)lower); ++ assert_int_equal(result, ISC_R_SUCCESS); ++ ++ result = isc_ht_add(ht, same, 16, (void *)same); ++ assert_int_equal(result, ISC_R_EXISTS); ++ ++ result = isc_ht_add(ht, upper, 16, (void *)upper); ++ assert_int_equal(result, ISC_R_EXISTS); ++ ++ result = isc_ht_find(ht, mixed, 16, &f); ++ assert_int_equal(result, ISC_R_SUCCESS); ++ assert_ptr_equal(f, &lower); ++ ++ isc_ht_destroy(&ht); ++ assert_null(ht); ++ ++ isc_mem_detach(&mctx); ++} ++ + int + main(void) { + const struct CMUnitTest tests[] = { ++ cmocka_unit_test(isc_ht_case), + cmocka_unit_test(isc_ht_20), + cmocka_unit_test(isc_ht_8), + cmocka_unit_test(isc_ht_1), +-- +2.43.2 + diff --git a/SOURCES/bind-9.11-CVE-2023-50387-fixup.patch b/SOURCES/bind-9.11-CVE-2023-50387-fixup.patch new file mode 100644 index 0000000..42b8287 --- /dev/null +++ b/SOURCES/bind-9.11-CVE-2023-50387-fixup.patch @@ -0,0 +1,64 @@ +From f0fc9d7999a94da3d471c4e0a35b1f447f25eea6 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Petr=20Men=C5=A1=C3=ADk?= +Date: Mon, 26 Feb 2024 21:08:42 +0100 +Subject: [PATCH] Add normal task queue also to non-thread version + +Non-thread builds are used by us for dhcp package. Make it working +again. + +Related to [GL #4424] and [GL #4459]. +--- + lib/isc/task.c | 14 ++++++++------ + 1 file changed, 8 insertions(+), 6 deletions(-) + +diff --git a/lib/isc/task.c b/lib/isc/task.c +index cc83269..5315b51 100644 +--- a/lib/isc/task.c ++++ b/lib/isc/task.c +@@ -1115,7 +1115,7 @@ dispatch(isc__taskmgr_t *manager, isc_taskqueue_t qid) { + } + #else /* USE_WORKER_THREADS */ + if (total_dispatch_count >= DEFAULT_TASKMGR_QUANTUM || +- empty_readyq(manager)) ++ empty_readyq(manager, qid)) + break; + #endif /* USE_WORKER_THREADS */ + XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TASK, +@@ -1318,11 +1318,11 @@ dispatch(isc__taskmgr_t *manager, isc_taskqueue_t qid) { + } + + #ifndef USE_WORKER_THREADS +- ISC_LIST_APPENDLIST(manager->ready_tasks, new_ready_tasks, ready_link); +- ISC_LIST_APPENDLIST(manager->ready_priority_tasks, new_priority_tasks, ++ ISC_LIST_APPENDLIST(manager->ready_tasks[qid], new_ready_tasks, ready_link); ++ ISC_LIST_APPENDLIST(manager->ready_priority_tasks[qid], new_priority_tasks, + ready_priority_link); + manager->tasks_ready += tasks_ready; +- if (empty_readyq(manager)) ++ if (empty_readyq(manager, qid)) + manager->mode = isc_taskmgrmode_normal; + #endif + +@@ -1713,7 +1713,8 @@ isc__taskmgr_ready(isc_taskmgr_t *manager0) { + return (false); + + LOCK(&manager->lock); +- is_ready = !empty_readyq(manager); ++ is_ready = !empty_readyq(manager, isc_taskqueue_normal) || ++ !empty_readyq(manager, isc_taskqueue_slow); + UNLOCK(&manager->lock); + + return (is_ready); +@@ -1730,7 +1731,8 @@ isc__taskmgr_dispatch(isc_taskmgr_t *manager0) { + if (manager == NULL) + return (ISC_R_NOTFOUND); + +- dispatch(manager); ++ dispatch(manager, isc_taskqueue_normal); ++ dispatch(manager, isc_taskqueue_slow); + + return (ISC_R_SUCCESS); + } +-- +2.43.2 + diff --git a/SOURCES/bind-9.11-CVE-2023-50387.patch b/SOURCES/bind-9.11-CVE-2023-50387.patch new file mode 100644 index 0000000..2f90d65 --- /dev/null +++ b/SOURCES/bind-9.11-CVE-2023-50387.patch @@ -0,0 +1,737 @@ +From 4c20ab54ec503f65d8ee0b863cbf41103d95130a Mon Sep 17 00:00:00 2001 +From: Mark Andrews +Date: Wed, 22 Nov 2023 16:59:03 +1100 +Subject: [PATCH] Fail the DNSSEC validation on the first failure + +Be more strict when encountering DNSSEC validation failures - fail on +the first failure. This will break domains that have DNSSEC signing +keys with duplicate key ids, but this is something that's much easier +to fix on the authoritative side, so we are just going to be strict +on the resolver side where it is causing performance problems. + +(cherry picked from commit 8b7ecba9885e163c07c2dd3e1ceab79b2ba89e34) + +Add normal and slow task queues + +Split the task manager queues into normal and slow task queues, so we +can move the tasks that blocks processing for a long time (like DNSSEC +validation) into the slow queue which doesn't block fast +operations (like responding from the cache). This mitigates the whole +class of KeyTrap-like issues. + +(cherry picked from commit db083a21726300916fa0b9fd8a433a796fedf636) + +Don't iterate from start every time we select new signing key + +Improve the selecting of the new signing key by remembering where +we stopped the iteration and just continue from that place instead +of iterating from the start over and over again each time. + +(cherry picked from commit 75faeefcab47e4f1e12b358525190b4be90f97de) + +Optimize selecting the signing key + +Don't parse the crypto data before parsing and matching the id and the +algorithm. + +(cherry picked from commit b38552cca7200a72658e482f8407f57516efc5db) + +6322. [security] Specific DNS answers could cause a denial-of-service + condition due to DNS validation taking a long time. + (CVE-2023-50387) [GL #4424] + + The same code change also addresses another problem: + preparing NSEC3 closest encloser proofs could exhaust + available CPU resources. (CVE-2023-50868) [GL #4459] +--- + lib/dns/dst_api.c | 25 ++++-- + lib/dns/include/dns/validator.h | 1 + + lib/dns/include/dst/dst.h | 4 + + lib/dns/resolver.c | 2 +- + lib/dns/validator.c | 97 +++++++++----------- + lib/dns/win32/libdns.def.in | 1 + + lib/isc/include/isc/task.h | 11 ++- + lib/isc/task.c | 153 ++++++++++++++++++++++---------- + 8 files changed, 186 insertions(+), 108 deletions(-) + +diff --git a/lib/dns/dst_api.c b/lib/dns/dst_api.c +index 2156384ec1..6bcd99796c 100644 +--- a/lib/dns/dst_api.c ++++ b/lib/dns/dst_api.c +@@ -105,6 +105,7 @@ static isc_result_t frombuffer(dns_name_t *name, + dns_rdataclass_t rdclass, + isc_buffer_t *source, + isc_mem_t *mctx, ++ bool no_rdata, + dst_key_t **keyp); + + static isc_result_t algorithm_status(unsigned int alg); +@@ -764,6 +765,13 @@ isc_result_t + dst_key_fromdns(dns_name_t *name, dns_rdataclass_t rdclass, + isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp) + { ++ return (dst_key_fromdns_ex(name, rdclass, source, mctx, false, keyp)); ++} ++ ++isc_result_t ++dst_key_fromdns_ex(dns_name_t *name, dns_rdataclass_t rdclass, ++ isc_buffer_t *source, isc_mem_t *mctx, bool no_rdata, ++ dst_key_t **keyp) { + uint8_t alg, proto; + uint32_t flags, extflags; + dst_key_t *key = NULL; +@@ -792,7 +800,7 @@ dst_key_fromdns(dns_name_t *name, dns_rdataclass_t rdclass, + } + + result = frombuffer(name, alg, flags, proto, rdclass, source, +- mctx, &key); ++ mctx, no_rdata, &key); + if (result != ISC_R_SUCCESS) + return (result); + key->key_id = id; +@@ -814,7 +822,7 @@ dst_key_frombuffer(dns_name_t *name, unsigned int alg, + REQUIRE(dst_initialized); + + result = frombuffer(name, alg, flags, protocol, rdclass, source, +- mctx, &key); ++ mctx, false, &key); + if (result != ISC_R_SUCCESS) + return (result); + +@@ -1915,7 +1923,8 @@ computeid(dst_key_t *key) { + static isc_result_t + frombuffer(dns_name_t *name, unsigned int alg, unsigned int flags, + unsigned int protocol, dns_rdataclass_t rdclass, +- isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp) ++ isc_buffer_t *source, isc_mem_t *mctx, bool no_rdata, ++ dst_key_t **keyp) + { + dst_key_t *key; + isc_result_t ret; +@@ -1940,10 +1949,12 @@ frombuffer(dns_name_t *name, unsigned int alg, unsigned int flags, + return (DST_R_UNSUPPORTEDALG); + } + +- ret = key->func->fromdns(key, source); +- if (ret != ISC_R_SUCCESS) { +- dst_key_free(&key); +- return (ret); ++ if (!no_rdata) { ++ ret = key->func->fromdns(key, source); ++ if (ret != ISC_R_SUCCESS) { ++ dst_key_free(&key); ++ return (ret); ++ } + } + } + +diff --git a/lib/dns/include/dns/validator.h b/lib/dns/include/dns/validator.h +index cc4478d6d4..b4bf8f29db 100644 +--- a/lib/dns/include/dns/validator.h ++++ b/lib/dns/include/dns/validator.h +@@ -160,6 +160,7 @@ struct dns_validator { + unsigned int depth; + unsigned int authcount; + unsigned int authfail; ++ bool failed; + isc_stdtime_t start; + }; + +diff --git a/lib/dns/include/dst/dst.h b/lib/dns/include/dst/dst.h +index 180c841307..a8be2daf67 100644 +--- a/lib/dns/include/dst/dst.h ++++ b/lib/dns/include/dst/dst.h +@@ -435,6 +435,10 @@ dst_key_tofile(const dst_key_t *key, int type, const char *directory); + */ + + isc_result_t ++dst_key_fromdns_ex(dns_name_t *name, dns_rdataclass_t rdclass, ++ isc_buffer_t *source, isc_mem_t *mctx, bool no_rdata, ++ dst_key_t **keyp); ++isc_result_t + dst_key_fromdns(dns_name_t *name, dns_rdataclass_t rdclass, + isc_buffer_t *source, isc_mem_t *mctx, dst_key_t **keyp); + /*%< +diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c +index 4f71f48039..487107614c 100644 +--- a/lib/dns/resolver.c ++++ b/lib/dns/resolver.c +@@ -9267,7 +9267,7 @@ dns_resolver_create(dns_view_t *view, + if (result != ISC_R_SUCCESS) + goto cleanup_buckets; + res->buckets[i].task = NULL; +- result = isc_task_create(taskmgr, 0, &res->buckets[i].task); ++ result = isc_task_create(taskmgr, ISC_TASK_QUANTUM_SLOW, &res->buckets[i].task); + if (result != ISC_R_SUCCESS) { + DESTROYLOCK(&res->buckets[i].lock); + goto cleanup_buckets; +diff --git a/lib/dns/validator.c b/lib/dns/validator.c +index 2a5c3caa6a..0b257fe874 100644 +--- a/lib/dns/validator.c ++++ b/lib/dns/validator.c +@@ -1207,6 +1207,12 @@ create_validator(dns_validator_t *val, dns_name_t *name, dns_rdatatype_t type, + * val->key at it. + * + * If val->key is non-NULL, this returns the next matching key. ++ * If val->key is already non-NULL, start searching from the next position in ++ * 'rdataset' to find the *next* key that could have signed 'siginfo', then ++ * set val->key to that. ++ * ++ * Returns ISC_R_SUCCESS if a possible matching key has been found, ++ * ISC_R_NOTFOUND if not. Any other value indicates error. + */ + static isc_result_t + get_dst_key(dns_validator_t *val, dns_rdata_rrsig_t *siginfo, +@@ -1216,54 +1222,59 @@ get_dst_key(dns_validator_t *val, dns_rdata_rrsig_t *siginfo, + isc_buffer_t b; + dns_rdata_t rdata = DNS_RDATA_INIT; + dst_key_t *oldkey = val->key; +- bool foundold; ++ bool no_rdata = false; + +- if (oldkey == NULL) +- foundold = true; +- else { +- foundold = false; ++ if (oldkey == NULL) { ++ result = dns_rdataset_first(rdataset); ++ } else { ++ dst_key_free(&oldkey); + val->key = NULL; ++ result = dns_rdataset_next(rdataset); ++ } ++ ++ if (result != ISC_R_SUCCESS) { ++ goto done; + } + +- result = dns_rdataset_first(rdataset); +- if (result != ISC_R_SUCCESS) +- goto failure; + do { + dns_rdataset_current(rdataset, &rdata); + + isc_buffer_init(&b, rdata.data, rdata.length); + isc_buffer_add(&b, rdata.length); + INSIST(val->key == NULL); +- result = dst_key_fromdns(&siginfo->signer, rdata.rdclass, &b, +- val->view->mctx, &val->key); ++ result = dst_key_fromdns_ex(&siginfo->signer, rdata.rdclass, &b, ++ val->view->mctx, no_rdata, ++ &val->key); + if (result == ISC_R_SUCCESS) { + if (siginfo->algorithm == + (dns_secalg_t)dst_key_alg(val->key) && + siginfo->keyid == + (dns_keytag_t)dst_key_id(val->key) && ++ (dst_key_flags(val->key) & DNS_KEYFLAG_REVOKE) == ++ 0 && + dst_key_iszonekey(val->key)) + { +- if (foundold) { +- /* +- * This is the key we're looking for. +- */ +- return (ISC_R_SUCCESS); +- } else if (dst_key_compare(oldkey, val->key)) { +- foundold = true; +- dst_key_free(&oldkey); ++ if (no_rdata) { ++ /* Retry with full key */ ++ dns_rdata_reset(&rdata); ++ dst_key_free(&val->key); ++ no_rdata = false; ++ continue; + } ++ /* This is the key we're looking for. */ ++ goto done; + } + dst_key_free(&val->key); + } + dns_rdata_reset(&rdata); + result = dns_rdataset_next(rdataset); ++ no_rdata = true; + } while (result == ISC_R_SUCCESS); +- if (result == ISC_R_NOMORE) +- result = ISC_R_NOTFOUND; + +- failure: +- if (oldkey != NULL) +- dst_key_free(&oldkey); ++done: ++ if (result == ISC_R_NOMORE) { ++ result = ISC_R_NOTFOUND; ++ } + + return (result); + } +@@ -1633,37 +1644,13 @@ validate(dns_validator_t *val, bool resume) { + continue; + } + +- do { +- vresult = verify(val, val->key, &rdata, +- val->siginfo->keyid); +- if (vresult == ISC_R_SUCCESS) +- break; +- if (val->keynode != NULL) { +- dns_keynode_t *nextnode = NULL; +- result = dns_keytable_findnextkeynode( +- val->keytable, +- val->keynode, +- &nextnode); +- dns_keytable_detachkeynode(val->keytable, +- &val->keynode); +- val->keynode = nextnode; +- if (result != ISC_R_SUCCESS) { +- val->key = NULL; +- break; +- } +- val->key = dns_keynode_key(val->keynode); +- if (val->key == NULL) +- break; +- } else { +- if (get_dst_key(val, val->siginfo, val->keyset) +- != ISC_R_SUCCESS) +- break; +- } +- } while (1); +- if (vresult != ISC_R_SUCCESS) ++ vresult = verify(val, val->key, &rdata, ++ val->siginfo->keyid); ++ if (vresult != ISC_R_SUCCESS) { ++ val->failed = true; + validator_log(val, ISC_LOG_DEBUG(3), + "failed to verify rdataset"); +- else { ++ } else { + dns_rdataset_trimttl(event->rdataset, + event->sigrdataset, + val->siginfo, val->start, +@@ -1700,9 +1687,13 @@ validate(dns_validator_t *val, bool resume) { + } else { + validator_log(val, ISC_LOG_DEBUG(3), + "verify failure: %s", +- isc_result_totext(result)); ++ isc_result_totext(vresult)); + resume = false; + } ++ if (val->failed) { ++ result = ISC_R_NOMORE; ++ break; ++ } + } + if (result != ISC_R_NOMORE) { + validator_log(val, ISC_LOG_DEBUG(3), +diff --git a/lib/dns/win32/libdns.def.in b/lib/dns/win32/libdns.def.in +index f597049493..7320653439 100644 +--- a/lib/dns/win32/libdns.def.in ++++ b/lib/dns/win32/libdns.def.in +@@ -1439,6 +1439,7 @@ dst_key_format + dst_key_free + dst_key_frombuffer + dst_key_fromdns ++dst_key_fromdns_ex + dst_key_fromfile + dst_key_fromgssapi + dst_key_fromlabel +diff --git a/lib/isc/include/isc/task.h b/lib/isc/include/isc/task.h +index 28e5e25fc6..42f7763869 100644 +--- a/lib/isc/include/isc/task.h ++++ b/lib/isc/include/isc/task.h +@@ -98,8 +98,15 @@ ISC_LANG_BEGINDECLS + ***/ + + typedef enum { +- isc_taskmgrmode_normal = 0, +- isc_taskmgrmode_privileged ++ isc_taskqueue_normal = 0, ++ isc_taskqueue_slow = 1, ++} isc_taskqueue_t; ++ ++#define ISC_TASK_QUANTUM_SLOW 1024 ++ ++typedef enum { ++ isc_taskmgrmode_normal = 0, ++ isc_taskmgrmode_privileged + } isc_taskmgrmode_t; + + /*% Task and task manager methods */ +diff --git a/lib/isc/task.c b/lib/isc/task.c +index 048639350b..cc83269df2 100644 +--- a/lib/isc/task.c ++++ b/lib/isc/task.c +@@ -107,6 +107,7 @@ struct isc__task { + isc_eventlist_t on_shutdown; + unsigned int nevents; + unsigned int quantum; ++ unsigned int qid; + unsigned int flags; + isc_stdtime_t now; + isc_time_t tnow; +@@ -141,11 +142,11 @@ struct isc__taskmgr { + /* Locked by task manager lock. */ + unsigned int default_quantum; + LIST(isc__task_t) tasks; +- isc__tasklist_t ready_tasks; +- isc__tasklist_t ready_priority_tasks; ++ isc__tasklist_t ready_tasks[2]; ++ isc__tasklist_t ready_priority_tasks[2]; + isc_taskmgrmode_t mode; + #ifdef ISC_PLATFORM_USETHREADS +- isc_condition_t work_available; ++ isc_condition_t work_available[2]; + isc_condition_t exclusive_granted; + isc_condition_t paused; + #endif /* ISC_PLATFORM_USETHREADS */ +@@ -247,13 +248,13 @@ isc_taskmgrmode_t + isc__taskmgr_mode(isc_taskmgr_t *manager0); + + static inline bool +-empty_readyq(isc__taskmgr_t *manager); ++empty_readyq(isc__taskmgr_t *manager, isc_taskqueue_t qid); + + static inline isc__task_t * +-pop_readyq(isc__taskmgr_t *manager); ++pop_readyq(isc__taskmgr_t *manager, isc_taskqueue_t qid); + + static inline void +-push_readyq(isc__taskmgr_t *manager, isc__task_t *task); ++push_readyq(isc__taskmgr_t *manager, isc__task_t *task, isc_taskqueue_t qid); + + static struct isc__taskmethods { + isc_taskmethods_t methods; +@@ -324,7 +325,8 @@ task_finished(isc__task_t *task) { + * any idle worker threads so they + * can exit. + */ +- BROADCAST(&manager->work_available); ++ BROADCAST(&manager->work_available[isc_taskqueue_normal]); ++ BROADCAST(&manager->work_available[isc_taskqueue_slow]); + } + #endif /* USE_WORKER_THREADS */ + UNLOCK(&manager->lock); +@@ -364,7 +366,13 @@ isc__task_create(isc_taskmgr_t *manager0, unsigned int quantum, + INIT_LIST(task->events); + INIT_LIST(task->on_shutdown); + task->nevents = 0; +- task->quantum = quantum; ++ if (quantum >= ISC_TASK_QUANTUM_SLOW) { ++ task->qid = isc_taskqueue_slow; ++ task->quantum = quantum - ISC_TASK_QUANTUM_SLOW; ++ } else { ++ task->qid = isc_taskqueue_normal; ++ task->quantum = quantum; ++ } + task->flags = 0; + task->now = 0; + isc_time_settoepoch(&task->tnow); +@@ -476,11 +484,11 @@ task_ready(isc__task_t *task) { + + LOCK(&manager->lock); + LOCK(&task->lock); +- push_readyq(manager, task); ++ push_readyq(manager, task, task->qid); + UNLOCK(&task->lock); + #ifdef USE_WORKER_THREADS + if (manager->mode == isc_taskmgrmode_normal || has_privilege) +- SIGNAL(&manager->work_available); ++ SIGNAL(&manager->work_available[task->qid]); + #endif /* USE_WORKER_THREADS */ + UNLOCK(&manager->lock); + } +@@ -961,13 +969,13 @@ isc__task_getcurrenttimex(isc_task_t *task0, isc_time_t *t) { + * Caller must hold the task manager lock. + */ + static inline bool +-empty_readyq(isc__taskmgr_t *manager) { ++empty_readyq(isc__taskmgr_t *manager, isc_taskqueue_t qid) { + isc__tasklist_t queue; + + if (manager->mode == isc_taskmgrmode_normal) +- queue = manager->ready_tasks; ++ queue = manager->ready_tasks[qid]; + else +- queue = manager->ready_priority_tasks; ++ queue = manager->ready_priority_tasks[qid]; + + return (EMPTY(queue)); + } +@@ -981,18 +989,18 @@ empty_readyq(isc__taskmgr_t *manager) { + * Caller must hold the task manager lock. + */ + static inline isc__task_t * +-pop_readyq(isc__taskmgr_t *manager) { ++pop_readyq(isc__taskmgr_t *manager, isc_taskqueue_t qid) { + isc__task_t *task; + + if (manager->mode == isc_taskmgrmode_normal) +- task = HEAD(manager->ready_tasks); ++ task = HEAD(manager->ready_tasks[qid]); + else +- task = HEAD(manager->ready_priority_tasks); ++ task = HEAD(manager->ready_priority_tasks[qid]); + + if (task != NULL) { +- DEQUEUE(manager->ready_tasks, task, ready_link); ++ DEQUEUE(manager->ready_tasks[qid], task, ready_link); + if (ISC_LINK_LINKED(task, ready_priority_link)) +- DEQUEUE(manager->ready_priority_tasks, task, ++ DEQUEUE(manager->ready_priority_tasks[qid], task, + ready_priority_link); + } + +@@ -1006,16 +1014,16 @@ pop_readyq(isc__taskmgr_t *manager) { + * Caller must hold the task manager lock. + */ + static inline void +-push_readyq(isc__taskmgr_t *manager, isc__task_t *task) { +- ENQUEUE(manager->ready_tasks, task, ready_link); ++push_readyq(isc__taskmgr_t *manager, isc__task_t *task, isc_taskqueue_t qid) { ++ ENQUEUE(manager->ready_tasks[qid], task, ready_link); + if ((task->flags & TASK_F_PRIVILEGED) != 0) +- ENQUEUE(manager->ready_priority_tasks, task, ++ ENQUEUE(manager->ready_priority_tasks[qid], task, + ready_priority_link); + manager->tasks_ready++; + } + + static void +-dispatch(isc__taskmgr_t *manager) { ++dispatch(isc__taskmgr_t *manager, isc_taskqueue_t qid) { + isc__task_t *task; + #ifndef USE_WORKER_THREADS + unsigned int total_dispatch_count = 0; +@@ -1094,13 +1102,13 @@ dispatch(isc__taskmgr_t *manager) { + * If a pause has been requested, don't do any work + * until it's been released. + */ +- while ((empty_readyq(manager) || manager->pause_requested || ++ while ((empty_readyq(manager, qid) || manager->pause_requested || + manager->exclusive_requested) && !FINISHED(manager)) + { + XTHREADTRACE(isc_msgcat_get(isc_msgcat, + ISC_MSGSET_GENERAL, + ISC_MSG_WAIT, "wait")); +- WAIT(&manager->work_available, &manager->lock); ++ WAIT(&manager->work_available[qid], &manager->lock); + XTHREADTRACE(isc_msgcat_get(isc_msgcat, + ISC_MSGSET_TASK, + ISC_MSG_AWAKE, "awake")); +@@ -1113,7 +1121,7 @@ dispatch(isc__taskmgr_t *manager) { + XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_TASK, + ISC_MSG_WORKING, "working")); + +- task = pop_readyq(manager); ++ task = pop_readyq(manager, qid); + if (task != NULL) { + unsigned int dispatch_count = 0; + bool done = false; +@@ -1278,7 +1286,7 @@ dispatch(isc__taskmgr_t *manager) { + */ + #ifdef USE_WORKER_THREADS + LOCK(&task->lock); +- push_readyq(manager, task); ++ push_readyq(manager, task, qid); + UNLOCK(&task->lock); + #else + ENQUEUE(new_ready_tasks, task, ready_link); +@@ -1297,10 +1305,14 @@ dispatch(isc__taskmgr_t *manager) { + * we're stuck. Automatically drop privileges at that + * point and continue with the regular ready queue. + */ +- if (manager->tasks_running == 0 && empty_readyq(manager)) { ++ if (manager->tasks_running == 0 && empty_readyq(manager, isc_taskqueue_normal) && empty_readyq(manager, isc_taskqueue_slow)) { + manager->mode = isc_taskmgrmode_normal; +- if (!empty_readyq(manager)) +- BROADCAST(&manager->work_available); ++ if (!empty_readyq(manager, isc_taskqueue_normal)) { ++ BROADCAST(&manager->work_available[isc_taskqueue_normal]); ++ } ++ if (!empty_readyq(manager, isc_taskqueue_slow)) { ++ BROADCAST(&manager->work_available[isc_taskqueue_slow]); ++ } + } + #endif + } +@@ -1322,13 +1334,37 @@ static isc_threadresult_t + #ifdef _WIN32 + WINAPI + #endif +-run(void *uap) { ++run_normal(void *uap) { + isc__taskmgr_t *manager = uap; + + XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_STARTING, "starting")); + +- dispatch(manager); ++ dispatch(manager, isc_taskqueue_normal); ++ ++ XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, ++ ISC_MSG_EXITING, "exiting")); ++ ++#ifdef OPENSSL_LEAKS ++ ERR_remove_state(0); ++#endif ++ ++ return ((isc_threadresult_t)0); ++} ++#endif /* USE_WORKER_THREADS */ ++ ++#ifdef USE_WORKER_THREADS ++static isc_threadresult_t ++#ifdef _WIN32 ++WINAPI ++#endif ++run_slow(void *uap) { ++ isc__taskmgr_t *manager = uap; ++ ++ XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, ++ ISC_MSG_STARTING, "starting")); ++ ++ dispatch(manager, isc_taskqueue_slow); + + XTHREADTRACE(isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, + ISC_MSG_EXITING, "exiting")); +@@ -1347,7 +1383,8 @@ manager_free(isc__taskmgr_t *manager) { + + #ifdef USE_WORKER_THREADS + (void)isc_condition_destroy(&manager->exclusive_granted); +- (void)isc_condition_destroy(&manager->work_available); ++ (void)isc_condition_destroy(&manager->work_available[isc_taskqueue_normal]); ++ (void)isc_condition_destroy(&manager->work_available[isc_taskqueue_slow]); + (void)isc_condition_destroy(&manager->paused); + isc_mem_free(manager->mctx, manager->threads); + #endif /* USE_WORKER_THREADS */ +@@ -1414,12 +1451,20 @@ isc__taskmgr_create(isc_mem_t *mctx, unsigned int workers, + #ifdef USE_WORKER_THREADS + manager->workers = 0; + manager->threads = isc_mem_allocate(mctx, +- workers * sizeof(isc_thread_t)); ++ 2 * workers * sizeof(isc_thread_t)); + if (manager->threads == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup_lock; + } +- if (isc_condition_init(&manager->work_available) != ISC_R_SUCCESS) { ++ if (isc_condition_init(&manager->work_available[isc_taskqueue_normal]) != ISC_R_SUCCESS) { ++ UNEXPECTED_ERROR(__FILE__, __LINE__, ++ "isc_condition_init() %s", ++ isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, ++ ISC_MSG_FAILED, "failed")); ++ result = ISC_R_UNEXPECTED; ++ goto cleanup_threads; ++ } ++ if (isc_condition_init(&manager->work_available[isc_taskqueue_slow]) != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "isc_condition_init() %s", + isc_msgcat_get(isc_msgcat, ISC_MSGSET_GENERAL, +@@ -1448,8 +1493,10 @@ isc__taskmgr_create(isc_mem_t *mctx, unsigned int workers, + default_quantum = DEFAULT_DEFAULT_QUANTUM; + manager->default_quantum = default_quantum; + INIT_LIST(manager->tasks); +- INIT_LIST(manager->ready_tasks); +- INIT_LIST(manager->ready_priority_tasks); ++ INIT_LIST(manager->ready_tasks[isc_taskqueue_normal]); ++ INIT_LIST(manager->ready_tasks[isc_taskqueue_slow]); ++ INIT_LIST(manager->ready_priority_tasks[isc_taskqueue_normal]); ++ INIT_LIST(manager->ready_priority_tasks[isc_taskqueue_slow]); + manager->tasks_running = 0; + manager->tasks_ready = 0; + manager->exclusive_requested = false; +@@ -1465,7 +1512,19 @@ isc__taskmgr_create(isc_mem_t *mctx, unsigned int workers, + * Start workers. + */ + for (i = 0; i < workers; i++) { +- if (isc_thread_create(run, manager, ++ if (isc_thread_create(run_normal, manager, ++ &manager->threads[manager->workers]) == ++ ISC_R_SUCCESS) { ++ char name[21]; /* thread name limit on Linux */ ++ snprintf(name, sizeof(name), "isc-worker%04u", i); ++ isc_thread_setname(manager->threads[manager->workers], ++ name); ++ manager->workers++; ++ started++; ++ } ++ } ++ for (; i < workers * 2; i++) { ++ if (isc_thread_create(run_slow, manager, + &manager->threads[manager->workers]) == + ISC_R_SUCCESS) { + char name[21]; /* thread name limit on Linux */ +@@ -1482,7 +1541,7 @@ isc__taskmgr_create(isc_mem_t *mctx, unsigned int workers, + manager_free(manager); + return (ISC_R_NOTHREADS); + } +- isc_thread_setconcurrency(workers); ++ isc_thread_setconcurrency(workers * 2); + #endif /* USE_WORKER_THREADS */ + #ifdef USE_SHARED_MANAGER + manager->refs = 1; +@@ -1497,7 +1556,8 @@ isc__taskmgr_create(isc_mem_t *mctx, unsigned int workers, + cleanup_exclusivegranted: + (void)isc_condition_destroy(&manager->exclusive_granted); + cleanup_workavailable: +- (void)isc_condition_destroy(&manager->work_available); ++ (void)isc_condition_destroy(&manager->work_available[isc_taskqueue_slow]); ++ (void)isc_condition_destroy(&manager->work_available[isc_taskqueue_normal]); + cleanup_threads: + isc_mem_free(mctx, manager->threads); + cleanup_lock: +@@ -1582,7 +1642,7 @@ isc__taskmgr_destroy(isc_taskmgr_t **managerp) { + task = NEXT(task, link)) { + LOCK(&task->lock); + if (task_shutdown(task)) +- push_readyq(manager, task); ++ push_readyq(manager, task, task->qid); + UNLOCK(&task->lock); + } + #ifdef USE_WORKER_THREADS +@@ -1591,7 +1651,8 @@ isc__taskmgr_destroy(isc_taskmgr_t **managerp) { + * there's work left to do, and if there are already no tasks left + * it will cause the workers to see manager->exiting. + */ +- BROADCAST(&manager->work_available); ++ BROADCAST(&manager->work_available[isc_taskqueue_normal]); ++ BROADCAST(&manager->work_available[isc_taskqueue_slow]); + UNLOCK(&manager->lock); + + /* +@@ -1693,7 +1754,8 @@ isc__taskmgr_resume(isc_taskmgr_t *manager0) { + LOCK(&manager->lock); + if (manager->pause_requested) { + manager->pause_requested = false; +- BROADCAST(&manager->work_available); ++ BROADCAST(&manager->work_available[isc_taskqueue_normal]); ++ BROADCAST(&manager->work_available[isc_taskqueue_slow]); + } + UNLOCK(&manager->lock); + } +@@ -1778,7 +1840,8 @@ isc__task_endexclusive(isc_task_t *task0) { + LOCK(&manager->lock); + REQUIRE(manager->exclusive_requested); + manager->exclusive_requested = false; +- BROADCAST(&manager->work_available); ++ BROADCAST(&manager->work_available[isc_taskqueue_normal]); ++ BROADCAST(&manager->work_available[isc_taskqueue_slow]); + UNLOCK(&manager->lock); + #else + UNUSED(task0); +@@ -1804,10 +1867,10 @@ isc__task_setprivilege(isc_task_t *task0, bool priv) { + + LOCK(&manager->lock); + if (priv && ISC_LINK_LINKED(task, ready_link)) +- ENQUEUE(manager->ready_priority_tasks, task, ++ ENQUEUE(manager->ready_priority_tasks[task->qid], task, + ready_priority_link); + else if (!priv && ISC_LINK_LINKED(task, ready_priority_link)) +- DEQUEUE(manager->ready_priority_tasks, task, ++ DEQUEUE(manager->ready_priority_tasks[task->qid], task, + ready_priority_link); + UNLOCK(&manager->lock); + } +-- +2.43.2 + diff --git a/SPECS/bind.spec b/SPECS/bind.spec index b0d0f91..13f16e1 100644 --- a/SPECS/bind.spec +++ b/SPECS/bind.spec @@ -68,7 +68,7 @@ Summary: The Berkeley Internet Name Domain (BIND) DNS (Domain Name System) serv Name: bind License: MPLv2.0 Version: 9.11.36 -Release: 11%{?PATCHVER:.%{PATCHVER}}%{?PREVER:.%{PREVER}}%{?dist} +Release: 11%{?PATCHVER:.%{PATCHVER}}%{?PREVER:.%{PREVER}}%{?dist}.1 Epoch: 32 Url: https://www.isc.org/downloads/bind/ # @@ -175,6 +175,14 @@ Patch196: bind-9.16-CVE-2022-3094-test.patch # https://gitlab.isc.org/isc-projects/bind9/commit/f1d9e9ee3859976f403914d20ad2a10855343702 Patch197: bind-9.11-CVE-2023-2828.patch Patch198: bind-9.16-CVE-2023-3341.patch +# https://gitlab.isc.org/isc-projects/bind9/-/merge_requests/8768 +Patch199: bind-9.11-CVE-2023-4408.patch +# https://gitlab.isc.org/isc-projects/bind9/-/merge_requests/8769 +Patch200: bind-9.11-CVE-2023-50387.patch +# https://gitlab.isc.org/isc-projects/bind9/-/merge_requests/8778 +Patch201: bind-9.11-CVE-2023-2828-fixup.patch +# addition to patch 200 +Patch202: bind-9.11-CVE-2023-50387-fixup.patch # SDB patches Patch11: bind-9.3.2b2-sdbsrc.patch @@ -583,6 +591,10 @@ are used for building ISC DHCP. %patch196 -p1 -b .CVE-2022-3094-test %patch197 -p1 -b .CVE-2023-2828 %patch198 -p1 -b .CVE-2023-3341 +%patch199 -p1 -b .CVE-2023-4408 +%patch200 -p1 -b .CVE-2023-50387+50868 +%patch201 -p1 -b .CVE-2023-2828-fixup +%patch202 -p1 -b .CVE-2023-50387-fixup mkdir lib/dns/tests/testdata/dstrandom cp -a %{SOURCE50} lib/dns/tests/testdata/dstrandom/random.data @@ -1635,6 +1647,11 @@ rm -rf ${RPM_BUILD_ROOT} %endif %changelog +* Mon Feb 26 2024 Petr Menšík - 32:9.11.36-11.1 +- Speed up parsing of DNS messages with many different names (CVE-2023-4408) +- Prevent increased CPU consumption in DNSSEC validator (CVE-2023-50387 CVE-2023-50868) +- Do not use header_prev in expire_lru_headers + * Tue Sep 19 2023 Petr Menšík - 32:9.11.36-11 - Prevent exahustion of memory from control channel (CVE-2023-3341)