diff --git a/SOURCES/bind-9.11.4-CVE-2023-2828-fixup.patch b/SOURCES/bind-9.11.4-CVE-2023-2828-fixup.patch new file mode 100644 index 0000000..584f536 --- /dev/null +++ b/SOURCES/bind-9.11.4-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 e61cb21..4171039 100644 +--- a/lib/dns/rbtdb.c ++++ b/lib/dns/rbtdb.c +@@ -10358,19 +10358,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, + isc_boolean_t 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.44.0 + diff --git a/SOURCES/bind-9.11.4-CVE-2023-4408.patch b/SOURCES/bind-9.11.4-CVE-2023-4408.patch new file mode 100644 index 0000000..a2b73fa --- /dev/null +++ b/SOURCES/bind-9.11.4-CVE-2023-4408.patch @@ -0,0 +1,2685 @@ +From 623bb196ca62f513345a93fffd77a60704261f38 Mon Sep 17 00:00:00 2001 +From: Stepan Broz +Date: Mon, 6 May 2024 19:40:19 +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] + +Fix assertion failure in nslookup/dig/mdig when message has + multiple SIG(0) options. + +When parsing message with DNS_MESSAGE_BESTEFFORT (used exclusively in +tools, never in named itself) if we hit an invalid SIG(0) in wrong +place we continue parsing the message, and put the sig0 in msg->sig0. +If we then hit another sig0 in a proper place we see that msg->sig0 +is already 'taken' and we don't free name and rdataset, and we don't +set seen_problem. This causes an assertion failure. +This fixes that issue by setting seen_problem if we hit second sig0, +tsig or opt, which causes name and rdataset to be always freed. + +(cherry picked from commit 51a55ddbb73f8707de3d1b8cda15c8f61585bacb) +(cherry picked from commit 736d8c5b80d02f10555f324abef920a7257f9f43) +--- + .../system/dnssec/ns3/secure.example.db.in | 5 + + bin/tests/system/dnssec/ns3/sign.sh | 112 ++-- + 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 | 624 +++++++++++------- + lib/dns/name.c | 1 + + lib/isc/ht.c | 598 ++++++++++++----- + lib/isc/include/isc/ht.h | 45 +- + lib/isc/tests/ht_test.c | 75 ++- + 11 files changed, 1032 insertions(+), 541 deletions(-) + +diff --git a/bin/tests/system/dnssec/ns3/secure.example.db.in b/bin/tests/system/dnssec/ns3/secure.example.db.in +index 9d310d8..3b713d8 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 99e9b49..ce5b2ee 100644 +--- a/bin/tests/system/dnssec/ns3/sign.sh ++++ b/bin/tests/system/dnssec/ns3/sign.sh +@@ -28,7 +28,9 @@ keyname=`$KEYGEN -q -r $RANDFILE -a RSASHA1 -b 768 -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 +@@ -313,11 +315,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 +@@ -326,10 +328,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 +@@ -338,10 +340,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.* + + # +@@ -351,10 +353,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 +@@ -364,12 +366,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 +@@ -379,12 +381,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. +@@ -447,10 +449,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 + +@@ -462,12 +464,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 +@@ -477,10 +479,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 +@@ -490,21 +492,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. +@@ -512,13 +514,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 +@@ -526,8 +528,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 + + # +@@ -551,10 +553,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 + + # +@@ -563,10 +565,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 +diff --git a/bin/tests/system/dnssec/tests.sh b/bin/tests/system/dnssec/tests.sh +index 91a4822..e8d8f7e 100644 +--- a/bin/tests/system/dnssec/tests.sh ++++ b/bin/tests/system/dnssec/tests.sh +@@ -699,6 +699,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 767c710..98ddd01 100644 +--- a/lib/dns/catz.c ++++ b/lib/dns/catz.c +@@ -395,33 +395,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 +@@ -565,7 +553,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) +@@ -605,9 +592,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; +@@ -624,7 +609,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); +@@ -667,9 +651,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, +@@ -698,7 +680,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)); +@@ -800,8 +781,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)) +@@ -860,8 +840,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;) + { +@@ -1970,8 +1949,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)) +@@ -1993,8 +1971,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;) + { +@@ -2036,5 +2013,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 868a87d..6682450 100644 +--- a/lib/dns/include/dns/message.h ++++ b/lib/dns/include/dns/message.h +@@ -739,45 +739,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 93ddacd..9f60081 100644 +--- a/lib/dns/include/dns/name.h ++++ b/lib/dns/include/dns/name.h +@@ -67,6 +67,7 @@ + #include + + #include ++#include + #include + #include + #include /* Required for storage size of dns_label_t. */ +@@ -110,6 +111,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') +@@ -170,7 +172,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) { \ +@@ -178,12 +180,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 \ + } + + /*% +@@ -1373,6 +1375,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 9210c26..c97b7bd 100644 +--- a/lib/dns/message.c ++++ b/lib/dns/message.c +@@ -19,6 +19,8 @@ + #include + + #include ++#include ++#include + #include + #include + #include /* Required for HP/UX (and others?) */ +@@ -159,6 +161,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); + +@@ -470,7 +475,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; +@@ -491,10 +496,11 @@ msgresetsigs(dns_message_t *msg, isc_boolean_t 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)) +@@ -504,13 +510,10 @@ msgresetsigs(dns_message_t *msg, isc_boolean_t 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); +@@ -812,6 +815,18 @@ dns_message_destroy(dns_message_t **msgp) { + isc_mem_putanddetach(&msg->mctx, msg, sizeof(dns_message_t)); + } + ++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) +@@ -831,28 +846,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 +@@ -980,6 +1003,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) +@@ -991,13 +1026,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; +- isc_boolean_t free_name; ++ isc_boolean_t free_name = ISC_FALSE; + isc_boolean_t best_effort; + isc_boolean_t seen_problem; ++ isc_ht_t *name_map = NULL; ++ isc_boolean_t free_ht = ISC_FALSE; + + section = &msg->sections[DNS_SECTION_QUESTION]; + +@@ -1005,9 +1042,14 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + seen_problem = ISC_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) +@@ -1030,13 +1072,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. +@@ -1048,18 +1097,28 @@ 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 = ISC_FALSE; +- } else { +- isc_mempool_put(msg->namepool, name); ++ break; ++ case ISC_R_EXISTS: ++ dns_message_puttempname(msg, &name); + name = name2; + name2 = NULL; +- free_name = ISC_FALSE; ++ break; ++ default: ++ /* ++ * Unreachable ++ */ ++ break; + } + ++ free_name = ISC_FALSE; ++ + /* + * Get type and class. + */ +@@ -1087,13 +1146,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. + */ +@@ -1102,7 +1154,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; +@@ -1115,32 +1167,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 = ISC_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); + } +@@ -1220,24 +1314,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; +- isc_boolean_t free_name = ISC_FALSE, free_rdataset = ISC_FALSE; +- isc_boolean_t preserve_order, best_effort, seen_problem; +- isc_boolean_t issigzero; +- +- preserve_order = ISC_TF(options & DNS_MESSAGEPARSE_PRESERVEORDER); +- best_effort = ISC_TF(options & DNS_MESSAGEPARSE_BESTEFFORT); +- seen_problem = ISC_FALSE; +- +- section = &msg->sections[sectionid]; ++ dns_namelist_t *section = &msg->sections[sectionid]; ++ isc_boolean_t free_name = ISC_FALSE, seen_problem = ISC_FALSE; ++ isc_boolean_t free_ht = ISC_FALSE; ++ isc_boolean_t preserve_order = ((options & DNS_MESSAGEPARSE_PRESERVEORDER) != 0); ++ isc_boolean_t best_effort = ((options & DNS_MESSAGEPARSE_BESTEFFORT) != 0); ++ isc_boolean_t isedns, issigzero, istsig; ++ isc_ht_t *name_map = NULL; ++ ++ 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; +@@ -1245,11 +1341,15 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + + skip_name_search = ISC_FALSE; + skip_type_search = ISC_FALSE; +- free_rdataset = ISC_FALSE; ++ isedns = ISC_FALSE; ++ issigzero = ISC_FALSE; ++ istsig = ISC_FALSE; ++ found_rdataset = NULL; + + name = isc_mempool_get(msg->namepool); +- if (name == NULL) ++ if (name == NULL) { + return (ISC_R_NOMEMORY); ++ } + free_name = ISC_TRUE; + + offsets = newoffsets(msg); +@@ -1265,8 +1365,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 +@@ -1286,9 +1387,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; + } +@@ -1297,15 +1399,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 +@@ -1315,7 +1419,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. +@@ -1328,10 +1434,13 @@ 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) ++ { + DO_ERROR(DNS_R_BADTSIG); +- msg->sigstart = recstart; +- skip_name_search = ISC_TRUE; +- skip_type_search = ISC_TRUE; ++ } else { ++ skip_name_search = ISC_TRUE; ++ skip_type_search = ISC_TRUE; ++ istsig = ISC_TRUE; ++ } + } else if (rdtype == dns_rdatatype_opt) { + /* + * The name of an OPT record must be ".", it +@@ -1341,9 +1450,13 @@ 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) ++ { + DO_ERROR(DNS_R_FORMERR); +- skip_name_search = ISC_TRUE; +- skip_type_search = ISC_TRUE; ++ } else { ++ skip_name_search = ISC_TRUE; ++ skip_type_search = ISC_TRUE; ++ isedns = ISC_TRUE; ++ } + } else if (rdtype == dns_rdatatype_tkey) { + /* + * A TKEY must be in the additional section if this +@@ -1354,13 +1467,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); ++ } + } + + /* +@@ -1386,7 +1502,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; +@@ -1406,46 +1523,54 @@ 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; +- issigzero = ISC_FALSE; +- 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) ++ { + DO_ERROR(DNS_R_BADSIG0); +- msg->sigstart = recstart; +- skip_name_search = ISC_TRUE; +- skip_type_search = ISC_TRUE; +- issigzero = ISC_TRUE; ++ } else { ++ skip_name_search = ISC_TRUE; ++ skip_type_search = ISC_TRUE; ++ issigzero = ISC_TRUE; ++ } + } 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, +- ISC_FALSE)) { ++ !dns_rdata_checkowner(name, msg->rdclass, rdtype, ISC_FALSE)) ++ { + result = DNS_R_BADOWNERNAME; + goto cleanup; + } +@@ -1456,109 +1581,156 @@ 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) { +- if (rdtype != dns_rdatatype_opt && +- rdtype != dns_rdatatype_tsig && +- !issigzero) +- { ++ skip_name_search) ++ { ++ if (!isedns && !istsig && !issigzero) { + ISC_LIST_APPEND(*section, name, link); + free_name = ISC_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: ++ /* ++ * Unreachable ++ */ ++ break; + } + free_name = ISC_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); ++ } ++ if (ISC_LIST_EMPTY(name->list)) { ++ result = ISC_R_SUCCESS; ++ goto skip_rds_check; ++ } ++ if (name->ht == NULL) { ++ isc_ht_init(&name->ht, msg->mctx, 1, ++ ISC_HT_CASE_SENSITIVE); ++ free_ht = ISC_TRUE; + +- rdataset = NULL; +- result = dns_message_find(name, rdclass, rdtype, +- covers, &rdataset); +- } ++ INSIST(ISC_LIST_HEAD(name->list) == ++ ISC_LIST_TAIL(name->list)); + +- /* +- * 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); +- } +- } ++ dns_rdataset_t *old_rdataset = ++ ISC_LIST_HEAD(name->list); + +- if (result == ISC_R_NOTFOUND) { +- rdataset = isc_mempool_get(msg->rdspool); +- if (rdataset == NULL) { +- result = ISC_R_NOMEMORY; +- goto cleanup; +- } +- free_rdataset = ISC_TRUE; ++ result = rds_hash_add(name->ht, old_rdataset, ++ NULL); + +- rdatalist = newrdatalist(msg); +- if (rdatalist == NULL) { +- result = ISC_R_NOMEMORY; +- goto cleanup; ++ INSIST(result == ISC_R_SUCCESS); + } ++ result = rds_hash_add(name->ht, rdataset, ++ &found_rdataset); + +- rdatalist->type = rdtype; +- rdatalist->covers = covers; +- rdatalist->rdclass = rdclass; +- rdatalist->ttl = ttl; +- +- dns_rdataset_init(rdataset); +- RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist, +- rdataset) +- == ISC_R_SUCCESS); +- dns_rdataset_setownercase(rdataset, name); ++ /* ++ * 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; ++ } + +- if (rdtype != dns_rdatatype_opt && +- rdtype != dns_rdatatype_tsig && +- !issigzero) +- { ++ 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 = ISC_FALSE; ++ break; ++ default: ++ /* ++ * Unreachable ++ */ ++ break; + } + } + +@@ -1572,8 +1744,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. */ +@@ -1588,43 +1761,40 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, + * already set if best-effort parsing is enabled otherwise + * there will only be at most one of each. + */ +- if (rdtype == dns_rdatatype_opt && msg->opt == NULL) { ++ if (isedns) { + dns_rcode_t ercode; + + msg->opt = rdataset; +- rdataset = NULL; +- free_rdataset = ISC_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 = ISC_FALSE; +- } else if (issigzero && msg->sig0 == NULL) { ++ } else if (issigzero) { + msg->sig0 = rdataset; + msg->sig0name = name; +- rdataset = NULL; +- free_rdataset = ISC_FALSE; ++ msg->sigstart = recstart; + free_name = ISC_FALSE; +- } else if (rdtype == dns_rdatatype_tsig && msg->tsig == NULL) { ++ } else if (istsig) { + msg->tsig = rdataset; + msg->tsigname = name; +- /* Windows doesn't like TSIG names to be compressed. */ ++ msg->sigstart = recstart; ++ /* ++ * Windows doesn't like TSIG names to be compressed. ++ */ + msg->tsigname->attributes |= DNS_NAMEATTR_NOCOMPRESS; +- rdataset = NULL; +- free_rdataset = ISC_FALSE; + free_name = ISC_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 = ISC_FALSE; ++ if (free_name) { ++ dns_message_puttempname(msg, &name); ++ } ++ free_name = ISC_FALSE; + } + INSIST(free_name == ISC_FALSE); +- INSIST(free_rdataset == ISC_FALSE); + } + + /* +@@ -1635,20 +1805,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); + } +@@ -2380,11 +2562,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); + } + } + +@@ -2477,24 +2659,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) +@@ -2587,6 +2751,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); +@@ -2602,14 +2769,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 +@@ -2774,7 +2946,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 e597c63..b3b127b 100644 +--- a/lib/dns/name.c ++++ b/lib/dns/name.c +@@ -213,6 +213,7 @@ dns_name_invalidate(dns_name_t *name) { + name->offsets = NULL; + name->buffer = NULL; + ISC_LINK_INIT(name, link); ++ INSIST(name->ht == NULL); + } + + isc_boolean_t +diff --git a/lib/isc/ht.c b/lib/isc/ht.c +index a86e3ec..0cd3a2c 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 http://mozilla.org/MPL/2.0/. +@@ -19,71 +21,290 @@ + #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) ((isc_uint64_t)(1) << (bits)) + + struct isc_ht_node { + void *value; + isc_ht_node_t *next; ++ isc_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; ++ isc_boolean_t case_sensitive; ++ size_t size[2]; ++ isc_uint8_t hashbits[2]; ++ isc_ht_node_t **table[2]; ++ isc_uint8_t hindex; ++ isc_uint32_t hiter; /* rehashing iterator */ + }; + + struct isc_ht_iter { + isc_ht_t *ht; + size_t i; ++ isc_uint8_t hindex; + isc_ht_node_t *cur; + }; + +-isc_result_t +-isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, isc_uint8_t bits) { +- isc_ht_t *ht = NULL; ++static isc_ht_node_t * ++isc__ht_find(const isc_ht_t *ht, const unsigned char *key, ++ const isc_uint32_t keysize, const isc_uint32_t hashval, const isc_uint8_t idx); ++static void ++isc__ht_add(isc_ht_t *ht, const unsigned char *key, const isc_uint32_t keysize, ++ const isc_uint32_t hashval, const isc_uint8_t idx, void *value); ++static isc_result_t ++isc__ht_delete(isc_ht_t *ht, const unsigned char *key, const isc_uint32_t keysize, ++ const isc_uint32_t hashval, const isc_uint8_t idx); ++ ++static isc_uint32_t ++rehash_bits(isc_ht_t *ht, size_t newcount); ++ ++static void ++hashtable_new(isc_ht_t *ht, const isc_uint8_t idx, const isc_uint8_t bits); ++static void ++hashtable_free(isc_ht_t *ht, const isc_uint8_t idx); ++static void ++hashtable_rehash(isc_ht_t *ht, isc_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 isc_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 ++}; ++ ++static int ++memcasecmp(const void *vs1, const void *vs2, size_t len) { ++ isc_uint8_t const *s1 = vs1; ++ isc_uint8_t const *s2 = vs2; + size_t i; ++ for (i = 0; i < len; i++) { ++ isc_uint8_t u1 = s1[i]; ++ isc_uint8_t u2 = s2[i]; ++ int U1 = maptolower[u1]; ++ int U2 = maptolower[u2]; ++ int diff = U1 - U2; ++ if (diff) { ++ return diff; ++ } ++ } ++ return 0; ++} + +- REQUIRE(htp != NULL && *htp == NULL); +- REQUIRE(mctx != NULL); +- REQUIRE(bits >= 1 && bits <= (sizeof(size_t)*8 - 1)); ++static isc_boolean_t ++isc__ht_node_match(isc_ht_node_t *node, const isc_uint32_t hashval, ++ const isc_uint8_t *key, isc_uint32_t keysize, isc_boolean_t case_sensitive) { ++ return (node->hashval == hashval && node->keysize == keysize && ++ (case_sensitive ? (memcmp(node->key, key, keysize) == 0) ++ : (memcasecmp(node->key, key, keysize) == 0))); ++} ++ ++static isc_uint32_t ++hash_32(isc_uint32_t val, unsigned int bits) { ++ REQUIRE(bits <= HT_MAX_BITS); ++ /* High bits are more random. */ ++ return (val * GOLDEN_RATIO_32 >> (32 - bits)); ++} ++ ++static isc_boolean_t ++rehashing_in_progress(const isc_ht_t *ht) { ++ return (ht->table[HT_NEXTTABLE(ht->hindex)] != NULL); ++} + +- ht = isc_mem_get(mctx, sizeof(struct isc_ht)); +- if (ht == NULL) { +- return (ISC_R_NOMEMORY); ++static isc_boolean_t ++hashtable_is_overcommited(isc_ht_t *ht) { ++ return (ht->count >= (ht->size[ht->hindex] * HT_OVERCOMMIT)); ++} ++ ++static isc_uint32_t ++rehash_bits(isc_ht_t *ht, size_t newcount) { ++ isc_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, isc_uint32_t newbits) { ++ isc_uint8_t oldindex = ht->hindex; ++ isc_uint32_t oldbits = ht->hashbits[oldindex]; ++ isc_uint8_t newindex = HT_NEXTTABLE(oldindex); ++ ++ 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); + +- ht->size = ((size_t)1<mask = ((size_t)1<count = 0; ++ hashtable_new(ht, newindex, newbits); + +- 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); ++ 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]; ++ isc_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++; + } + +- for (i = 0; i < ht->size; i++) { +- ht->table[i] = NULL; ++ /* Rehashing complete */ ++ if (ht->hiter == oldsize) { ++ hashtable_free(ht, HT_NEXTTABLE(ht->hindex)); ++ ht->hiter = 0; ++ return; + } + ++ /* Move the first non-empty node from old hashtable to new hashtable */ ++ for (node = oldtable[ht->hiter]; node != NULL; node = nextnode) { ++ isc_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) { ++ isc_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 isc_uint8_t idx, const isc_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 isc_uint8_t idx) { ++ size_t size = ht->size[idx] * sizeof(isc_ht_node_t *); ++ size_t i; ++ ++ for (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, isc_uint8_t bits, ++ unsigned int options) { ++ isc_ht_t *ht = NULL; ++ isc_boolean_t 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 +@@ -91,125 +312,190 @@ isc_ht_destroy(isc_ht_t **htp) { + isc_ht_t *ht; + size_t i; + ++ + REQUIRE(htp != NULL); ++ REQUIRE(ISC_HT_VALID(*htp)); + + ht = *htp; +- REQUIRE(ISC_HT_VALID(ht)); +- ++ *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 (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, +- isc_uint32_t keysize, void *value) +-{ ++static void ++isc__ht_add(isc_ht_t *ht, const unsigned char *key, const isc_uint32_t keysize, ++ const isc_uint32_t hashval, const isc_uint8_t idx, void *value) { + isc_ht_node_t *node; + isc_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 isc_uint32_t keysize, ++ void *value) { ++ isc_uint32_t hashval; ++ + REQUIRE(ISC_HT_VALID(ht)); + REQUIRE(key != NULL && keysize > 0); + +- hash = isc_hash_function(key, keysize, ISC_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 isc_uint32_t keysize, const isc_uint32_t hashval, ++ const isc_uint8_t idx) { ++ isc_uint32_t hash; ++ isc_uint8_t findex = idx; ++ isc_ht_node_t *node; ++ ++nexttable: ++ hash = hash_32(hashval, ht->hashbits[findex]); ++ for (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, +- isc_uint32_t keysize, void **valuep) +-{ ++ const isc_uint32_t keysize, void **valuep) { ++ isc_uint32_t hashval; + isc_ht_node_t *node; +- isc_uint32_t hash; + + REQUIRE(ISC_HT_VALID(ht)); + REQUIRE(key != NULL && keysize > 0); + REQUIRE(valuep == NULL || *valuep == NULL); + +- hash = isc_hash_function(key, keysize, ISC_TRUE, NULL); +- node = ht->table[hash & ht->mask]; +- while (node != NULL) { +- if (keysize == node->keysize && +- memcmp(key, node->key, keysize) == 0) { +- *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, isc_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 isc_uint32_t keysize, ++ const isc_uint32_t hashval, const isc_uint8_t idx) { ++ isc_ht_node_t *prev = NULL; + isc_uint32_t hash; ++ isc_ht_node_t *node; + +- REQUIRE(ISC_HT_VALID(ht)); +- REQUIRE(key != NULL && keysize > 0); +- +- prev = NULL; +- hash = isc_hash_function(key, keysize, ISC_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 (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 isc_uint32_t keysize) { ++ isc_uint32_t hashval; ++ isc_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; + +@@ -217,16 +503,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 +@@ -237,26 +520,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 +@@ -265,58 +570,35 @@ 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; +- isc_uint32_t hash; ++ isc_ht_node_t *dnode = NULL; ++ isc_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; ++ dnode = it->cur; ++ dindex = it->hindex; + +- 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]; +- } +- +- hash = isc_hash_function(to_delete->key, to_delete->keysize, ISC_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); + } +@@ -331,8 +613,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); +@@ -341,9 +623,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 ea9eab7..843e671 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 http://mozilla.org/MPL/2.0/. +@@ -11,30 +13,33 @@ + + /* ! \file */ + +-#ifndef ISC_HT_H +-#define ISC_HT_H 1 ++#pragma once + + #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, isc_uint8_t bits); ++void ++isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, isc_uint8_t bits, ++ unsigned int options); + + /*% + * Destroy hashtable, freeing everything +@@ -51,6 +56,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 +@@ -58,15 +64,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, isc_uint32_t keysize, +- void *value); ++isc_ht_add(isc_ht_t *ht, const unsigned char *key, const isc_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 +@@ -74,20 +83,21 @@ isc_ht_add(isc_ht_t *ht, const unsigned char *key, isc_uint32_t keysize, + */ + isc_result_t + isc_ht_find(const isc_ht_t *ht, const unsigned char *key, +- isc_uint32_t keysize, void **valuep); ++ const isc_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, isc_uint32_t keysize); ++isc_ht_delete(isc_ht_t *ht, const unsigned char *key, const isc_uint32_t keysize); + + /*% + * Create an iterator for the hashtable; point '*itp' to it. +@@ -96,7 +106,7 @@ isc_ht_delete(isc_ht_t *ht, const unsigned char *key, isc_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); + + /*% +@@ -176,6 +186,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 11ffb79..f1b2782 100644 +--- a/lib/isc/tests/ht_test.c ++++ b/lib/isc/tests/ht_test.c +@@ -52,8 +52,7 @@ static void test_ht_full(int bits, uintptr_t count) { + NULL, &mctx, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + +- result = isc_ht_init(&ht, mctx, bits); +- ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); ++ isc_ht_init(&ht, mctx, bits, ISC_HT_CASE_SENSITIVE); + ATF_REQUIRE(ht != NULL); + + for (i = 1; i < count; i++) { +@@ -203,8 +202,7 @@ static void test_ht_iterator() { + NULL, &mctx, 0); + ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); + +- result = isc_ht_init(&ht, mctx, 16); +- ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); ++ isc_ht_init(&ht, mctx, 16, ISC_HT_CASE_SENSITIVE); + ATF_REQUIRE(ht != NULL); + for (i = 1; i <= count; i++) { + /* +@@ -218,8 +216,7 @@ static void test_ht_iterator() { + } + + walked = 0; +- result = isc_ht_iter_create(ht, &iter); +- ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); ++ isc_ht_iter_create(ht, &iter); + + for (result = isc_ht_iter_first(iter); + result == ISC_R_SUCCESS; +@@ -303,6 +300,61 @@ static void test_ht_iterator() { + ATF_REQUIRE_EQ(ht, NULL); + } + ++static void test_ht_case() { ++ ++ 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); ++ ATF_REQUIRE_EQ(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); ++ ATF_REQUIRE(ht != NULL); ++ ++ result = isc_ht_add(ht, lower, 16, (void *)lower); ++ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); ++ ++ result = isc_ht_add(ht, same, 16, (void *)same); ++ ATF_REQUIRE_EQ(result, ISC_R_EXISTS); ++ ++ result = isc_ht_add(ht, upper, 16, (void *)upper); ++ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); ++ ++ result = isc_ht_find(ht, mixed, 16, &f); ++ ATF_REQUIRE_EQ(result, ISC_R_NOTFOUND); ++ ATF_REQUIRE_EQ(f, NULL); ++ ++ isc_ht_destroy(&ht); ++ ATF_REQUIRE_EQ(ht, NULL); ++ ++ isc_ht_init(&ht, mctx, 8, ISC_HT_CASE_INSENSITIVE); ++ ATF_REQUIRE(ht != NULL); ++ ++ result = isc_ht_add(ht, lower, 16, (void *)lower); ++ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); ++ ++ result = isc_ht_add(ht, same, 16, (void *)same); ++ ATF_REQUIRE_EQ(result, ISC_R_EXISTS); ++ ++ result = isc_ht_add(ht, upper, 16, (void *)upper); ++ ATF_REQUIRE_EQ(result, ISC_R_EXISTS); ++ ++ result = isc_ht_find(ht, mixed, 16, &f); ++ ATF_REQUIRE_EQ(result, ISC_R_SUCCESS); ++ ATF_REQUIRE_EQ(f, &lower); ++ ++ isc_ht_destroy(&ht); ++ ATF_REQUIRE_EQ(ht, NULL); ++} ++ + ATF_TC(isc_ht_20); + ATF_TC_HEAD(isc_ht_20, tc) { + atf_tc_set_md_var(tc, "descr", "20 bit, 200K elements test"); +@@ -357,6 +409,16 @@ ATF_TC_BODY(isc_ht_iterator, tc) { + test_ht_iterator(); + } + ++ATF_TC(isc_ht_case); ++ATF_TC_HEAD(isc_ht_case, tc) { ++ atf_tc_set_md_var(tc, "descr", "case sensitivity test"); ++} ++ ++ATF_TC_BODY(isc_ht_case, tc) { ++ UNUSED(tc); ++ test_ht_case(); ++} ++ + /* + * Main + */ +@@ -366,5 +428,6 @@ ATF_TP_ADD_TCS(tp) { + ATF_TP_ADD_TC(tp, isc_ht_1); + /* ATF_TP_ADD_TC(tp, isc_ht_32); */ + ATF_TP_ADD_TC(tp, isc_ht_iterator); ++ ATF_TP_ADD_TC(tp, isc_ht_case); + return (atf_no_error()); + } +-- +2.45.0 + diff --git a/SOURCES/bind-9.11.4-CVE-2023-50387.patch b/SOURCES/bind-9.11.4-CVE-2023-50387.patch new file mode 100644 index 0000000..c1c1cab --- /dev/null +++ b/SOURCES/bind-9.11.4-CVE-2023-50387.patch @@ -0,0 +1,779 @@ +From b7c66c030242c9271f8f0629b631e5a84a97cbe6 Mon Sep 17 00:00:00 2001 +From: Stepan Broz +Date: Tue, 16 Apr 2024 18:48:51 +0200 +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) + +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] + +Related to [GL #4424] and [GL #4459]: + +Add normal task queue also to non-thread version + +Non-thread builds are used by us for dhcp package. Make it working +again. +--- + 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 | 96 ++++++++---------- + lib/dns/win32/libdns.def.in | 1 + + lib/isc/include/isc/task.h | 11 ++- + lib/isc/task.c | 167 ++++++++++++++++++++++---------- + 8 files changed, 193 insertions(+), 114 deletions(-) + +diff --git a/lib/dns/dst_api.c b/lib/dns/dst_api.c +index dbece0a..3ed8f7f 100644 +--- a/lib/dns/dst_api.c ++++ b/lib/dns/dst_api.c +@@ -103,6 +103,7 @@ static isc_result_t frombuffer(dns_name_t *name, + dns_rdataclass_t rdclass, + isc_buffer_t *source, + isc_mem_t *mctx, ++ isc_boolean_t no_rdata, + dst_key_t **keyp); + + static isc_result_t algorithm_status(unsigned int alg); +@@ -751,6 +752,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, ISC_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, isc_boolean_t no_rdata, ++ dst_key_t **keyp) { + isc_uint8_t alg, proto; + isc_uint32_t flags, extflags; + dst_key_t *key = NULL; +@@ -779,7 +787,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; +@@ -801,7 +809,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, ISC_FALSE, &key); + if (result != ISC_R_SUCCESS) + return (result); + +@@ -1899,7 +1907,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, isc_boolean_t no_rdata, ++ dst_key_t **keyp) + { + dst_key_t *key; + isc_result_t ret; +@@ -1924,10 +1933,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 100eab7..a4d15b4 100644 +--- a/lib/dns/include/dns/validator.h ++++ b/lib/dns/include/dns/validator.h +@@ -158,6 +158,7 @@ struct dns_validator { + unsigned int depth; + unsigned int authcount; + unsigned int authfail; ++ isc_boolean_t failed; + isc_stdtime_t start; + }; + +diff --git a/lib/dns/include/dst/dst.h b/lib/dns/include/dst/dst.h +index 42c67d4..6018bc2 100644 +--- a/lib/dns/include/dst/dst.h ++++ b/lib/dns/include/dst/dst.h +@@ -414,6 +414,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, isc_boolean_t 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 a55b5fd..b2de13a 100644 +--- a/lib/dns/resolver.c ++++ b/lib/dns/resolver.c +@@ -9089,7 +9089,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 289b505..695a5c2 100644 +--- a/lib/dns/validator.c ++++ b/lib/dns/validator.c +@@ -1197,6 +1197,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, +@@ -1206,54 +1212,58 @@ 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; +- isc_boolean_t foundold; ++ isc_boolean_t no_rdata = ISC_FALSE; + +- if (oldkey == NULL) +- foundold = ISC_TRUE; +- else { +- foundold = ISC_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 = ISC_TRUE; +- dst_key_free(&oldkey); ++ if (no_rdata) { ++ /* Retry with full key */ ++ dns_rdata_reset(&rdata); ++ dst_key_free(&val->key); ++ no_rdata = ISC_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 = ISC_TRUE; + } while (result == ISC_R_SUCCESS); +- if (result == ISC_R_NOMORE) ++done: ++ if (result == ISC_R_NOMORE) { + result = ISC_R_NOTFOUND; +- +- failure: +- if (oldkey != NULL) +- dst_key_free(&oldkey); ++ } + + return (result); + } +@@ -1622,37 +1632,13 @@ validate(dns_validator_t *val, isc_boolean_t 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 = ISC_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, +@@ -1689,9 +1675,13 @@ validate(dns_validator_t *val, isc_boolean_t resume) { + } else { + validator_log(val, ISC_LOG_DEBUG(3), + "verify failure: %s", +- isc_result_totext(result)); ++ isc_result_totext(vresult)); + resume = ISC_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 d48eeb2..54e11a3 100644 +--- a/lib/dns/win32/libdns.def.in ++++ b/lib/dns/win32/libdns.def.in +@@ -1432,6 +1432,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 1273214..9b6f58a 100644 +--- a/lib/isc/include/isc/task.h ++++ b/lib/isc/include/isc/task.h +@@ -96,8 +96,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 139ff22..3fe442d 100644 +--- a/lib/isc/task.c ++++ b/lib/isc/task.c +@@ -105,6 +105,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; +@@ -139,11 +140,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 */ +@@ -245,13 +246,13 @@ isc_taskmgrmode_t + isc__taskmgr_mode(isc_taskmgr_t *manager0); + + static inline isc_boolean_t +-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; +@@ -322,7 +323,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); +@@ -360,7 +362,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); +@@ -471,10 +479,10 @@ task_ready(isc__task_t *task) { + XTRACE("task_ready"); + + LOCK(&manager->lock); +- push_readyq(manager, task); ++ push_readyq(manager, task, task->qid); + #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); + } +@@ -945,13 +953,13 @@ isc__task_getcurrenttimex(isc_task_t *task0, isc_time_t *t) { + * Caller must hold the task manager lock. + */ + static inline isc_boolean_t +-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 (ISC_TF(EMPTY(queue))); + } +@@ -965,18 +973,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); + } + +@@ -990,16 +998,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; +@@ -1078,26 +1086,26 @@ 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")); + } + #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, + ISC_MSG_WORKING, "working")); + +- task = pop_readyq(manager); ++ task = pop_readyq(manager, qid); + if (task != NULL) { + unsigned int dispatch_count = 0; + isc_boolean_t done = ISC_FALSE; +@@ -1261,7 +1269,7 @@ dispatch(isc__taskmgr_t *manager) { + * might even hurt rather than help. + */ + #ifdef USE_WORKER_THREADS +- push_readyq(manager, task); ++ push_readyq(manager, task, qid); + #else + ENQUEUE(new_ready_tasks, task, ready_link); + if ((task->flags & TASK_F_PRIVILEGED) != 0) +@@ -1279,20 +1287,24 @@ 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 + } + + #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 + +@@ -1304,13 +1316,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")); +@@ -1329,7 +1365,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 */ +@@ -1396,12 +1433,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, +@@ -1430,8 +1475,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 = ISC_FALSE; +@@ -1447,7 +1494,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[16]; /* thread name limit on Linux */ +@@ -1464,7 +1523,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; +@@ -1479,7 +1538,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: +@@ -1564,7 +1624,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 +@@ -1573,7 +1633,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); + + /* +@@ -1634,7 +1695,8 @@ isc__taskmgr_ready(isc_taskmgr_t *manager0) { + return (ISC_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); +@@ -1651,7 +1713,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); + } +@@ -1675,7 +1738,8 @@ isc__taskmgr_resume(isc_taskmgr_t *manager0) { + LOCK(&manager->lock); + if (manager->pause_requested) { + manager->pause_requested = ISC_FALSE; +- BROADCAST(&manager->work_available); ++ BROADCAST(&manager->work_available[isc_taskqueue_normal]); ++ BROADCAST(&manager->work_available[isc_taskqueue_slow]); + } + UNLOCK(&manager->lock); + } +@@ -1751,7 +1815,8 @@ isc__task_endexclusive(isc_task_t *task0) { + LOCK(&manager->lock); + REQUIRE(manager->exclusive_requested); + manager->exclusive_requested = ISC_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); +@@ -1777,10 +1842,10 @@ isc__task_setprivilege(isc_task_t *task0, isc_boolean_t 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.44.0 + diff --git a/SOURCES/bind-9.11.4-missing-DBC-checks.patch b/SOURCES/bind-9.11.4-missing-DBC-checks.patch new file mode 100644 index 0000000..e70fcbf --- /dev/null +++ b/SOURCES/bind-9.11.4-missing-DBC-checks.patch @@ -0,0 +1,1141 @@ +From c0a3c488020120b82acc03cf68d347722b38123a Mon Sep 17 00:00:00 2001 +From: Mark Andrews +Date: Wed, 28 Nov 2018 18:57:38 +1100 +Subject: [PATCH] add missing DBC checks for catz and add isc_magic checks; add + DBC checks to ht.c + +5103. [bug] Add missing design by contract tests to dns_catz*. + [GL #748] + +(cherry picked from commit a487473fc554baf1421193c6803dad466f470af5) +(cherry picked from commit 17d9fa3cc70b29c8dcf4495170bfc3f6c79f9083) +(cherry picked from commit c0a3c488020120b82acc03cf68d347722b38123a) +--- + lib/dns/catz.c | 135 ++++++++++++++++++++++----------- + lib/dns/include/dns/catz.h | 148 +++++++++++++++++++------------------ + lib/isc/ht.c | 6 +- + lib/isc/include/isc/ht.h | 37 ++++++++-- + lib/isc/tests/ht_test.c | 11 ++- + 5 files changed, 215 insertions(+), 122 deletions(-) + +diff --git a/lib/dns/catz.c b/lib/dns/catz.c +index 7218430..767c710 100644 +--- a/lib/dns/catz.c ++++ b/lib/dns/catz.c +@@ -29,11 +29,19 @@ + #include + #include + ++#define DNS_CATZ_ZONE_MAGIC ISC_MAGIC('c', 'a', 't', 'z') ++#define DNS_CATZ_ZONES_MAGIC ISC_MAGIC('c', 'a', 't', 's') ++#define DNS_CATZ_ENTRY_MAGIC ISC_MAGIC('c', 'a', 't', 'e') ++ ++#define DNS_CATZ_ZONE_VALID(catz) ISC_MAGIC_VALID(catz, DNS_CATZ_ZONE_MAGIC) ++#define DNS_CATZ_ZONES_VALID(catzs) ISC_MAGIC_VALID(catzs, DNS_CATZ_ZONES_MAGIC) ++#define DNS_CATZ_ENTRY_VALID(entry) ISC_MAGIC_VALID(entry, DNS_CATZ_ENTRY_MAGIC) + + /*% + * Single member zone in a catalog + */ + struct dns_catz_entry { ++ unsigned int magic; + dns_name_t name; + dns_catz_options_t opts; + isc_refcount_t refs; +@@ -43,6 +51,7 @@ struct dns_catz_entry { + * Catalog zone + */ + struct dns_catz_zone { ++ unsigned int magic; + dns_name_t name; + dns_catz_zones_t *catzs; + dns_rdata_t soa; +@@ -81,6 +90,7 @@ catz_process_zones_suboption(dns_catz_zone_t *zone, dns_rdataset_t *value, + * Collection of catalog zones for a view + */ + struct dns_catz_zones { ++ unsigned int magic; + isc_ht_t *zones; + isc_mem_t *mctx; + isc_refcount_t refs; +@@ -94,6 +104,9 @@ struct dns_catz_zones { + + void + dns_catz_options_init(dns_catz_options_t *options) { ++ ++ REQUIRE(options != NULL); ++ + dns_ipkeylist_init(&options->masters); + + options->allow_query = NULL; +@@ -109,6 +122,10 @@ dns_catz_options_init(dns_catz_options_t *options) { + + void + dns_catz_options_free(dns_catz_options_t *options, isc_mem_t *mctx) { ++ ++ REQUIRE(options != NULL); ++ REQUIRE(mctx != NULL); ++ + if (options->masters.count != 0) + dns_ipkeylist_clear(mctx, &options->masters); + if (options->zonedir != NULL) { +@@ -125,6 +142,7 @@ isc_result_t + dns_catz_options_copy(isc_mem_t *mctx, const dns_catz_options_t *src, + dns_catz_options_t *dst) + { ++ REQUIRE(mctx != NULL); + REQUIRE(src != NULL); + REQUIRE(dst != NULL); + REQUIRE(dst->masters.count == 0); +@@ -155,6 +173,10 @@ isc_result_t + dns_catz_options_setdefault(isc_mem_t *mctx, const dns_catz_options_t *defaults, + dns_catz_options_t *opts) + { ++ REQUIRE(mctx != NULL); ++ REQUIRE(defaults != NULL); ++ REQUIRE(opts != NULL); ++ + if (opts->masters.count == 0 && defaults->masters.count != 0) + dns_ipkeylist_copy(mctx, &defaults->masters, &opts->masters); + +@@ -179,6 +201,7 @@ dns_catz_entry_new(isc_mem_t *mctx, const dns_name_t *domain, + dns_catz_entry_t *nentry; + isc_result_t result; + ++ REQUIRE(mctx != NULL); + REQUIRE(nentryp != NULL && *nentryp == NULL); + + nentry = isc_mem_get(mctx, sizeof(dns_catz_entry_t)); +@@ -194,6 +217,7 @@ dns_catz_entry_new(isc_mem_t *mctx, const dns_name_t *domain, + + dns_catz_options_init(&nentry->opts); + isc_refcount_init(&nentry->refs, 1); ++ nentry->magic = DNS_CATZ_ENTRY_MAGIC; + *nentryp = nentry; + return (ISC_R_SUCCESS); + +@@ -204,6 +228,7 @@ cleanup: + + dns_name_t * + dns_catz_entry_getname(dns_catz_entry_t *entry) { ++ REQUIRE(DNS_CATZ_ENTRY_VALID(entry)); + return (&entry->name); + } + +@@ -214,6 +239,10 @@ dns_catz_entry_copy(dns_catz_zone_t *zone, const dns_catz_entry_t *entry, + isc_result_t result; + dns_catz_entry_t *nentry = NULL; + ++ REQUIRE(DNS_CATZ_ZONE_VALID(zone)); ++ REQUIRE(DNS_CATZ_ENTRY_VALID(entry)); ++ REQUIRE(nentryp != NULL && *nentryp == NULL); ++ + result = dns_catz_entry_new(zone->catzs->mctx, &entry->name, &nentry); + if (result != ISC_R_SUCCESS) + return (result); +@@ -229,7 +258,9 @@ dns_catz_entry_copy(dns_catz_zone_t *zone, const dns_catz_entry_t *entry, + + void + dns_catz_entry_attach(dns_catz_entry_t *entry, dns_catz_entry_t **entryp) { ++ REQUIRE(DNS_CATZ_ENTRY_VALID(entry)); + REQUIRE(entryp != NULL && *entryp == NULL); ++ + isc_refcount_increment(&entry->refs, NULL); + *entryp = entry; + } +@@ -240,9 +271,11 @@ dns_catz_entry_detach(dns_catz_zone_t *zone, dns_catz_entry_t **entryp) { + isc_mem_t *mctx; + unsigned int refs; + ++ REQUIRE(DNS_CATZ_ZONE_VALID(zone)); + REQUIRE(entryp != NULL && *entryp != NULL); +- + entry = *entryp; ++ REQUIRE(DNS_CATZ_ENTRY_VALID(entry)); ++ + *entryp = NULL; + + mctx = zone->catzs->mctx; +@@ -259,6 +292,7 @@ dns_catz_entry_detach(dns_catz_zone_t *zone, dns_catz_entry_t **entryp) { + + isc_boolean_t + dns_catz_entry_validate(const dns_catz_entry_t *entry) { ++ REQUIRE(DNS_CATZ_ENTRY_VALID(entry)); + UNUSED(entry); + + return (ISC_TRUE); +@@ -268,6 +302,9 @@ isc_boolean_t + dns_catz_entry_cmp(const dns_catz_entry_t *ea, const dns_catz_entry_t *eb) { + isc_region_t ra, rb; + ++ REQUIRE(DNS_CATZ_ENTRY_VALID(ea)); ++ REQUIRE(DNS_CATZ_ENTRY_VALID(eb)); ++ + if (ea == eb) + return (ISC_TRUE); + +@@ -308,21 +345,21 @@ dns_catz_entry_cmp(const dns_catz_entry_t *ea, const dns_catz_entry_t *eb) { + + dns_name_t * + dns_catz_zone_getname(dns_catz_zone_t *zone) { +- REQUIRE(zone != NULL); ++ REQUIRE(DNS_CATZ_ZONE_VALID(zone)); + + return (&zone->name); + } + + dns_catz_options_t * + dns_catz_zone_getdefoptions(dns_catz_zone_t *zone) { +- REQUIRE(zone != NULL); ++ REQUIRE(DNS_CATZ_ZONE_VALID(zone)); + + return (&zone->defoptions); + } + + void + dns_catz_zone_resetdefoptions(dns_catz_zone_t *zone) { +- REQUIRE(zone != NULL); ++ REQUIRE(DNS_CATZ_ZONE_VALID(zone)); + + dns_catz_options_free(&zone->defoptions, zone->catzs->mctx); + dns_catz_options_init(&zone->defoptions); +@@ -339,8 +376,8 @@ dns_catz_zones_merge(dns_catz_zone_t *target, dns_catz_zone_t *newzone) { + char zname[DNS_NAME_FORMATSIZE]; + dns_catz_zoneop_fn_t addzone, modzone, delzone; + +- REQUIRE(target != NULL); +- REQUIRE(newzone != NULL); ++ REQUIRE(DNS_CATZ_ZONE_VALID(newzone)); ++ REQUIRE(DNS_CATZ_ZONE_VALID(target)); + + /* TODO verify the new zone first! */ + +@@ -395,9 +432,9 @@ dns_catz_zones_merge(dns_catz_zone_t *target, dns_catz_zone_t *newzone) { + result = delcur ? isc_ht_iter_delcurrent_next(iter1) : + isc_ht_iter_next(iter1)) + { +- dns_catz_entry_t *nentry; +- dns_catz_entry_t *oentry; +- unsigned char * key; ++ dns_catz_entry_t *nentry = NULL; ++ dns_catz_entry_t *oentry = NULL; ++ unsigned char * key = NULL; + size_t keysize; + delcur = ISC_FALSE; + +@@ -468,7 +505,7 @@ dns_catz_zones_merge(dns_catz_zone_t *target, dns_catz_zone_t *newzone) { + result == ISC_R_SUCCESS; + result = isc_ht_iter_delcurrent_next(iter2)) + { +- dns_catz_entry_t *entry; ++ dns_catz_entry_t *entry = NULL; + isc_ht_iter_current(iter2, (void **) &entry); + + dns_name_format(&entry->name, zname, DNS_NAME_FORMATSIZE); +@@ -491,7 +528,7 @@ dns_catz_zones_merge(dns_catz_zone_t *target, dns_catz_zone_t *newzone) { + result == ISC_R_SUCCESS; + result = isc_ht_iter_delcurrent_next(iteradd)) + { +- dns_catz_entry_t *entry; ++ dns_catz_entry_t *entry = NULL; + isc_ht_iter_current(iteradd, (void **) &entry); + + dns_name_format(&entry->name, zname, DNS_NAME_FORMATSIZE); +@@ -510,7 +547,7 @@ dns_catz_zones_merge(dns_catz_zone_t *target, dns_catz_zone_t *newzone) { + result == ISC_R_SUCCESS; + result = isc_ht_iter_delcurrent_next(itermod)) + { +- dns_catz_entry_t *entry; ++ dns_catz_entry_t *entry = NULL; + isc_ht_iter_current(itermod, (void **) &entry); + result = modzone(entry, target, target->catzs->view, + target->catzs->taskmgr, +@@ -542,7 +579,6 @@ cleanup: + if (tomod != NULL) + isc_ht_destroy(&tomod); + return (result); +- + } + + isc_result_t +@@ -581,6 +617,7 @@ dns_catz_new_zones(dns_catz_zones_t **catzsp, dns_catz_zonemodmethods_t *zmm, + result = isc_task_create(taskmgr, 0, &new_zones->updater); + if (result != ISC_R_SUCCESS) + goto cleanup_ht; ++ new_zones->magic = DNS_CATZ_ZONES_MAGIC; + + *catzsp = new_zones; + return (ISC_R_SUCCESS); +@@ -599,7 +636,7 @@ dns_catz_new_zones(dns_catz_zones_t **catzsp, dns_catz_zonemodmethods_t *zmm, + + void + dns_catz_catzs_set_view(dns_catz_zones_t *catzs, dns_view_t *view) { +- REQUIRE(catzs != NULL); ++ REQUIRE(DNS_CATZ_ZONES_VALID(catzs)); + REQUIRE(view != NULL); + /* Either it's a new one or it's being reconfigured. */ + REQUIRE(catzs->view == NULL || !strcmp(catzs->view->name, view->name)); +@@ -614,7 +651,9 @@ dns_catz_new_zone(dns_catz_zones_t *catzs, dns_catz_zone_t **zonep, + isc_result_t result; + dns_catz_zone_t *new_zone; + ++ REQUIRE(DNS_CATZ_ZONES_VALID(catzs)); + REQUIRE(zonep != NULL && *zonep == NULL); ++ REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC)); + + new_zone = isc_mem_get(catzs->mctx, sizeof(*new_zone)); + if (new_zone == NULL) +@@ -651,6 +690,7 @@ dns_catz_new_zone(dns_catz_zones_t *catzs, dns_catz_zone_t **zonep, + new_zone->db_registered = ISC_FALSE; + new_zone->version = (isc_uint32_t)(-1); + isc_refcount_init(&new_zone->refs, 1); ++ new_zone->magic = DNS_CATZ_ZONE_MAGIC; + + *zonep = new_zone; + +@@ -674,9 +714,10 @@ dns_catz_add_zone(dns_catz_zones_t *catzs, const dns_name_t *name, + isc_result_t result, tresult; + char zname[DNS_NAME_FORMATSIZE]; + +- REQUIRE(catzs != NULL); +- REQUIRE(name != NULL); ++ REQUIRE(DNS_CATZ_ZONES_VALID(catzs)); ++ REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC)); + REQUIRE(zonep != NULL && *zonep == NULL); ++ + dns_name_format(name, zname, DNS_NAME_FORMATSIZE); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, + DNS_LOGMODULE_MASTER, ISC_LOG_DEBUG(3), +@@ -714,7 +755,10 @@ dns_catz_add_zone(dns_catz_zones_t *catzs, const dns_name_t *name, + dns_catz_zone_t * + dns_catz_get_zone(dns_catz_zones_t *catzs, const dns_name_t *name) { + isc_result_t result; +- dns_catz_zone_t *found; ++ dns_catz_zone_t *found = NULL; ++ ++ REQUIRE(DNS_CATZ_ZONES_VALID(catzs)); ++ REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC)); + + result = isc_ht_find(catzs->zones, name->ndata, name->length, + (void **) &found); +@@ -726,6 +770,7 @@ dns_catz_get_zone(dns_catz_zones_t *catzs, const dns_name_t *name) { + + void + dns_catz_catzs_attach(dns_catz_zones_t *catzs, dns_catz_zones_t **catzsp) { ++ REQUIRE(DNS_CATZ_ZONES_VALID(catzs)); + REQUIRE(catzsp != NULL && *catzsp == NULL); + + isc_refcount_increment(&catzs->refs, NULL); +@@ -745,7 +790,6 @@ dns_catz_zone_detach(dns_catz_zone_t **zonep) { + isc_result_t result; + dns_catz_zone_t *zone; + isc_ht_iter_t *iter = NULL; +- isc_mem_t *mctx; + unsigned int refs; + + REQUIRE(zonep != NULL && *zonep != NULL); +@@ -754,6 +798,7 @@ dns_catz_zone_detach(dns_catz_zone_t **zonep) { + *zonep = NULL; + isc_refcount_decrement(&zone->refs, &refs); + 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); +@@ -761,7 +806,7 @@ dns_catz_zone_detach(dns_catz_zone_t **zonep) { + result == ISC_R_SUCCESS; + result = isc_ht_iter_delcurrent_next(iter)) + { +- dns_catz_entry_t *entry; ++ dns_catz_entry_t *entry = NULL; + + isc_ht_iter_current(iter, (void **) &entry); + dns_catz_entry_detach(zone, &entry); +@@ -773,7 +818,7 @@ dns_catz_zone_detach(dns_catz_zone_t **zonep) { + INSIST(isc_ht_count(zone->entries) == 0); + isc_ht_destroy(&zone->entries); + } +- mctx = zone->catzs->mctx; ++ zone->magic = 0; + isc_timer_detach(&zone->updatetimer); + isc_refcount_destroy(&zone->refs); + if (zone->db_registered == ISC_TRUE) { +@@ -798,22 +843,21 @@ dns_catz_zone_detach(dns_catz_zone_t **zonep) { + } + + void +-dns_catz_catzs_detach(dns_catz_zones_t ** catzsp) { ++dns_catz_catzs_detach(dns_catz_zones_t **catzsp) { + dns_catz_zones_t *catzs; + isc_ht_iter_t *iter = NULL; + isc_result_t result; + unsigned int refs; +- dns_catz_zone_t *zone; +- + + REQUIRE(catzsp != NULL); + catzs = *catzsp; +- REQUIRE(catzs != NULL); ++ REQUIRE(DNS_CATZ_ZONES_VALID(catzs)); + + *catzsp = NULL; + isc_refcount_decrement(&catzs->refs, &refs); + + if (refs == 0) { ++ catzs->magic = 0; + DESTROYLOCK(&catzs->lock); + if (catzs->zones != NULL) { + result = isc_ht_iter_create(catzs->zones, &iter); +@@ -821,6 +865,7 @@ dns_catz_catzs_detach(dns_catz_zones_t ** catzsp) { + for (result = isc_ht_iter_first(iter); + result == ISC_R_SUCCESS;) + { ++ dns_catz_zone_t *zone = NULL; + isc_ht_iter_current(iter, (void **) &zone); + result = isc_ht_iter_delcurrent_next(iter); + dns_catz_zone_detach(&zone); +@@ -878,9 +923,9 @@ catz_process_zones(dns_catz_zone_t *zone, dns_rdataset_t *value, + dns_label_t mhash; + dns_name_t opt; + +- REQUIRE(zone != NULL); ++ REQUIRE(DNS_CATZ_ZONE_VALID(zone)); + REQUIRE(DNS_RDATASET_VALID(value)); +- REQUIRE(name != NULL); ++ REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC)); + + if (value->rdclass != dns_rdataclass_in) + return (ISC_R_FAILURE); +@@ -971,7 +1016,7 @@ catz_process_version(dns_catz_zone_t *zone, dns_rdataset_t *value) { + isc_uint32_t tversion; + char t[16]; + +- REQUIRE(zone != NULL); ++ REQUIRE(DNS_CATZ_ZONE_VALID(zone)); + REQUIRE(DNS_RDATASET_VALID(value)); + + if (value->rdclass != dns_rdataclass_in || +@@ -1036,11 +1081,11 @@ catz_process_masters(dns_catz_zone_t *zone, dns_ipkeylist_t *ipkl, + unsigned int rcount; + unsigned int i; + +- REQUIRE(zone != NULL); ++ REQUIRE(DNS_CATZ_ZONE_VALID(zone)); + REQUIRE(ipkl != NULL); + REQUIRE(DNS_RDATASET_VALID(value)); + REQUIRE(dns_rdataset_isassociated(value)); +- REQUIRE(name != NULL); ++ REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC)); + + mctx = zone->catzs->mctx; + memset(&rdata_a, 0, sizeof(rdata_a)); +@@ -1220,7 +1265,7 @@ catz_process_apl(dns_catz_zone_t *zone, isc_buffer_t **aclbp, + isc_buffer_t *aclb = NULL; + unsigned char buf[256]; /* larger than INET6_ADDRSTRLEN */ + +- REQUIRE(zone != NULL); ++ REQUIRE(DNS_CATZ_ZONE_VALID(zone)); + REQUIRE(aclbp != NULL); + REQUIRE(*aclbp == NULL); + REQUIRE(DNS_RDATASET_VALID(value)); +@@ -1297,9 +1342,10 @@ catz_process_zones_suboption(dns_catz_zone_t *zone, dns_rdataset_t *value, + dns_name_t prefix; + catz_opt_t opt; + +- REQUIRE(zone != NULL); ++ REQUIRE(DNS_CATZ_ZONE_VALID(zone)); + REQUIRE(mhash != NULL); + REQUIRE(DNS_RDATASET_VALID(value)); ++ REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC)); + + if (name->labels == 0) + return (ISC_R_FAILURE); +@@ -1355,8 +1401,8 @@ catz_process_value(dns_catz_zone_t *zone, dns_name_t *name, + dns_name_t prefix; + catz_opt_t opt; + +- REQUIRE(zone != NULL); +- REQUIRE(name != NULL); ++ REQUIRE(DNS_CATZ_ZONE_VALID(zone)); ++ REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC)); + REQUIRE(DNS_RDATASET_VALID(rdataset)); + + dns_name_getlabel(name, name->labels - 1, &option); +@@ -1401,8 +1447,9 @@ dns_catz_update_process(dns_catz_zones_t *catzs, dns_catz_zone_t *zone, + dns_rdata_soa_t soa; + dns_name_t prefix; + +- REQUIRE(catzs != NULL); +- REQUIRE(zone != NULL); ++ REQUIRE(DNS_CATZ_ZONES_VALID(catzs)); ++ REQUIRE(DNS_CATZ_ZONE_VALID(zone)); ++ REQUIRE(ISC_MAGIC_VALID(src_name, DNS_NAME_MAGIC)); + + nrres = dns_name_fullcompare(src_name, &zone->name, &order, &nlabels); + if (nrres == dns_namereln_equal) { +@@ -1446,7 +1493,7 @@ dns_catz_generate_masterfilename(dns_catz_zone_t *zone, dns_catz_entry_t *entry, + isc_result_t result; + size_t rlen; + +- REQUIRE(zone != NULL); ++ REQUIRE(DNS_CATZ_ZONE_VALID(zone)); + REQUIRE(entry != NULL); + REQUIRE(buffer != NULL && *buffer != NULL); + +@@ -1523,7 +1570,7 @@ dns_catz_generate_zonecfg(dns_catz_zone_t *zone, dns_catz_entry_t *entry, + char pbuf[sizeof("65535")]; /* used both for port number and DSCP */ + char zname[DNS_NAME_FORMATSIZE]; + +- REQUIRE(zone != NULL); ++ REQUIRE(DNS_CATZ_ZONE_VALID(zone)); + REQUIRE(entry != NULL); + REQUIRE(buf != NULL && *buf == NULL); + +@@ -1635,7 +1682,7 @@ dns_catz_update_taskaction(isc_task_t *task, isc_event_t *event) { + + REQUIRE(event != NULL); + zone = event->ev_arg; +- REQUIRE(zone != NULL); ++ REQUIRE(DNS_CATZ_ZONE_VALID(zone)); + + LOCK(&zone->catzs->lock); + zone->updatepending = ISC_FALSE; +@@ -1747,7 +1794,7 @@ dns_catz_update_from_db(dns_db_t *db, dns_catz_zones_t *catzs) { + isc_uint32_t vers; + + REQUIRE(DNS_DB_VALID(db)); +- REQUIRE(catzs != NULL); ++ REQUIRE(DNS_CATZ_ZONES_VALID(catzs)); + + /* + * Create a new catz in the same context as current catz. +@@ -1920,9 +1967,8 @@ void + dns_catz_prereconfig(dns_catz_zones_t *catzs) { + isc_result_t result; + isc_ht_iter_t *iter = NULL; +- dns_catz_zone_t *zone; + +- REQUIRE(catzs != NULL); ++ REQUIRE(DNS_CATZ_ZONES_VALID(catzs)); + + result = isc_ht_iter_create(catzs->zones, &iter); + INSIST(result == ISC_R_SUCCESS); +@@ -1930,6 +1976,7 @@ dns_catz_prereconfig(dns_catz_zones_t *catzs) { + result == ISC_R_SUCCESS; + result = isc_ht_iter_next(iter)) + { ++ dns_catz_zone_t *zone = NULL; + isc_ht_iter_current(iter, (void **) &zone); + zone->active = ISC_FALSE; + } +@@ -1942,7 +1989,8 @@ dns_catz_postreconfig(dns_catz_zones_t *catzs) { + isc_result_t result; + dns_catz_zone_t *newzone = NULL; + isc_ht_iter_t *iter = NULL; +- dns_catz_zone_t *zone; ++ ++ REQUIRE(DNS_CATZ_ZONES_VALID(catzs)); + + LOCK(&catzs->lock); + result = isc_ht_iter_create(catzs->zones, &iter); +@@ -1950,6 +1998,8 @@ dns_catz_postreconfig(dns_catz_zones_t *catzs) { + for (result = isc_ht_iter_first(iter); + result == ISC_R_SUCCESS;) + { ++ dns_catz_zone_t *zone = NULL; ++ + isc_ht_iter_current(iter, (void **) &zone); + if (zone->active == ISC_FALSE) { + char cname[DNS_NAME_FORMATSIZE]; +@@ -1985,5 +2035,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)); + } +diff --git a/lib/dns/include/dns/catz.h b/lib/dns/include/dns/catz.h +index 6345e1e..789ad54 100644 +--- a/lib/dns/include/dns/catz.h ++++ b/lib/dns/include/dns/catz.h +@@ -75,7 +75,7 @@ dns_catz_options_init(dns_catz_options_t *options); + * Initialize 'options' to NULL values. + * + * Requires: +- * \li options to be non NULL ++ * \li 'options' to be non NULL. + */ + + void +@@ -84,20 +84,20 @@ dns_catz_options_free(dns_catz_options_t *options, isc_mem_t *mctx); + * Free 'options' contents into 'mctx'. ('options' itself is not freed.) + * + * Requires: +- * \li options to be non NULL +- * \li mctx to be a valid memory context ++ * \li 'options' to be non NULL. ++ * \li 'mctx' to be a valid memory context. + */ + + isc_result_t + dns_catz_options_copy(isc_mem_t *mctx, const dns_catz_options_t *opts, + dns_catz_options_t *nopts); + /*%< +- * Duplicate 'opts' into 'nopts', allocating space from 'mctx' ++ * Duplicate 'opts' into 'nopts', allocating space from 'mctx'. + * + * Requires: +- * \li 'mctx' to be a valid memory context +- * \li 'options' to be non NULL and valid options +- * \li 'nopts' to be non NULL ++ * \li 'mctx' to be a valid memory context. ++ * \li 'options' to be non NULL and valid options. ++ * \li 'nopts' to be non NULL. + */ + + isc_result_t +@@ -107,9 +107,9 @@ dns_catz_options_setdefault(isc_mem_t *mctx, const dns_catz_options_t *defaults, + * Replace empty values in 'opts' with values from 'defaults' + * + * Requires: +- * \li mctx to be a valid memory context +- * \li defaults to be non NULL and valid options +- * \li opts to be non NULL ++ * \li 'mctx' to be a valid memory context. ++ * \li 'defaults' to be non NULL and valid options. ++ * \li 'opts' to be non NULL. + */ + + dns_name_t * +@@ -118,10 +118,10 @@ dns_catz_entry_getname(dns_catz_entry_t *entry); + * Get domain name for 'entry' + * + * Requires: +- * \li entry to be non NULL ++ * \li 'entry' to be non NULL. + * + * Returns: +- * \li domain name for entry ++ * \li domain name for entry. + */ + + isc_result_t +@@ -131,9 +131,9 @@ dns_catz_entry_new(isc_mem_t *mctx, const dns_name_t *domain, + * Allocate a new catz_entry on 'mctx', with the name 'domain' + * + * Requires: +- * \li mctx to be a valid memory context +- * \li domain to be valid dns_name or NULL +- * \li nentryp to be non NULL, *nentryp to be NULL ++ * \li 'mctx' to be a valid memory context. ++ * \li 'domain' to be valid dns_name or NULL. ++ * \li 'nentryp' to be non NULL, *nentryp to be NULL. + * + * Returns: + * \li ISC_R_SUCCESS on success +@@ -147,9 +147,9 @@ dns_catz_entry_copy(dns_catz_zone_t *zone, const dns_catz_entry_t *entry, + * Allocate a new catz_entry and deep copy 'entry' into 'nentryp'. + * + * Requires: +- * \li mctx to be a valid memory context +- * \li entry to be non NULL +- * \li nentryp to be non NULL, *nentryp to be NULL ++ * \li 'mctx' to be a valid memory context. ++ * \li 'entry' to be non NULL. ++ * \li 'nentryp' to be non NULL, *nentryp to be NULL. + * + * Returns: + * \li ISC_R_SUCCESS on success +@@ -162,8 +162,8 @@ dns_catz_entry_attach(dns_catz_entry_t *entry, dns_catz_entry_t **entryp); + * Attach an entry + * + * Requires: +- * \li entry is not NULL +- * \li entryp is not NULL, *entryp is NULL ++ * \li 'entry' is a valid dns_catz_entry_t. ++ * \li 'entryp' is not NULL and '*entryp' is NULL. + */ + + void +@@ -172,8 +172,8 @@ dns_catz_entry_detach(dns_catz_zone_t *zone, dns_catz_entry_t **entryp); + * Detach an entry, free if no further references + * + * Requires: +- * \li zone is not NULL +- * \li entryp is not NULL, *entryp is not NULL ++ * \li 'zone' is a valid dns_catz_zone_t. ++ * \li 'entryp' is not NULL and '*entryp' is not NULL. + */ + + isc_boolean_t +@@ -181,6 +181,9 @@ dns_catz_entry_validate(const dns_catz_entry_t *entry); + /*%< + * Validate whether entry is correct. + * (NOT YET IMPLEMENTED: always returns true) ++ * ++ * Requires: ++ *\li 'entry' is a valid dns_catz_entry_t. + */ + + isc_boolean_t +@@ -189,12 +192,12 @@ dns_catz_entry_cmp(const dns_catz_entry_t *ea, const dns_catz_entry_t *eb); + * Deep compare two entries + * + * Requires: +- * \li ea is not NULL +- * \li eb is not NULL ++ * \li 'ea' is a valid dns_catz_entry_t. ++ * \li 'eb' is a valid dns_catz_entry_t. + * + * Returns: +- * \li ISC_TRUE if entries are the same +- * \li ISC_FALSE if the entries differ ++ * \li 'ISC_TRUE' if entries are the same. ++ * \li 'ISC_FALSE' if the entries differ. + */ + + void +@@ -203,8 +206,8 @@ dns_catz_zone_attach(dns_catz_zone_t *zone, dns_catz_zone_t **zonep); + * Attach a catzone + * + * Requires: +- * \li zone is not NULL +- * \li zonep is not NULL, *zonep is NULL ++ * \li 'zone' is a valid dns_catz_zone_t. ++ * \li 'zonep' is not NULL and '*zonep' is NULL. + */ + + void +@@ -213,7 +216,7 @@ dns_catz_zone_detach(dns_catz_zone_t** zonep); + * Detach a zone, free if no further references + * + * Requires: +- * \li zonep is not NULL, *zonep is not NULL ++ * \li 'zonep' is not NULL and '*zonep' is not NULL. + */ + + isc_result_t +@@ -223,9 +226,9 @@ dns_catz_new_zone(dns_catz_zones_t *catzs, dns_catz_zone_t **zonep, + * Allocate a new catz zone on catzs mctx + * + * Requires: +- * \li catzs is not NULL +- * \li zonep is not NULL, *zonep is NULL +- * \li name is not NULL ++ * \li 'catzs' is a valid dns_catz_zones_t. ++ * \li 'zonep' is not NULL and '*zonep' is NULL. ++ * \li 'name' is a valid dns_name_t. + * + */ + +@@ -235,7 +238,7 @@ dns_catz_zone_getname(dns_catz_zone_t *zone); + * Get catalog zone name + * + * Requires: +- * \li zone is not NULL ++ * \li 'zone' is a valid dns_catz_zone_t. + */ + + dns_catz_options_t * +@@ -244,7 +247,7 @@ dns_catz_zone_getdefoptions(dns_catz_zone_t *zone); + * Get default member zone options for catalog zone 'zone' + * + * Requires: +- * \li zone is not NULL ++ * \li 'zone' is a valid dns_catz_zone_t. + */ + + void +@@ -254,7 +257,7 @@ dns_catz_zone_resetdefoptions(dns_catz_zone_t *zone); + * the default values. + * + * Requires: +- * \li zone is not NULL ++ * \li 'zone' is a valid dns_catz_zone_t. + */ + + isc_result_t +@@ -264,8 +267,8 @@ dns_catz_zones_merge(dns_catz_zone_t *target, dns_catz_zone_t *newzone); + * (from zone->catzs->zmm) for appropriate member zones. + * + * Requires: +- * \li orig is not NULL +- * \li newzone is not NULL, *newzone is not NULL ++ * \li 'orig' is a valid dns_catz_zone_t. ++ * \li 'newzone' is not NULL and '*newzone' is not NULL. + * + */ + +@@ -277,10 +280,10 @@ dns_catz_update_process(dns_catz_zones_t *catzs, dns_catz_zone_t *zone, + * record name. + * + * Requires: +- * \li catzs is not NULL +- * \li zone is not NULL +- * \li src_name is not NULL +- * \li rdataset is valid ++ * \li 'catzs' is a valid dns_catz_zones_t. ++ * \li 'zone' is a valid dns_catz_zone_t. ++ * \li 'src_name' is a valid dns_name_t. ++ * \li 'rdataset' is valid rdataset. + */ + + isc_result_t +@@ -294,9 +297,9 @@ dns_catz_generate_masterfilename(dns_catz_zone_t *zone, dns_catz_entry_t *entry, + * __catz__unique_hash_generated_from_the_above.db + * + * Requires: +- * \li zone is not NULL +- * \li entry is not NULL +- * \li buffer is not NULL and *buffer is not NULL ++ * \li 'zone' is a valid dns_catz_zone_t. ++ * \li 'entry' is a valid dns_catz_entry_t. ++ * \li 'buffer' is not NULL and '*buffer' is not NULL. + */ + + isc_result_t +@@ -307,10 +310,9 @@ dns_catz_generate_zonecfg(dns_catz_zone_t *zone, dns_catz_entry_t *entry, + * it into *buf. buf might be reallocated. + * + * Requires: +- * \li zone is not NULL +- * \li entry is not NULL +- * \li buf is not NULL +- * \li *buf is NULL ++ * \li 'zone' is a valid dns_catz_zone_t. ++ * \li 'entry' is a valid dns_catz_entry_t. ++ * \li 'buf' is not NULL and '*buf' is NULL. + * + */ + +@@ -337,8 +339,8 @@ dns_catz_new_zones(dns_catz_zones_t **catzsp, dns_catz_zonemodmethods_t *zmm, + * for a view. + * + * Requires: +- * \li catzsp is not NULL, *catzsp is NULL +- * \li zmm is not NULL ++ * \li 'catzsp' is not NULL and '*catzsp' is NULL. ++ * \li 'zmm' is not NULL. + * + */ + +@@ -349,9 +351,9 @@ dns_catz_add_zone(dns_catz_zones_t *catzs, const dns_name_t *name, + * Allocate a new catz named 'name' and put it in 'catzs' collection. + * + * Requires: +- * \li catzs is not NULL +- * \li name is not NULL +- * \li zonep is not NULL, *zonep is NULL ++ * \li 'catzs' is a valid dns_catz_zones_t. ++ * \li 'name' is a valid dns_name_t. ++ * \li 'zonep' is not NULL and *zonep is NULL. + * + */ + +@@ -361,37 +363,37 @@ dns_catz_get_zone(dns_catz_zones_t *catzs, const dns_name_t *name); + * Returns a zone named 'name' from collection 'catzs' + * + * Requires: +- * \li catzs is not NULL +- * \li name is not NULL ++ * \li 'catzs' is a valid dns_catz_zones_t. ++ * \li 'name' is a valid dns_name_t. + */ + + void + dns_catz_catzs_attach(dns_catz_zones_t *catzs, dns_catz_zones_t **catzsp); + /*%< +- * Attach 'catzs' to 'catzsp' ++ * Attach 'catzs' to 'catzsp'. + * + * Requires: +- * \li catzs is not NULL +- * \li catzsp is not NULL, *catzsp is NULL ++ * \li 'catzs' is a valid dns_catz_zones_t. ++ * \li 'catzsp' is not NULL and *catzsp is NULL. + */ + + void + dns_catz_catzs_detach(dns_catz_zones_t **catzsp); + /*%< +- * Detach 'catzsp', free if no further references ++ * Detach 'catzsp', free if no further references. + * + * Requires: +- * \li catzsp is not NULL, *catzsp is not NULL ++ * \li 'catzsp' is not NULL and *catzsp is not NULL. + */ + + void + dns_catz_catzs_set_view(dns_catz_zones_t *catzs, dns_view_t *view); + /*%< +- * Set a view for catzs ++ * Set a view for 'catzs'. + * + * Requires: +- * \li catzs is not NULL +- * \li catzs->view is NULL or catzs->view == view ++ * \li 'catzs' is a valid dns_catz_zones_t. ++ * \li 'catzs->view' is NULL or 'catzs->view' == 'view'. + */ + + +@@ -406,17 +408,17 @@ dns_catz_dbupdate_callback(dns_db_t *db, void *fn_arg); + * If there is an update scheduled it replaces old db version with a new one. + * + * Requires: +- * \li db is a valid database +- * \li fn_arg is not NULL (casted to dns_catz_zones_t*) ++ * \li 'db' is a valid database. ++ * \li 'fn_arg' is not NULL (casted to dns_catz_zones_t*). + */ + + void + dns_catz_update_taskaction(isc_task_t *task, isc_event_t *event); + /*%< +- * Task that launches dns_catz_update_from_db ++ * Task that launches dns_catz_update_from_db. + * + * Requires: +- * \li event is not NULL ++ * \li 'event' is not NULL. + */ + + void +@@ -427,8 +429,8 @@ dns_catz_update_from_db(dns_db_t *db, dns_catz_zones_t *catzs); + * then merges new catz into old catz. + * + * Requires: +- * \li db is a valid DB +- * \li catzs is not NULL ++ * \li 'db' is a valid DB. ++ * \li 'catzs' is a valid dns_catz_zones_t. + * + */ + +@@ -438,7 +440,7 @@ dns_catz_prereconfig(dns_catz_zones_t *catzs); + * Called before reconfig, clears 'active' flag on all the zones in set + * + * Requires: +- * \li catzs is not NULL ++ * \li 'catzs' is a valid dns_catz_zones_t. + * + */ + +@@ -449,7 +451,7 @@ dns_catz_postreconfig(dns_catz_zones_t *catzs); + * inactive and force reload of those with changed configuration. + * + * Requires: +- * \li catzs is not NULL ++ * \li 'catzs' is a valid dns_catz_zones_t. + */ + + isc_result_t +@@ -457,6 +459,10 @@ dns_catz_get_iterator(dns_catz_zone_t *catz, isc_ht_iter_t **itp); + /*%< + * Get the hashtable iterator on catalog zone members, point '*itp' to it. + * ++ * Requires: ++ * \li 'catzs' is a valid dns_catz_zones_t. ++ * \li 'itp' is not NULL and '*itp' is NULL. ++ * + * Returns: + * \li #ISC_R_SUCCESS -- success + * \li Any other value -- failure +diff --git a/lib/isc/ht.c b/lib/isc/ht.c +index 5719394..a86e3ec 100644 +--- a/lib/isc/ht.c ++++ b/lib/isc/ht.c +@@ -161,7 +161,7 @@ isc_ht_find(const isc_ht_t *ht, const unsigned char *key, + + REQUIRE(ISC_HT_VALID(ht)); + REQUIRE(key != NULL && keysize > 0); +- REQUIRE(valuep != NULL); ++ REQUIRE(valuep == NULL || *valuep == NULL); + + hash = isc_hash_function(key, keysize, ISC_TRUE, NULL); + node = ht->table[hash & ht->mask]; +@@ -325,6 +325,8 @@ void + isc_ht_iter_current(isc_ht_iter_t *it, void **valuep) { + REQUIRE(it != NULL); + REQUIRE(it->cur != NULL); ++ REQUIRE(valuep != NULL && *valuep == NULL); ++ + *valuep = it->cur->value; + } + +@@ -333,6 +335,8 @@ 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); ++ + *key = it->cur->key; + *keysize = it->cur->keysize; + } +diff --git a/lib/isc/include/isc/ht.h b/lib/isc/include/isc/ht.h +index 6ca4481..ea9eab7 100644 +--- a/lib/isc/include/isc/ht.h ++++ b/lib/isc/include/isc/ht.h +@@ -25,10 +25,9 @@ typedef struct isc_ht_iter isc_ht_iter_t; + * Initialize hashtable at *htp, using memory context and size of (1<=1 && bits <=32 ++ *\li 'htp' is not NULL and '*htp' is NULL. ++ *\li 'mctx' is a valid memory context. ++ *\li 'bits' >=1 and 'bits' <=32 + * + * Returns: + *\li #ISC_R_NOMEMORY -- not enough memory to create pool +@@ -41,7 +40,7 @@ isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, isc_uint8_t bits); + * Destroy hashtable, freeing everything + * + * Requires: +- * \li *htp is valid hashtable ++ * \li '*htp' is valid hashtable + */ + void + isc_ht_destroy(isc_ht_t **htp); +@@ -51,7 +50,7 @@ isc_ht_destroy(isc_ht_t **htp); + * set its value to 'value' + * + * Requires: +- *\li ht is a valid hashtable ++ *\li 'ht' is a valid hashtable + * + * Returns: + *\li #ISC_R_NOMEMORY -- not enough memory to create pool +@@ -79,6 +78,7 @@ isc_ht_find(const isc_ht_t *ht, const unsigned char *key, + + /*% + * Delete node from hashtable ++ * + * Requires: + *\li ht is a valid hashtable + * +@@ -91,12 +91,19 @@ isc_ht_delete(isc_ht_t *ht, const unsigned char *key, isc_uint32_t keysize); + + /*% + * Create an iterator for the hashtable; point '*itp' to it. ++ * ++ * Requires: ++ *\li 'ht' is a valid hashtable ++ *\li 'itp' is non NULL and '*itp' is NULL. + */ + isc_result_t + isc_ht_iter_create(isc_ht_t *ht, isc_ht_iter_t **itp); + + /*% + * Destroy the iterator '*itp', set it to NULL ++ * ++ * Requires: ++ *\li 'itp' is non NULL and '*itp' is non NULL. + */ + void + isc_ht_iter_destroy(isc_ht_iter_t **itp); +@@ -104,6 +111,9 @@ isc_ht_iter_destroy(isc_ht_iter_t **itp); + /*% + * Set an iterator to the first entry. + * ++ * Requires: ++ *\li 'it' is non NULL. ++ * + * Returns: + * \li #ISC_R_SUCCESS -- success + * \li #ISC_R_NOMORE -- no data in the hashtable +@@ -114,6 +124,9 @@ isc_ht_iter_first(isc_ht_iter_t *it); + /*% + * Set an iterator to the next entry. + * ++ * Requires: ++ *\li 'it' is non NULL. ++ * + * Returns: + * \li #ISC_R_SUCCESS -- success + * \li #ISC_R_NOMORE -- end of hashtable reached +@@ -124,6 +137,9 @@ isc_ht_iter_next(isc_ht_iter_t *it); + /*% + * Delete current entry and set an iterator to the next entry. + * ++ * Requires: ++ *\li 'it' is non NULL. ++ * + * Returns: + * \li #ISC_R_SUCCESS -- success + * \li #ISC_R_NOMORE -- end of hashtable reached +@@ -134,6 +150,10 @@ isc_ht_iter_delcurrent_next(isc_ht_iter_t *it); + + /*% + * Set 'value' to the current value under the iterator ++ * ++ * Requires: ++ *\li 'it' is non NULL. ++ *\li 'valuep' is non NULL and '*valuep' is NULL. + */ + void + isc_ht_iter_current(isc_ht_iter_t *it, void **valuep); +@@ -141,6 +161,11 @@ isc_ht_iter_current(isc_ht_iter_t *it, void **valuep); + /*% + * Set 'key' and 'keysize to the current key and keysize for the value + * under the iterator ++ * ++ * Requires: ++ *\li 'it' is non NULL. ++ *\li 'key' is non NULL and '*key' is NULL. ++ *\li 'keysize' is non NULL. + */ + void + isc_ht_iter_currentkey(isc_ht_iter_t *it, unsigned char **key, size_t *keysize); +diff --git a/lib/isc/tests/ht_test.c b/lib/isc/tests/ht_test.c +index 4b069ef..11ffb79 100644 +--- a/lib/isc/tests/ht_test.c ++++ b/lib/isc/tests/ht_test.c +@@ -194,11 +194,9 @@ static void test_ht_iterator() { + isc_mem_t *mctx = NULL; + isc_ht_iter_t * iter = NULL; + uintptr_t i; +- void *v; + uintptr_t count = 10000; + isc_uint32_t walked; + unsigned char key[16]; +- unsigned char *tkey; + size_t tksize; + + result = isc_mem_createx2(0, 0, default_memalloc, default_memfree, +@@ -227,6 +225,9 @@ static void test_ht_iterator() { + result == ISC_R_SUCCESS; + result = isc_ht_iter_next(iter)) + { ++ unsigned char *tkey = NULL; ++ void *v = NULL; ++ + isc_ht_iter_current(iter, &v); + isc_ht_iter_currentkey(iter, &tkey, &tksize); + ATF_REQUIRE_EQ(tksize, 16); +@@ -243,6 +244,9 @@ static void test_ht_iterator() { + walked = 0; + result = isc_ht_iter_first(iter); + while (result == ISC_R_SUCCESS) { ++ unsigned char *tkey = NULL; ++ void *v = NULL; ++ + isc_ht_iter_current(iter, &v); + isc_ht_iter_currentkey(iter, &tkey, &tksize); + ATF_REQUIRE_EQ(tksize, 16); +@@ -264,6 +268,9 @@ static void test_ht_iterator() { + walked = 0; + result = isc_ht_iter_first(iter); + while (result == ISC_R_SUCCESS) { ++ unsigned char *tkey = NULL; ++ void *v = NULL; ++ + isc_ht_iter_current(iter, &v); + isc_ht_iter_currentkey(iter, &tkey, &tksize); + ATF_REQUIRE_EQ(tksize, 16); +-- +2.45.0 + diff --git a/SPECS/bind.spec b/SPECS/bind.spec index e5ddfe3..704662e 100644 --- a/SPECS/bind.spec +++ b/SPECS/bind.spec @@ -64,7 +64,7 @@ Summary: The Berkeley Internet Name Domain (BIND) DNS (Domain Name System) serv Name: bind License: MPLv2.0 Version: 9.11.4 -Release: 26%{?PATCHVER:.%{PATCHVER}}%{?PREVER:.%{PREVER}}%{?dist}.15 +Release: 26%{?PATCHVER:.%{PATCHVER}}%{?PREVER:.%{PREVER}}%{?dist}.16 Epoch: 32 Url: http://www.isc.org/products/BIND/ # @@ -190,6 +190,10 @@ Patch203: bind-9.11-CVE-2021-25220-test.patch Patch204: bind-9.11-CVE-2021-25220.patch Patch205: bind-9.11-CVE-2023-2828.patch Patch206: bind-9.11-CVE-2023-3341.patch +Patch207: bind-9.11.4-CVE-2023-50387.patch +Patch208: bind-9.11.4-missing-DBC-checks.patch +Patch209: bind-9.11.4-CVE-2023-4408.patch +Patch210: bind-9.11.4-CVE-2023-2828-fixup.patch # SDB patches Patch11: bind-9.3.2b2-sdbsrc.patch @@ -572,6 +576,10 @@ are used for building ISC DHCP. %patch204 -p1 -b .CVE-2021-25220 %patch205 -p1 -b .CVE-2023-2828 %patch206 -p1 -b .CVE-2023-3341 +%patch207 -p1 -b .CVE-2023-50387+50868 +%patch208 -p1 -b .DBC-checks +%patch209 -p1 -b .CVE-2023-4408 +%patch210 -p1 -b .CVE-2023-2828-fixup # Override upstream builtin keys cp -fp %{SOURCE29} bind.keys @@ -1553,6 +1561,13 @@ rm -rf ${RPM_BUILD_ROOT} %changelog +* Tue Apr 16 2024 Stepan Broz - 32:9.11.4-26.P2.16 +- Prevent increased CPU consumption in DNSSEC validator (CVE-2023-50387 + CVE-2023-50868) +- Add missing design by contract tests to dns_catz* +- Speed up parsing of DNS messages with many different names (CVE-2023-4408) +- Do not use header_prev in expire_lru_headers + * Mon Sep 25 2023 Stepan Broz - 32:9.11.4-26.P2.15 - Limit the amount of recursion possible in control channel (CVE-2023-3341)