ee1d55
From 623bb196ca62f513345a93fffd77a60704261f38 Mon Sep 17 00:00:00 2001
ee1d55
From: Stepan Broz <sbroz@redhat.com>
ee1d55
Date: Mon, 6 May 2024 19:40:19 +0200
ee1d55
Subject: [PATCH] Use hashtable when parsing a message
ee1d55
ee1d55
When parsing messages use a hashtable instead of a linear search to reduce
ee1d55
the amount of work done in findname when there's more than one name in
ee1d55
the section.
ee1d55
ee1d55
There are two hashtables:
ee1d55
ee1d55
1) hashtable for owner names - that's constructed for each section when we
ee1d55
hit the second name in the section and destroyed right after parsing
ee1d55
that section;
ee1d55
ee1d55
2) per-name hashtable - for each name in the section, we construct a new
ee1d55
hashtable for that name if there are more than one rdataset for that
ee1d55
particular name.
ee1d55
ee1d55
(cherry picked from commit b8a96317544c7b310b4f74360825a87b6402ddc2)
ee1d55
(cherry picked from commit 0ceed03ebea395da1a39ad1cb39205ce75a3f768)
ee1d55
ee1d55
Backport isc_ht API changes from BIND 9.18
ee1d55
ee1d55
To prevent allocating large hashtable in dns_message, we need to
ee1d55
backport the improvements to isc_ht API from BIND 9.18+ that includes
ee1d55
support for case insensitive keys and incremental rehashing of the
ee1d55
hashtables.
ee1d55
ee1d55
(cherry picked from commit a4baf324159ec3764195c949cb56c861d9f173ff)
ee1d55
(cherry picked from commit 2fc28056b33018f7f78b625409eb44c32d5c9b11)
ee1d55
ee1d55
fix a message parsing regression
ee1d55
ee1d55
the fix for CVE-2023-4408 introduced a regression in the message
ee1d55
parser, which could cause a crash if duplicate rdatasets were found
ee1d55
in the question section. this commit ensures that rdatasets are
ee1d55
correctly disassociated and freed when this occurs.
ee1d55
ee1d55
(cherry picked from commit 4c19d35614f8cd80d8748156a5bad361e19abc28)
ee1d55
(cherry picked from commit 98ab8c81cc7739dc220aa3f50efa3061774de8ba)
ee1d55
ee1d55
fix another message parsing regression
ee1d55
ee1d55
The fix for CVE-2023-4408 introduced a regression in the message
ee1d55
parser, which could cause a crash if an rdata type that can only
ee1d55
occur in the question was found in another section.
ee1d55
ee1d55
(cherry picked from commit 510f1de8a6add516b842a55750366944701d3d9a)
ee1d55
(cherry picked from commit bbbcaf8b2ec17d2cad28841ea86078168072ae2f)
ee1d55
ee1d55
Apply various tweaks specific to BIND 9.11
ee1d55
ee1d55
(cherry picked from commit c6026cbbaa9d297910af350fa6cc45788cc9f397)
ee1d55
ee1d55
Fix case insensitive matching in isc_ht hash table implementation
ee1d55
ee1d55
The case insensitive matching in isc_ht was basically completely broken
ee1d55
as only the hashvalue computation was case insensitive, but the key
ee1d55
comparison was always case sensitive.
ee1d55
ee1d55
(cherry picked from commit c462d65b2fd0db362947db4a18a87df78f8d8e5d)
ee1d55
(cherry picked from commit 418b3793598a1e1c7e391bb317866d405cd03501)
ee1d55
ee1d55
Add a system test for mixed-case data for the same owner
ee1d55
ee1d55
We were missing a test where a single owner name would have multiple
ee1d55
types with a different case.  The generated RRSIGs and NSEC records will
ee1d55
then have different case than the signed records and message parser have
ee1d55
to cope with that and treat everything as the same owner.
ee1d55
ee1d55
(cherry picked from commit c8b623d87f0fb8f9cba8dea5c6a4b600953895e7)
ee1d55
(cherry picked from commit 1f9bbe1fe34b7a2c9765431e8a86b460afc9b323)
ee1d55
ee1d55
6315.   [security]      Speed up parsing of DNS messages with many different
ee1d55
                        names. (CVE-2023-4408) [GL #4234]
ee1d55
ee1d55
Fix assertion failure in nslookup/dig/mdig when message has
ee1d55
 multiple SIG(0) options.
ee1d55
ee1d55
When parsing message with DNS_MESSAGE_BESTEFFORT (used exclusively in
ee1d55
tools, never in named itself) if we hit an invalid SIG(0) in wrong
ee1d55
place we continue parsing the message, and put the sig0 in msg->sig0.
ee1d55
If we then hit another sig0 in a proper place we see that msg->sig0
ee1d55
is already 'taken' and we don't free name and rdataset, and we don't
ee1d55
set seen_problem. This causes an assertion failure.
ee1d55
This fixes that issue by setting seen_problem if we hit second sig0,
ee1d55
tsig or opt, which causes name and rdataset to be always freed.
ee1d55
ee1d55
(cherry picked from commit 51a55ddbb73f8707de3d1b8cda15c8f61585bacb)
ee1d55
(cherry picked from commit 736d8c5b80d02f10555f324abef920a7257f9f43)
ee1d55
---
ee1d55
 .../system/dnssec/ns3/secure.example.db.in    |   5 +
ee1d55
 bin/tests/system/dnssec/ns3/sign.sh           | 112 ++--
ee1d55
 bin/tests/system/dnssec/tests.sh              |  15 +
ee1d55
 lib/dns/catz.c                                |  50 +-
ee1d55
 lib/dns/include/dns/message.h                 |  39 --
ee1d55
 lib/dns/include/dns/name.h                    |   9 +-
ee1d55
 lib/dns/message.c                             | 624 +++++++++++-------
ee1d55
 lib/dns/name.c                                |   1 +
ee1d55
 lib/isc/ht.c                                  | 598 ++++++++++++-----
ee1d55
 lib/isc/include/isc/ht.h                      |  45 +-
ee1d55
 lib/isc/tests/ht_test.c                       |  75 ++-
ee1d55
 11 files changed, 1032 insertions(+), 541 deletions(-)
ee1d55
ee1d55
diff --git a/bin/tests/system/dnssec/ns3/secure.example.db.in b/bin/tests/system/dnssec/ns3/secure.example.db.in
ee1d55
index 9d310d8..3b713d8 100644
ee1d55
--- a/bin/tests/system/dnssec/ns3/secure.example.db.in
ee1d55
+++ b/bin/tests/system/dnssec/ns3/secure.example.db.in
ee1d55
@@ -44,3 +44,8 @@ rrsigonly		A	10.0.0.29
ee1d55
 cnameandkey		CNAME	@
ee1d55
 cnamenokey		CNAME	@
ee1d55
 dnameandkey		DNAME	@
ee1d55
+
ee1d55
+mixedcase		A	10.0.0.30
ee1d55
+mixedCASE		TXT	"mixed case"
ee1d55
+MIXEDcase		AAAA	2002::
ee1d55
+mIxEdCaSe		LOC	37 52 56.788 N 121 54 55.02 W 1120m 10m 100m 10m
ee1d55
diff --git a/bin/tests/system/dnssec/ns3/sign.sh b/bin/tests/system/dnssec/ns3/sign.sh
ee1d55
index 99e9b49..ce5b2ee 100644
ee1d55
--- a/bin/tests/system/dnssec/ns3/sign.sh
ee1d55
+++ b/bin/tests/system/dnssec/ns3/sign.sh
ee1d55
@@ -28,7 +28,9 @@ keyname=`$KEYGEN -q -r $RANDFILE -a RSASHA1 -b 768 -n zone $zone`
ee1d55
 
ee1d55
 cat $infile $cnameandkey.key $dnameandkey.key $keyname.key >$zonefile
ee1d55
 
ee1d55
-$SIGNER -P -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1
ee1d55
+$SIGNER -P -D -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1
ee1d55
+cat "$zonefile" "$zonefile".signed >"$zonefile".tmp
ee1d55
+mv "$zonefile".tmp "$zonefile".signed
ee1d55
 
ee1d55
 zone=bogus.example.
ee1d55
 infile=bogus.example.db.in
ee1d55
@@ -313,11 +315,11 @@ zone=rsasha512.example.
ee1d55
 infile=rsasha512.example.db.in
ee1d55
 zonefile=rsasha512.example.db
ee1d55
 
ee1d55
-keyname=`$KEYGEN -q -r $RANDFILE -a RSASHA512 -b 1024 -n zone $zone`
ee1d55
+keyname=$($KEYGEN -q -r $RANDFILE -a RSASHA512 -b 1024 -n zone $zone)
ee1d55
 
ee1d55
 cat $infile $keyname.key >$zonefile
ee1d55
 
ee1d55
-$SIGNER -P -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1
ee1d55
+$SIGNER -P -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1
ee1d55
 
ee1d55
 #
ee1d55
 # A zone with the DNSKEY set only signed by the KSK
ee1d55
@@ -326,10 +328,10 @@ zone=kskonly.example.
ee1d55
 infile=kskonly.example.db.in
ee1d55
 zonefile=kskonly.example.db
ee1d55
 
ee1d55
-kskname=`$KEYGEN -q -r $RANDFILE -fk $zone`
ee1d55
-zskname=`$KEYGEN -q -r $RANDFILE $zone`
ee1d55
+kskname=$($KEYGEN -q -r $RANDFILE -fk $zone)
ee1d55
+zskname=$($KEYGEN -q -r $RANDFILE $zone)
ee1d55
 cat $infile $kskname.key $zskname.key >$zonefile
ee1d55
-$SIGNER -x -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1
ee1d55
+$SIGNER -x -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1
ee1d55
 
ee1d55
 #
ee1d55
 # A zone with the expired signatures
ee1d55
@@ -338,10 +340,10 @@ zone=expired.example.
ee1d55
 infile=expired.example.db.in
ee1d55
 zonefile=expired.example.db
ee1d55
 
ee1d55
-kskname=`$KEYGEN -q -r $RANDFILE -fk $zone`
ee1d55
-zskname=`$KEYGEN -q -r $RANDFILE $zone`
ee1d55
+kskname=$($KEYGEN -q -r $RANDFILE -fk $zone)
ee1d55
+zskname=$($KEYGEN -q -r $RANDFILE $zone)
ee1d55
 cat $infile $kskname.key $zskname.key >$zonefile
ee1d55
-$SIGNER -P -r $RANDFILE -o $zone -s -1d -e +1h $zonefile > /dev/null 2>&1
ee1d55
+$SIGNER -P -r $RANDFILE -o $zone -s -1d -e +1h $zonefile >/dev/null 2>&1
ee1d55
 rm -f $kskname.* $zskname.*
ee1d55
 
ee1d55
 #
ee1d55
@@ -351,10 +353,10 @@ zone=update-nsec3.example.
ee1d55
 infile=update-nsec3.example.db.in
ee1d55
 zonefile=update-nsec3.example.db
ee1d55
 
ee1d55
-kskname=`$KEYGEN -q -3 -r $RANDFILE -fk $zone`
ee1d55
-zskname=`$KEYGEN -q -3 -r $RANDFILE $zone`
ee1d55
+kskname=$($KEYGEN -q -3 -r $RANDFILE -fk $zone)
ee1d55
+zskname=$($KEYGEN -q -3 -r $RANDFILE $zone)
ee1d55
 cat $infile $kskname.key $zskname.key >$zonefile
ee1d55
-$SIGNER -P -3 - -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1
ee1d55
+$SIGNER -P -3 - -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1
ee1d55
 
ee1d55
 #
ee1d55
 # A NSEC signed zone that will have auto-dnssec enabled and
ee1d55
@@ -364,12 +366,12 @@ zone=auto-nsec.example.
ee1d55
 infile=auto-nsec.example.db.in
ee1d55
 zonefile=auto-nsec.example.db
ee1d55
 
ee1d55
-kskname=`$KEYGEN -q -r $RANDFILE -fk $zone`
ee1d55
-zskname=`$KEYGEN -q -r $RANDFILE $zone`
ee1d55
-kskname=`$KEYGEN -q -r $RANDFILE -fk $zone`
ee1d55
-zskname=`$KEYGEN -q -r $RANDFILE $zone`
ee1d55
+kskname=$($KEYGEN -q -r $RANDFILE -fk $zone)
ee1d55
+zskname=$($KEYGEN -q -r $RANDFILE $zone)
ee1d55
+kskname=$($KEYGEN -q -r $RANDFILE -fk $zone)
ee1d55
+zskname=$($KEYGEN -q -r $RANDFILE $zone)
ee1d55
 cat $infile $kskname.key $zskname.key >$zonefile
ee1d55
-$SIGNER -P -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1
ee1d55
+$SIGNER -P -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1
ee1d55
 
ee1d55
 #
ee1d55
 # A NSEC3 signed zone that will have auto-dnssec enabled and
ee1d55
@@ -379,12 +381,12 @@ zone=auto-nsec3.example.
ee1d55
 infile=auto-nsec3.example.db.in
ee1d55
 zonefile=auto-nsec3.example.db
ee1d55
 
ee1d55
-kskname=`$KEYGEN -q -3 -r $RANDFILE -fk $zone`
ee1d55
-zskname=`$KEYGEN -q -3 -r $RANDFILE $zone`
ee1d55
-kskname=`$KEYGEN -q -3 -r $RANDFILE -fk $zone`
ee1d55
-zskname=`$KEYGEN -q -3 -r $RANDFILE $zone`
ee1d55
+kskname=$($KEYGEN -q -3 -r $RANDFILE -fk $zone)
ee1d55
+zskname=$($KEYGEN -q -3 -r $RANDFILE $zone)
ee1d55
+kskname=$($KEYGEN -q -3 -r $RANDFILE -fk $zone)
ee1d55
+zskname=$($KEYGEN -q -3 -r $RANDFILE $zone)
ee1d55
 cat $infile $kskname.key $zskname.key >$zonefile
ee1d55
-$SIGNER -P -3 - -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1
ee1d55
+$SIGNER -P -3 - -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1
ee1d55
 
ee1d55
 #
ee1d55
 # Secure below cname test zone.
ee1d55
@@ -447,10 +449,10 @@ zone="expiring.example."
ee1d55
 infile="expiring.example.db.in"
ee1d55
 zonefile="expiring.example.db"
ee1d55
 signedfile="expiring.example.db.signed"
ee1d55
-kskname=`$KEYGEN -q -r $RANDFILE $zone`
ee1d55
-zskname=`$KEYGEN -q -r $RANDFILE -f KSK $zone`
ee1d55
+kskname=$($KEYGEN -q -r $RANDFILE $zone)
ee1d55
+zskname=$($KEYGEN -q -r $RANDFILE -f KSK $zone)
ee1d55
 cp $infile $zonefile
ee1d55
-$SIGNER -S -r $RANDFILE -e now+1mi -o $zone $zonefile > /dev/null 2>&1
ee1d55
+$SIGNER -S -r $RANDFILE -e now+1mi -o $zone $zonefile >/dev/null 2>&1
ee1d55
 mv -f ${zskname}.private ${zskname}.private.moved
ee1d55
 mv -f ${kskname}.private ${kskname}.private.moved
ee1d55
 
ee1d55
@@ -462,12 +464,12 @@ infile="upper.example.db.in"
ee1d55
 zonefile="upper.example.db"
ee1d55
 lower="upper.example.db.lower"
ee1d55
 signedfile="upper.example.db.signed"
ee1d55
-kskname=`$KEYGEN -q -r $RANDFILE $zone`
ee1d55
-zskname=`$KEYGEN -q -r $RANDFILE -f KSK $zone`
ee1d55
+kskname=$($KEYGEN -q -r $RANDFILE $zone)
ee1d55
+zskname=$($KEYGEN -q -r $RANDFILE -f KSK $zone)
ee1d55
 cp $infile $zonefile
ee1d55
-$SIGNER -P -S -r $RANDFILE -o $zone -f $lower $zonefile > /dev/null 2>/dev/null
ee1d55
-$CHECKZONE -D upper.example $lower 2>/dev/null | \
ee1d55
-	sed '/RRSIG/s/ upper.example. / UPPER.EXAMPLE. /' > $signedfile
ee1d55
+$SIGNER -P -S -r $RANDFILE -o $zone -f $lower $zonefile >/dev/null 2>/dev/null
ee1d55
+$CHECKZONE -D upper.example $lower 2>/dev/null |
ee1d55
+	sed '/RRSIG/s/ upper.example. / UPPER.EXAMPLE. /' >$signedfile
ee1d55
 
ee1d55
 #
ee1d55
 # Check that the signer's name is in lower case when zone name is in
ee1d55
@@ -477,10 +479,10 @@ zone="LOWER.EXAMPLE."
ee1d55
 infile="lower.example.db.in"
ee1d55
 zonefile="lower.example.db"
ee1d55
 signedfile="lower.example.db.signed"
ee1d55
-kskname=`$KEYGEN -q -r $RANDFILE $zone`
ee1d55
-zskname=`$KEYGEN -q -r $RANDFILE -f KSK $zone`
ee1d55
+kskname=$($KEYGEN -q -r $RANDFILE $zone)
ee1d55
+zskname=$($KEYGEN -q -r $RANDFILE -f KSK $zone)
ee1d55
 cp $infile $zonefile
ee1d55
-$SIGNER -P -S -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1
ee1d55
+$SIGNER -P -S -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1
ee1d55
 
ee1d55
 #
ee1d55
 # Zone with signatures about to expire, and dynamic, but configured
ee1d55
@@ -490,21 +492,21 @@ zone="nosign.example."
ee1d55
 infile="nosign.example.db.in"
ee1d55
 zonefile="nosign.example.db"
ee1d55
 signedfile="nosign.example.db.signed"
ee1d55
-kskname=`$KEYGEN -q -r $RANDFILE $zone`
ee1d55
-zskname=`$KEYGEN -q -r $RANDFILE -f KSK $zone`
ee1d55
+kskname=$($KEYGEN -q -r $RANDFILE $zone)
ee1d55
+zskname=$($KEYGEN -q -r $RANDFILE -f KSK $zone)
ee1d55
 cp $infile $zonefile
ee1d55
-$SIGNER -S -r $RANDFILE -e now+1mi -o $zone $zonefile > /dev/null 2>&1
ee1d55
+$SIGNER -S -r $RANDFILE -e now+1mi -o $zone $zonefile >/dev/null 2>&1
ee1d55
 # preserve a normalized copy of the NS RRSIG for comparison later
ee1d55
-$CHECKZONE -D nosign.example nosign.example.db.signed 2>/dev/null | \
ee1d55
-        awk '$4 == "RRSIG" && $5 == "NS" {$2 = ""; print}' | \
ee1d55
-        sed 's/[ 	][ 	]*/ /g'> ../nosign.before
ee1d55
+$CHECKZONE -D nosign.example nosign.example.db.signed 2>/dev/null |
ee1d55
+	awk '$4 == "RRSIG" && $5 == "NS" {$2 = ""; print}' |
ee1d55
+	sed 's/[ 	][ 	]*/ /g' >../nosign.before
ee1d55
 
ee1d55
 #
ee1d55
 # An inline signing zone
ee1d55
 #
ee1d55
 zone=inline.example.
ee1d55
-kskname=`$KEYGEN -q -3 -r $RANDFILE -fk $zone`
ee1d55
-zskname=`$KEYGEN -q -3 -r $RANDFILE $zone`
ee1d55
+kskname=$($KEYGEN -q -3 -r $RANDFILE -fk $zone)
ee1d55
+zskname=$($KEYGEN -q -3 -r $RANDFILE $zone)
ee1d55
 
ee1d55
 #
ee1d55
 # publish a new key while deactivating another key at the same time.
ee1d55
@@ -512,13 +514,13 @@ zskname=`$KEYGEN -q -3 -r $RANDFILE $zone`
ee1d55
 zone=publish-inactive.example
ee1d55
 infile=publish-inactive.example.db.in
ee1d55
 zonefile=publish-inactive.example.db
ee1d55
-now=`date -u +%Y%m%d%H%M%S`
ee1d55
-kskname=`$KEYGEN -q -r $RANDFILE -f KSK $zone`
ee1d55
-kskname=`$KEYGEN -P $now+90s -A $now+3600s -q -r $RANDFILE -f KSK $zone`
ee1d55
-kskname=`$KEYGEN -I $now+90s -q -r $RANDFILE -f KSK $zone`
ee1d55
-zskname=`$KEYGEN -q -r $RANDFILE $zone`
ee1d55
+now=$(date -u +%Y%m%d%H%M%S)
ee1d55
+kskname=$($KEYGEN -q -r $RANDFILE -f KSK $zone)
ee1d55
+kskname=$($KEYGEN -P $now+90s -A $now+3600s -q -r $RANDFILE -f KSK $zone)
ee1d55
+kskname=$($KEYGEN -I $now+90s -q -r $RANDFILE -f KSK $zone)
ee1d55
+zskname=$($KEYGEN -q -r $RANDFILE $zone)
ee1d55
 cp $infile $zonefile
ee1d55
-$SIGNER -S -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1
ee1d55
+$SIGNER -S -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1
ee1d55
 
ee1d55
 #
ee1d55
 # A zone which will change its sig-validity-interval
ee1d55
@@ -526,8 +528,8 @@ $SIGNER -S -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1
ee1d55
 zone=siginterval.example
ee1d55
 infile=siginterval.example.db.in
ee1d55
 zonefile=siginterval.example.db
ee1d55
-kskname=`$KEYGEN -q -3 -r $RANDFILE -fk $zone`
ee1d55
-zskname=`$KEYGEN -q -3 -r $RANDFILE $zone`
ee1d55
+kskname=$($KEYGEN -q -3 -r $RANDFILE -fk $zone)
ee1d55
+zskname=$($KEYGEN -q -3 -r $RANDFILE $zone)
ee1d55
 cp $infile $zonefile
ee1d55
 
ee1d55
 #
ee1d55
@@ -551,10 +553,10 @@ sed -e 's/bogus/badds/g' < dsset-bogus.example$TP > dsset-badds.example$TP
ee1d55
 zone=future.example
ee1d55
 infile=future.example.db.in
ee1d55
 zonefile=future.example.db
ee1d55
-kskname=`$KEYGEN -q -r $RANDFILE -f KSK $zone`
ee1d55
-zskname=`$KEYGEN -q -r $RANDFILE $zone`
ee1d55
+kskname=$($KEYGEN -q -r $RANDFILE -f KSK $zone)
ee1d55
+zskname=$($KEYGEN -q -r $RANDFILE $zone)
ee1d55
 cat $infile $kskname.key $zskname.key >$zonefile
ee1d55
-$SIGNER -P -s +3600 -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1
ee1d55
+$SIGNER -P -s +3600 -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1
ee1d55
 cp -f $kskname.key trusted-future.key
ee1d55
 
ee1d55
 #
ee1d55
@@ -563,10 +565,10 @@ cp -f $kskname.key trusted-future.key
ee1d55
 zone=managed-future.example
ee1d55
 infile=managed-future.example.db.in
ee1d55
 zonefile=managed-future.example.db
ee1d55
-kskname=`$KEYGEN -q -r $RANDFILE -f KSK $zone`
ee1d55
-zskname=`$KEYGEN -q -r $RANDFILE $zone`
ee1d55
+kskname=$($KEYGEN -q -r $RANDFILE -f KSK $zone)
ee1d55
+zskname=$($KEYGEN -q -r $RANDFILE $zone)
ee1d55
 cat $infile $kskname.key $zskname.key >$zonefile
ee1d55
-$SIGNER -P -s +3600 -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1
ee1d55
+$SIGNER -P -s +3600 -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1
ee1d55
 
ee1d55
 #
ee1d55
 # A zone with a revoked key
ee1d55
diff --git a/bin/tests/system/dnssec/tests.sh b/bin/tests/system/dnssec/tests.sh
ee1d55
index 91a4822..e8d8f7e 100644
ee1d55
--- a/bin/tests/system/dnssec/tests.sh
ee1d55
+++ b/bin/tests/system/dnssec/tests.sh
ee1d55
@@ -699,6 +699,21 @@ n=`expr $n + 1`
ee1d55
 if [ $ret != 0 ]; then echo_i "failed"; fi
ee1d55
 status=`expr $status + $ret`
ee1d55
 
ee1d55
+echo_i "checking mixed-case positive validation ($n)"
ee1d55
+ret=0
ee1d55
+for type in a txt aaaa loc; do
ee1d55
+  $DIG $DIGOPTS +noauth mixedcase.secure.example. \
ee1d55
+    @10.53.0.3 $type >dig.out.$type.ns3.test$n || ret=1
ee1d55
+  $DIG $DIGOPTS +noauth mixedcase.secure.example. \
ee1d55
+    @10.53.0.4 $type >dig.out.$type.ns4.test$n || ret=1
ee1d55
+  digcomp --lc dig.out.$type.ns3.test$n dig.out.$type.ns4.test$n || ret=1
ee1d55
+  grep "status: NOERROR" dig.out.$type.ns4.test$n >/dev/null || ret=1
ee1d55
+  grep "flags:.*ad.*QUERY" dig.out.$type.ns4.test$n >/dev/null || ret=1
ee1d55
+done
ee1d55
+n=$((n + 1))
ee1d55
+test "$ret" -eq 0 || echo_i "failed"
ee1d55
+status=$((status + ret))
ee1d55
+
ee1d55
 echo_i "checking multi-stage positive validation NSEC/NSEC3 ($n)"
ee1d55
 ret=0
ee1d55
 $DIG $DIGOPTS +noauth a.nsec3.example. \
ee1d55
diff --git a/lib/dns/catz.c b/lib/dns/catz.c
ee1d55
index 767c710..98ddd01 100644
ee1d55
--- a/lib/dns/catz.c
ee1d55
+++ b/lib/dns/catz.c
ee1d55
@@ -395,33 +395,21 @@ dns_catz_zones_merge(dns_catz_zone_t *target, dns_catz_zone_t *newzone) {
ee1d55
 
ee1d55
 	dns_name_format(&target->name, czname, DNS_NAME_FORMATSIZE);
ee1d55
 
ee1d55
-	result = isc_ht_init(&toadd, target->catzs->mctx, 16);
ee1d55
-	if (result != ISC_R_SUCCESS)
ee1d55
-		goto cleanup;
ee1d55
+	isc_ht_init(&toadd, target->catzs->mctx, 16, ISC_HT_CASE_SENSITIVE);
ee1d55
 
ee1d55
-	result = isc_ht_init(&tomod, target->catzs->mctx, 16);
ee1d55
-	if (result != ISC_R_SUCCESS)
ee1d55
-		goto cleanup;
ee1d55
+	isc_ht_init(&tomod, target->catzs->mctx, 16, ISC_HT_CASE_SENSITIVE);
ee1d55
 
ee1d55
-	result = isc_ht_iter_create(newzone->entries, &iter1);
ee1d55
-	if (result != ISC_R_SUCCESS)
ee1d55
-		goto cleanup;
ee1d55
+	isc_ht_iter_create(newzone->entries, &iter1);
ee1d55
 
ee1d55
-	result = isc_ht_iter_create(target->entries, &iter2);
ee1d55
-	if (result != ISC_R_SUCCESS)
ee1d55
-		goto cleanup;
ee1d55
+	isc_ht_iter_create(target->entries, &iter2);
ee1d55
 
ee1d55
 	/*
ee1d55
 	 * We can create those iterators now, even though toadd and tomod are
ee1d55
 	 * empty
ee1d55
 	 */
ee1d55
-	result = isc_ht_iter_create(toadd, &iteradd);
ee1d55
-	if (result != ISC_R_SUCCESS)
ee1d55
-		goto cleanup;
ee1d55
+	isc_ht_iter_create(toadd, &iteradd);
ee1d55
 
ee1d55
-	result = isc_ht_iter_create(tomod, &itermod);
ee1d55
-	if (result != ISC_R_SUCCESS)
ee1d55
-		goto cleanup;
ee1d55
+	isc_ht_iter_create(tomod, &itermod);
ee1d55
 
ee1d55
 	/*
ee1d55
 	 * First - walk the new zone and find all nodes that are not in the
ee1d55
@@ -565,7 +553,6 @@ dns_catz_zones_merge(dns_catz_zone_t *target, dns_catz_zone_t *newzone) {
ee1d55
 
ee1d55
 	result = ISC_R_SUCCESS;
ee1d55
 
ee1d55
-cleanup:
ee1d55
 	if (iter1 != NULL)
ee1d55
 		isc_ht_iter_destroy(&iter1);
ee1d55
 	if (iter2 != NULL)
ee1d55
@@ -605,9 +592,7 @@ dns_catz_new_zones(dns_catz_zones_t **catzsp, dns_catz_zonemodmethods_t *zmm,
ee1d55
 	if (result != ISC_R_SUCCESS)
ee1d55
 		goto cleanup_mutex;
ee1d55
 
ee1d55
-	result = isc_ht_init(&new_zones->zones, mctx, 4);
ee1d55
-	if (result != ISC_R_SUCCESS)
ee1d55
-		goto cleanup_refcount;
ee1d55
+	isc_ht_init(&new_zones->zones, mctx, 4, ISC_HT_CASE_SENSITIVE);
ee1d55
 
ee1d55
 	isc_mem_attach(mctx, &new_zones->mctx);
ee1d55
 	new_zones->zmm = zmm;
ee1d55
@@ -624,7 +609,6 @@ dns_catz_new_zones(dns_catz_zones_t **catzsp, dns_catz_zonemodmethods_t *zmm,
ee1d55
 
ee1d55
   cleanup_ht:
ee1d55
 	isc_ht_destroy(&new_zones->zones);
ee1d55
-  cleanup_refcount:
ee1d55
 	isc_refcount_destroy(&new_zones->refs);
ee1d55
   cleanup_mutex:
ee1d55
 	isc_mutex_destroy(&new_zones->lock);
ee1d55
@@ -667,9 +651,7 @@ dns_catz_new_zone(dns_catz_zones_t *catzs, dns_catz_zone_t **zonep,
ee1d55
 	if (result != ISC_R_SUCCESS)
ee1d55
 		goto cleanup_newzone;
ee1d55
 
ee1d55
-	result = isc_ht_init(&new_zone->entries, catzs->mctx, 4);
ee1d55
-	if (result != ISC_R_SUCCESS)
ee1d55
-		goto cleanup_name;
ee1d55
+	isc_ht_init(&new_zone->entries, catzs->mctx, 4, ISC_HT_CASE_SENSITIVE);
ee1d55
 
ee1d55
 	new_zone->updatetimer = NULL;
ee1d55
 	result = isc_timer_create(catzs->timermgr, isc_timertype_inactive,
ee1d55
@@ -698,7 +680,6 @@ dns_catz_new_zone(dns_catz_zones_t *catzs, dns_catz_zone_t **zonep,
ee1d55
 
ee1d55
   cleanup_ht:
ee1d55
 	isc_ht_destroy(&new_zone->entries);
ee1d55
-  cleanup_name:
ee1d55
 	dns_name_free(&new_zone->name, catzs->mctx);
ee1d55
   cleanup_newzone:
ee1d55
 	isc_mem_put(catzs->mctx, new_zone, sizeof(*new_zone));
ee1d55
@@ -800,8 +781,7 @@ dns_catz_zone_detach(dns_catz_zone_t **zonep) {
ee1d55
 	if (refs == 0) {
ee1d55
 		isc_mem_t *mctx = zone->catzs->mctx;
ee1d55
 		if (zone->entries != NULL) {
ee1d55
-			result = isc_ht_iter_create(zone->entries, &iter);
ee1d55
-			INSIST(result == ISC_R_SUCCESS);
ee1d55
+			isc_ht_iter_create(zone->entries, &iter);
ee1d55
 			for (result = isc_ht_iter_first(iter);
ee1d55
 			     result == ISC_R_SUCCESS;
ee1d55
 			     result = isc_ht_iter_delcurrent_next(iter))
ee1d55
@@ -860,8 +840,7 @@ dns_catz_catzs_detach(dns_catz_zones_t **catzsp) {
ee1d55
 		catzs->magic = 0;
ee1d55
 		DESTROYLOCK(&catzs->lock);
ee1d55
 		if (catzs->zones != NULL) {
ee1d55
-			result = isc_ht_iter_create(catzs->zones, &iter);
ee1d55
-			INSIST(result == ISC_R_SUCCESS);
ee1d55
+			isc_ht_iter_create(catzs->zones, &iter);
ee1d55
 			for (result = isc_ht_iter_first(iter);
ee1d55
 			     result == ISC_R_SUCCESS;)
ee1d55
 			{
ee1d55
@@ -1970,8 +1949,7 @@ dns_catz_prereconfig(dns_catz_zones_t *catzs) {
ee1d55
 
ee1d55
 	REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
ee1d55
 
ee1d55
-	result = isc_ht_iter_create(catzs->zones, &iter);
ee1d55
-	INSIST(result == ISC_R_SUCCESS);
ee1d55
+	isc_ht_iter_create(catzs->zones, &iter);
ee1d55
 	for (result = isc_ht_iter_first(iter);
ee1d55
 	     result == ISC_R_SUCCESS;
ee1d55
 	     result = isc_ht_iter_next(iter))
ee1d55
@@ -1993,8 +1971,7 @@ dns_catz_postreconfig(dns_catz_zones_t *catzs) {
ee1d55
 	REQUIRE(DNS_CATZ_ZONES_VALID(catzs));
ee1d55
 
ee1d55
 	LOCK(&catzs->lock);
ee1d55
-	result = isc_ht_iter_create(catzs->zones, &iter);
ee1d55
-	INSIST(result == ISC_R_SUCCESS);
ee1d55
+	isc_ht_iter_create(catzs->zones, &iter);
ee1d55
 	for (result = isc_ht_iter_first(iter);
ee1d55
 	     result == ISC_R_SUCCESS;)
ee1d55
 	{
ee1d55
@@ -2036,5 +2013,6 @@ dns_catz_postreconfig(dns_catz_zones_t *catzs) {
ee1d55
 isc_result_t
ee1d55
 dns_catz_get_iterator(dns_catz_zone_t *catz, isc_ht_iter_t **itp) {
ee1d55
 	REQUIRE(DNS_CATZ_ZONE_VALID(catz));
ee1d55
-	return (isc_ht_iter_create(catz->entries, itp));
ee1d55
+	isc_ht_iter_create(catz->entries, itp);
ee1d55
+	return (ISC_R_SUCCESS);
ee1d55
 }
ee1d55
diff --git a/lib/dns/include/dns/message.h b/lib/dns/include/dns/message.h
ee1d55
index 868a87d..6682450 100644
ee1d55
--- a/lib/dns/include/dns/message.h
ee1d55
+++ b/lib/dns/include/dns/message.h
ee1d55
@@ -739,45 +739,6 @@ dns_message_findtype(dns_name_t *name, dns_rdatatype_t type,
ee1d55
  *\li	#ISC_R_NOTFOUND		-- the desired type does not exist.
ee1d55
  */
ee1d55
 
ee1d55
-isc_result_t
ee1d55
-dns_message_find(dns_name_t *name, dns_rdataclass_t rdclass,
ee1d55
-		 dns_rdatatype_t type, dns_rdatatype_t covers,
ee1d55
-		 dns_rdataset_t **rdataset);
ee1d55
-/*%<
ee1d55
- * Search the name for the specified rdclass and type.  If it is found,
ee1d55
- * *rdataset is filled in with a pointer to that rdataset.
ee1d55
- *
ee1d55
- * Requires:
ee1d55
- *\li	if '**rdataset' is non-NULL, *rdataset needs to be NULL.
ee1d55
- *
ee1d55
- *\li	'type' be a valid type, and NOT dns_rdatatype_any.
ee1d55
- *
ee1d55
- *\li	If 'type' is dns_rdatatype_rrsig, 'covers' must be a valid type.
ee1d55
- *	Otherwise it should be 0.
ee1d55
- *
ee1d55
- * Returns:
ee1d55
- *\li	#ISC_R_SUCCESS		-- all is well.
ee1d55
- *\li	#ISC_R_NOTFOUND		-- the desired type does not exist.
ee1d55
- */
ee1d55
-
ee1d55
-void
ee1d55
-dns_message_movename(dns_message_t *msg, dns_name_t *name,
ee1d55
-		     dns_section_t fromsection,
ee1d55
-		     dns_section_t tosection);
ee1d55
-/*%<
ee1d55
- * Move a name from one section to another.
ee1d55
- *
ee1d55
- * Requires:
ee1d55
- *
ee1d55
- *\li	'msg' be valid.
ee1d55
- *
ee1d55
- *\li	'name' must be a name already in 'fromsection'.
ee1d55
- *
ee1d55
- *\li	'fromsection' must be a valid section.
ee1d55
- *
ee1d55
- *\li	'tosection' must be a valid section.
ee1d55
- */
ee1d55
-
ee1d55
 void
ee1d55
 dns_message_addname(dns_message_t *msg, dns_name_t *name,
ee1d55
 		    dns_section_t section);
ee1d55
diff --git a/lib/dns/include/dns/name.h b/lib/dns/include/dns/name.h
ee1d55
index 93ddacd..9f60081 100644
ee1d55
--- a/lib/dns/include/dns/name.h
ee1d55
+++ b/lib/dns/include/dns/name.h
ee1d55
@@ -67,6 +67,7 @@
ee1d55
 #include <stdio.h>
ee1d55
 
ee1d55
 #include <isc/boolean.h>
ee1d55
+#include <isc/ht.h>
ee1d55
 #include <isc/lang.h>
ee1d55
 #include <isc/magic.h>
ee1d55
 #include <isc/region.h>		/* Required for storage size of dns_label_t. */
ee1d55
@@ -110,6 +111,7 @@ struct dns_name {
ee1d55
 	isc_buffer_t *			buffer;
ee1d55
 	ISC_LINK(dns_name_t)		link;
ee1d55
 	ISC_LIST(dns_rdataset_t)	list;
ee1d55
+	isc_ht_t *ht;
ee1d55
 };
ee1d55
 
ee1d55
 #define DNS_NAME_MAGIC			ISC_MAGIC('D','N','S','n')
ee1d55
@@ -170,7 +172,7 @@ LIBDNS_EXTERNAL_DATA extern dns_name_t *dns_wildcardname;
ee1d55
 	A, (sizeof(A) - 1), sizeof(B), \
ee1d55
 	DNS_NAMEATTR_READONLY, \
ee1d55
 	B, NULL, { (void *)-1, (void *)-1}, \
ee1d55
-	{NULL, NULL} \
ee1d55
+	{NULL, NULL}, NULL			    \
ee1d55
 }
ee1d55
 
ee1d55
 #define DNS_NAME_INITABSOLUTE(A,B) { \
ee1d55
@@ -178,12 +180,12 @@ LIBDNS_EXTERNAL_DATA extern dns_name_t *dns_wildcardname;
ee1d55
 	A, sizeof(A), sizeof(B), \
ee1d55
 	DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE, \
ee1d55
 	B, NULL, { (void *)-1, (void *)-1}, \
ee1d55
-	{NULL, NULL} \
ee1d55
+	{NULL, NULL}, NULL			    \
ee1d55
 }
ee1d55
 
ee1d55
 #define DNS_NAME_INITEMPTY { \
ee1d55
 	DNS_NAME_MAGIC, NULL, 0, 0, 0, NULL, NULL, \
ee1d55
-	{ (void *)-1, (void *)-1 }, { NULL, NULL } \
ee1d55
+	{ (void *)-1, (void *)-1 }, { NULL, NULL }, NULL	\
ee1d55
 }
ee1d55
 
ee1d55
 /*%
ee1d55
@@ -1373,6 +1375,7 @@ do { \
ee1d55
 	_n->buffer = NULL; \
ee1d55
 	ISC_LINK_INIT(_n, link); \
ee1d55
 	ISC_LIST_INIT(_n->list); \
ee1d55
+	_n->ht = NULL; \
ee1d55
 } while (0)
ee1d55
 
ee1d55
 #define DNS_NAME_RESET(n) \
ee1d55
diff --git a/lib/dns/message.c b/lib/dns/message.c
ee1d55
index 9210c26..c97b7bd 100644
ee1d55
--- a/lib/dns/message.c
ee1d55
+++ b/lib/dns/message.c
ee1d55
@@ -19,6 +19,8 @@
ee1d55
 #include <ctype.h>
ee1d55
 
ee1d55
 #include <isc/buffer.h>
ee1d55
+#include <isc/hash.h>
ee1d55
+#include <isc/ht.h>
ee1d55
 #include <isc/mem.h>
ee1d55
 #include <isc/print.h>
ee1d55
 #include <isc/string.h>		/* Required for HP/UX (and others?) */
ee1d55
@@ -159,6 +161,9 @@ msgblock_allocate(isc_mem_t *, unsigned int, unsigned int);
ee1d55
 #define msgblock_get(block, type) \
ee1d55
 	((type *)msgblock_internalget(block, sizeof(type)))
ee1d55
 
ee1d55
+static void
ee1d55
+dns__message_puttemprdataset(dns_message_t *msg, dns_rdataset_t **rdatasetp);
ee1d55
+
ee1d55
 static inline void *
ee1d55
 msgblock_internalget(dns_msgblock_t *, unsigned int);
ee1d55
 
ee1d55
@@ -470,7 +475,7 @@ msgresetopt(dns_message_t *msg)
ee1d55
 		}
ee1d55
 		INSIST(dns_rdataset_isassociated(msg->opt));
ee1d55
 		dns_rdataset_disassociate(msg->opt);
ee1d55
-		isc_mempool_put(msg->rdspool, msg->opt);
ee1d55
+		dns__message_puttemprdataset(msg, &msg->opt);
ee1d55
 		msg->opt = NULL;
ee1d55
 		msg->cc_ok = 0;
ee1d55
 		msg->cc_bad = 0;
ee1d55
@@ -491,10 +496,11 @@ msgresetsigs(dns_message_t *msg, isc_boolean_t replying) {
ee1d55
 			msg->querytsig = msg->tsig;
ee1d55
 		} else {
ee1d55
 			dns_rdataset_disassociate(msg->tsig);
ee1d55
-			isc_mempool_put(msg->rdspool, msg->tsig);
ee1d55
+			dns__message_puttemprdataset(msg, &msg->tsig);
ee1d55
 			if (msg->querytsig != NULL) {
ee1d55
 				dns_rdataset_disassociate(msg->querytsig);
ee1d55
-				isc_mempool_put(msg->rdspool, msg->querytsig);
ee1d55
+				dns__message_puttemprdataset(msg,
ee1d55
+							     &msg->querytsig);
ee1d55
 			}
ee1d55
 		}
ee1d55
 		if (dns_name_dynamic(msg->tsigname))
ee1d55
@@ -504,13 +510,10 @@ msgresetsigs(dns_message_t *msg, isc_boolean_t replying) {
ee1d55
 		msg->tsigname = NULL;
ee1d55
 	} else if (msg->querytsig != NULL && !replying) {
ee1d55
 		dns_rdataset_disassociate(msg->querytsig);
ee1d55
-		isc_mempool_put(msg->rdspool, msg->querytsig);
ee1d55
-		msg->querytsig = NULL;
ee1d55
+		dns__message_puttemprdataset(msg, &msg->querytsig);
ee1d55
 	}
ee1d55
 	if (msg->sig0 != NULL) {
ee1d55
-		INSIST(dns_rdataset_isassociated(msg->sig0));
ee1d55
-		dns_rdataset_disassociate(msg->sig0);
ee1d55
-		isc_mempool_put(msg->rdspool, msg->sig0);
ee1d55
+		dns__message_puttemprdataset(msg, &msg->sig0);
ee1d55
 		if (msg->sig0name != NULL) {
ee1d55
 			if (dns_name_dynamic(msg->sig0name))
ee1d55
 				dns_name_free(msg->sig0name, msg->mctx);
ee1d55
@@ -812,6 +815,18 @@ dns_message_destroy(dns_message_t **msgp) {
ee1d55
 	isc_mem_putanddetach(&msg->mctx, msg, sizeof(dns_message_t));
ee1d55
 }
ee1d55
 
ee1d55
+static isc_result_t
ee1d55
+name_hash_add(isc_ht_t *ht, dns_name_t *name, dns_name_t **foundp) {
ee1d55
+	isc_result_t result = isc_ht_find(ht, name->ndata, name->length,
ee1d55
+					  (void **)foundp);
ee1d55
+	if (result == ISC_R_SUCCESS) {
ee1d55
+		return (ISC_R_EXISTS);
ee1d55
+	}
ee1d55
+	result = isc_ht_add(ht, name->ndata, name->length, (void *)name);
ee1d55
+	INSIST(result == ISC_R_SUCCESS);
ee1d55
+	return (ISC_R_SUCCESS);
ee1d55
+}
ee1d55
+
ee1d55
 static isc_result_t
ee1d55
 findname(dns_name_t **foundname, dns_name_t *target,
ee1d55
 	 dns_namelist_t *section)
ee1d55
@@ -831,28 +846,36 @@ findname(dns_name_t **foundname, dns_name_t *target,
ee1d55
 	return (ISC_R_NOTFOUND);
ee1d55
 }
ee1d55
 
ee1d55
-isc_result_t
ee1d55
-dns_message_find(dns_name_t *name, dns_rdataclass_t rdclass,
ee1d55
-		 dns_rdatatype_t type, dns_rdatatype_t covers,
ee1d55
-		 dns_rdataset_t **rdataset)
ee1d55
-{
ee1d55
-	dns_rdataset_t *curr;
ee1d55
-
ee1d55
-	REQUIRE(name != NULL);
ee1d55
-	REQUIRE(rdataset == NULL || *rdataset == NULL);
ee1d55
+#ifdef _WIN32
ee1d55
+__pragma(pack(push, 1))
ee1d55
+typedef struct rds_key {
ee1d55
+	dns_rdataclass_t rdclass;
ee1d55
+	dns_rdatatype_t type;
ee1d55
+	dns_rdatatype_t covers;
ee1d55
+} rds_key_t;
ee1d55
+__pragma(pack(pop))
ee1d55
+#else
ee1d55
+typedef struct __attribute__((__packed__)) rds_key {
ee1d55
+	dns_rdataclass_t rdclass;
ee1d55
+	dns_rdatatype_t type;
ee1d55
+	dns_rdatatype_t covers;
ee1d55
+} rds_key_t;
ee1d55
+#endif
ee1d55
 
ee1d55
-	for (curr = ISC_LIST_TAIL(name->list);
ee1d55
-	     curr != NULL;
ee1d55
-	     curr = ISC_LIST_PREV(curr, link)) {
ee1d55
-		if (curr->rdclass == rdclass &&
ee1d55
-		    curr->type == type && curr->covers == covers) {
ee1d55
-			if (rdataset != NULL)
ee1d55
-				*rdataset = curr;
ee1d55
-			return (ISC_R_SUCCESS);
ee1d55
-		}
ee1d55
+static isc_result_t
ee1d55
+rds_hash_add(isc_ht_t *ht, dns_rdataset_t *rds, dns_rdataset_t **foundp) {
ee1d55
+	rds_key_t key = { .rdclass = rds->rdclass,
ee1d55
+			  .type = rds->type,
ee1d55
+			  .covers = rds->covers };
ee1d55
+	isc_result_t result = isc_ht_find(ht, (const unsigned char *)&key,
ee1d55
+					  sizeof(key), (void **)foundp);
ee1d55
+	if (result == ISC_R_SUCCESS) {
ee1d55
+		return (ISC_R_EXISTS);
ee1d55
 	}
ee1d55
-
ee1d55
-	return (ISC_R_NOTFOUND);
ee1d55
+	result = isc_ht_add(ht, (const unsigned char *)&key, sizeof(key),
ee1d55
+			    (void *)rds);
ee1d55
+	INSIST(result == ISC_R_SUCCESS);
ee1d55
+	return (ISC_R_SUCCESS);
ee1d55
 }
ee1d55
 
ee1d55
 isc_result_t
ee1d55
@@ -980,6 +1003,18 @@ getrdata(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
ee1d55
 		}					\
ee1d55
 	} while (0)
ee1d55
 
ee1d55
+static void
ee1d55
+cleanup_name_hashmaps(dns_namelist_t *section) {
ee1d55
+	dns_name_t *name = NULL;
ee1d55
+	for (name = ISC_LIST_HEAD(*section); name != NULL;
ee1d55
+	     name = ISC_LIST_NEXT(name, link))
ee1d55
+	{
ee1d55
+		if (name->ht != NULL) {
ee1d55
+			isc_ht_destroy(&name->ht);
ee1d55
+		}
ee1d55
+	}
ee1d55
+}
ee1d55
+
ee1d55
 static isc_result_t
ee1d55
 getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
ee1d55
 	     unsigned int options)
ee1d55
@@ -991,13 +1026,15 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
ee1d55
 	dns_offsets_t *offsets;
ee1d55
 	dns_rdataset_t *rdataset;
ee1d55
 	dns_rdatalist_t *rdatalist;
ee1d55
-	isc_result_t result;
ee1d55
+	isc_result_t result = ISC_R_SUCCESS;
ee1d55
 	dns_rdatatype_t rdtype;
ee1d55
 	dns_rdataclass_t rdclass;
ee1d55
 	dns_namelist_t *section;
ee1d55
-	isc_boolean_t free_name;
ee1d55
+	isc_boolean_t free_name = ISC_FALSE;
ee1d55
 	isc_boolean_t best_effort;
ee1d55
 	isc_boolean_t seen_problem;
ee1d55
+	isc_ht_t *name_map = NULL;
ee1d55
+	isc_boolean_t free_ht = ISC_FALSE;
ee1d55
 
ee1d55
 	section = &msg->sections[DNS_SECTION_QUESTION];
ee1d55
 
ee1d55
@@ -1005,9 +1042,14 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
ee1d55
 	seen_problem = ISC_FALSE;
ee1d55
 
ee1d55
 	name = NULL;
ee1d55
+	name2 = NULL;
ee1d55
 	rdataset = NULL;
ee1d55
 	rdatalist = NULL;
ee1d55
 
ee1d55
+	if (msg->counts[DNS_SECTION_QUESTION] > 1) {
ee1d55
+		isc_ht_init(&name_map, msg->mctx, 1, ISC_HT_CASE_INSENSITIVE);
ee1d55
+	}
ee1d55
+
ee1d55
 	for (count = 0; count < msg->counts[DNS_SECTION_QUESTION]; count++) {
ee1d55
 		name = isc_mempool_get(msg->namepool);
ee1d55
 		if (name == NULL)
ee1d55
@@ -1030,13 +1072,20 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
ee1d55
 		if (result != ISC_R_SUCCESS)
ee1d55
 			goto cleanup;
ee1d55
 
ee1d55
+
ee1d55
+		/* If there is only one QNAME, skip the duplicity checks */
ee1d55
+		if (name_map == NULL) {
ee1d55
+			result = ISC_R_SUCCESS;
ee1d55
+			goto skip_name_check;
ee1d55
+		}
ee1d55
+
ee1d55
 		/*
ee1d55
 		 * Run through the section, looking to see if this name
ee1d55
 		 * is already there.  If it is found, put back the allocated
ee1d55
 		 * name since we no longer need it, and set our name pointer
ee1d55
 		 * to point to the name we found.
ee1d55
 		 */
ee1d55
-		result = findname(&name2, name, section);
ee1d55
+		result = name_hash_add(name_map, name, &name2);
ee1d55
 
ee1d55
 		/*
ee1d55
 		 * If it is the first name in the section, accept it.
ee1d55
@@ -1048,18 +1097,28 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
ee1d55
 		 * this should be legal or not.  In either case we no longer
ee1d55
 		 * need this name pointer.
ee1d55
 		 */
ee1d55
-		if (result != ISC_R_SUCCESS) {
ee1d55
-			if (!ISC_LIST_EMPTY(*section))
ee1d55
+	skip_name_check:
ee1d55
+		switch (result) {
ee1d55
+		case ISC_R_SUCCESS:
ee1d55
+			if (!ISC_LIST_EMPTY(*section)) {
ee1d55
 				DO_ERROR(DNS_R_FORMERR);
ee1d55
+			}
ee1d55
 			ISC_LIST_APPEND(*section, name, link);
ee1d55
-			free_name = ISC_FALSE;
ee1d55
-		} else {
ee1d55
-			isc_mempool_put(msg->namepool, name);
ee1d55
+			break;
ee1d55
+		case ISC_R_EXISTS:
ee1d55
+			dns_message_puttempname(msg, &name);
ee1d55
 			name = name2;
ee1d55
 			name2 = NULL;
ee1d55
-			free_name = ISC_FALSE;
ee1d55
+			break;
ee1d55
+		default:
ee1d55
+			/*
ee1d55
+			 * Unreachable
ee1d55
+			 */
ee1d55
+			break;
ee1d55
 		}
ee1d55
 
ee1d55
+		free_name = ISC_FALSE;
ee1d55
+
ee1d55
 		/*
ee1d55
 		 * Get type and class.
ee1d55
 		 */
ee1d55
@@ -1087,13 +1146,6 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
ee1d55
 		if (rdtype == dns_rdatatype_tkey)
ee1d55
 			msg->tkey = 1;
ee1d55
 
ee1d55
-		/*
ee1d55
-		 * Can't ask the same question twice.
ee1d55
-		 */
ee1d55
-		result = dns_message_find(name, rdclass, rdtype, 0, NULL);
ee1d55
-		if (result == ISC_R_SUCCESS)
ee1d55
-			DO_ERROR(DNS_R_FORMERR);
ee1d55
-
ee1d55
 		/*
ee1d55
 		 * Allocate a new rdatalist.
ee1d55
 		 */
ee1d55
@@ -1102,7 +1154,7 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
ee1d55
 			result = ISC_R_NOMEMORY;
ee1d55
 			goto cleanup;
ee1d55
 		}
ee1d55
-		rdataset =  isc_mempool_get(msg->rdspool);
ee1d55
+		dns_message_gettemprdataset(msg, &rdataset);
ee1d55
 		if (rdataset == NULL) {
ee1d55
 			result = ISC_R_NOMEMORY;
ee1d55
 			goto cleanup;
ee1d55
@@ -1115,32 +1167,74 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
ee1d55
 		rdatalist->type = rdtype;
ee1d55
 		rdatalist->rdclass = rdclass;
ee1d55
 
ee1d55
-		dns_rdataset_init(rdataset);
ee1d55
 		result = dns_rdatalist_tordataset(rdatalist, rdataset);
ee1d55
-		if (result != ISC_R_SUCCESS)
ee1d55
-			goto cleanup;
ee1d55
+		RUNTIME_CHECK(result == ISC_R_SUCCESS);
ee1d55
 
ee1d55
 		rdataset->attributes |= DNS_RDATASETATTR_QUESTION;
ee1d55
 
ee1d55
+		/*
ee1d55
+		 * Skip the duplicity check for first rdataset
ee1d55
+		 */
ee1d55
+		if (ISC_LIST_EMPTY(name->list)) {
ee1d55
+			goto skip_rds_check;
ee1d55
+		}
ee1d55
+
ee1d55
+		/*
ee1d55
+		 * Can't ask the same question twice.
ee1d55
+		 */
ee1d55
+		if (name->ht == NULL) {
ee1d55
+			isc_ht_init(&name->ht, msg->mctx, 1,
ee1d55
+				    ISC_HT_CASE_SENSITIVE);
ee1d55
+			free_ht = ISC_TRUE;
ee1d55
+
ee1d55
+			dns_rdataset_t *old_rdataset = NULL;
ee1d55
+			for (old_rdataset = ISC_LIST_HEAD(name->list);
ee1d55
+			     old_rdataset != NULL;
ee1d55
+			     old_rdataset = ISC_LIST_NEXT(old_rdataset, link))
ee1d55
+			{
ee1d55
+				result = rds_hash_add(name->ht, old_rdataset,
ee1d55
+						      NULL);
ee1d55
+				INSIST(result == ISC_R_SUCCESS);
ee1d55
+			}
ee1d55
+		}
ee1d55
+		result = rds_hash_add(name->ht, rdataset, NULL);
ee1d55
+		if (result == ISC_R_EXISTS) {
ee1d55
+			DO_ERROR(DNS_R_FORMERR);
ee1d55
+		}
ee1d55
+
ee1d55
+	skip_rds_check:
ee1d55
 		ISC_LIST_APPEND(name->list, rdataset, link);
ee1d55
+
ee1d55
 		rdataset = NULL;
ee1d55
 	}
ee1d55
 
ee1d55
-	if (seen_problem)
ee1d55
-		return (DNS_R_RECOVERABLE);
ee1d55
-	return (ISC_R_SUCCESS);
ee1d55
+
ee1d55
+	if (seen_problem) {
ee1d55
+		result = DNS_R_RECOVERABLE;
ee1d55
+	}
ee1d55
 
ee1d55
  cleanup:
ee1d55
 	if (rdataset != NULL) {
ee1d55
-		INSIST(!dns_rdataset_isassociated(rdataset));
ee1d55
-		isc_mempool_put(msg->rdspool, rdataset);
ee1d55
+		if (dns_rdataset_isassociated(rdataset)) {
ee1d55
+			dns_rdataset_disassociate(rdataset);
ee1d55
+		}
ee1d55
+		dns_message_puttemprdataset(msg, &rdataset);
ee1d55
 	}
ee1d55
 #if 0
ee1d55
 	if (rdatalist != NULL)
ee1d55
 		isc_mempool_put(msg->rdlpool, rdatalist);
ee1d55
 #endif
ee1d55
-	if (free_name)
ee1d55
-		isc_mempool_put(msg->namepool, name);
ee1d55
+	if (free_name) {
ee1d55
+		dns_message_puttempname(msg, &name);
ee1d55
+	}
ee1d55
+
ee1d55
+	if (free_ht) {
ee1d55
+		cleanup_name_hashmaps(section);
ee1d55
+	}
ee1d55
+
ee1d55
+	if (name_map != NULL) {
ee1d55
+		isc_ht_destroy(&name_map);
ee1d55
+	}
ee1d55
 
ee1d55
 	return (result);
ee1d55
 }
ee1d55
@@ -1220,24 +1314,26 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
ee1d55
 	unsigned int count, rdatalen;
ee1d55
 	dns_name_t *name = NULL;
ee1d55
 	dns_name_t *name2 = NULL;
ee1d55
-	dns_offsets_t *offsets;
ee1d55
-	dns_rdataset_t *rdataset;
ee1d55
-	dns_rdatalist_t *rdatalist;
ee1d55
-	isc_result_t result;
ee1d55
+	dns_offsets_t *offsets = NULL;
ee1d55
+	dns_rdataset_t *rdataset = NULL;
ee1d55
+	dns_rdataset_t *found_rdataset = NULL;
ee1d55
+	dns_rdatalist_t *rdatalist = NULL;
ee1d55
+	isc_result_t result = ISC_R_SUCCESS;
ee1d55
 	dns_rdatatype_t rdtype, covers;
ee1d55
 	dns_rdataclass_t rdclass;
ee1d55
-	dns_rdata_t *rdata;
ee1d55
+	dns_rdata_t *rdata = NULL;
ee1d55
 	dns_ttl_t ttl;
ee1d55
-	dns_namelist_t *section;
ee1d55
-	isc_boolean_t free_name = ISC_FALSE, free_rdataset = ISC_FALSE;
ee1d55
-	isc_boolean_t preserve_order, best_effort, seen_problem;
ee1d55
-	isc_boolean_t issigzero;
ee1d55
-
ee1d55
-	preserve_order = ISC_TF(options & DNS_MESSAGEPARSE_PRESERVEORDER);
ee1d55
-	best_effort = ISC_TF(options & DNS_MESSAGEPARSE_BESTEFFORT);
ee1d55
-	seen_problem = ISC_FALSE;
ee1d55
-
ee1d55
-	section = &msg->sections[sectionid];
ee1d55
+	dns_namelist_t *section = &msg->sections[sectionid];
ee1d55
+	isc_boolean_t free_name = ISC_FALSE, seen_problem = ISC_FALSE;
ee1d55
+	isc_boolean_t free_ht = ISC_FALSE;
ee1d55
+	isc_boolean_t preserve_order = ((options & DNS_MESSAGEPARSE_PRESERVEORDER) != 0);
ee1d55
+	isc_boolean_t best_effort = ((options & DNS_MESSAGEPARSE_BESTEFFORT) != 0);
ee1d55
+	isc_boolean_t isedns, issigzero, istsig;
ee1d55
+	isc_ht_t *name_map = NULL;
ee1d55
+
ee1d55
+	if (msg->counts[sectionid] > 1) {
ee1d55
+		isc_ht_init(&name_map, msg->mctx, 1, ISC_HT_CASE_INSENSITIVE);
ee1d55
+	}
ee1d55
 
ee1d55
 	for (count = 0; count < msg->counts[sectionid]; count++) {
ee1d55
 		int recstart = source->current;
ee1d55
@@ -1245,11 +1341,15 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
ee1d55
 
ee1d55
 		skip_name_search = ISC_FALSE;
ee1d55
 		skip_type_search = ISC_FALSE;
ee1d55
-		free_rdataset = ISC_FALSE;
ee1d55
+		isedns = ISC_FALSE;
ee1d55
+		issigzero = ISC_FALSE;
ee1d55
+		istsig = ISC_FALSE;
ee1d55
+		found_rdataset = NULL;
ee1d55
 
ee1d55
 		name = isc_mempool_get(msg->namepool);
ee1d55
-		if (name == NULL)
ee1d55
+		if (name == NULL) {
ee1d55
 			return (ISC_R_NOMEMORY);
ee1d55
+		}
ee1d55
 		free_name = ISC_TRUE;
ee1d55
 
ee1d55
 		offsets = newoffsets(msg);
ee1d55
@@ -1265,8 +1365,9 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
ee1d55
 		isc_buffer_remainingregion(source, &r);
ee1d55
 		isc_buffer_setactive(source, r.length);
ee1d55
 		result = getname(name, source, msg, dctx);
ee1d55
-		if (result != ISC_R_SUCCESS)
ee1d55
+		if (result != ISC_R_SUCCESS) {
ee1d55
 			goto cleanup;
ee1d55
+		}
ee1d55
 
ee1d55
 		/*
ee1d55
 		 * Get type, class, ttl, and rdatalen.  Verify that at least
ee1d55
@@ -1286,9 +1387,10 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
ee1d55
 		 * established a class.  Do so now.
ee1d55
 		 */
ee1d55
 		if (msg->rdclass_set == 0 &&
ee1d55
-		    rdtype != dns_rdatatype_opt &&	/* class is UDP SIZE */
ee1d55
-		    rdtype != dns_rdatatype_tsig &&	/* class is ANY */
ee1d55
-		    rdtype != dns_rdatatype_tkey) {	/* class is undefined */
ee1d55
+		    rdtype != dns_rdatatype_opt &&  /* class is UDP SIZE */
ee1d55
+		    rdtype != dns_rdatatype_tsig && /* class is ANY */
ee1d55
+		    rdtype != dns_rdatatype_tkey)   /* class is undefined */
ee1d55
+		{
ee1d55
 			msg->rdclass = rdclass;
ee1d55
 			msg->rdclass_set = 1;
ee1d55
 		}
ee1d55
@@ -1297,15 +1399,17 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
ee1d55
 		 * If this class is different than the one in the question
ee1d55
 		 * section, bail.
ee1d55
 		 */
ee1d55
-		if (msg->opcode != dns_opcode_update
ee1d55
-		    && rdtype != dns_rdatatype_tsig
ee1d55
-		    && rdtype != dns_rdatatype_opt
ee1d55
-		    && rdtype != dns_rdatatype_key /* in a TKEY query */
ee1d55
-		    && rdtype != dns_rdatatype_sig /* SIG(0) */
ee1d55
-		    && rdtype != dns_rdatatype_tkey /* Win2000 TKEY */
ee1d55
-		    && msg->rdclass != dns_rdataclass_any
ee1d55
-		    && msg->rdclass != rdclass)
ee1d55
+		if (msg->opcode != dns_opcode_update &&
ee1d55
+		    rdtype != dns_rdatatype_tsig &&
ee1d55
+		    rdtype != dns_rdatatype_opt &&
ee1d55
+		    rdtype != dns_rdatatype_key &&  /* in a TKEY query */
ee1d55
+		    rdtype != dns_rdatatype_sig &&  /* SIG(0) */
ee1d55
+		    rdtype != dns_rdatatype_tkey && /* Win2000 TKEY */
ee1d55
+		    msg->rdclass != dns_rdataclass_any &&
ee1d55
+		    msg->rdclass != rdclass)
ee1d55
+		{
ee1d55
 			DO_ERROR(DNS_R_FORMERR);
ee1d55
+		}
ee1d55
 
ee1d55
 		/*
ee1d55
 		 * If this is not a TKEY query/response then the KEY
ee1d55
@@ -1315,7 +1419,9 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
ee1d55
 		    rdtype == dns_rdatatype_key &&
ee1d55
 		    msg->rdclass != dns_rdataclass_any &&
ee1d55
 		    msg->rdclass != rdclass)
ee1d55
+		{
ee1d55
 			DO_ERROR(DNS_R_FORMERR);
ee1d55
+		}
ee1d55
 
ee1d55
 		/*
ee1d55
 		 * Special type handling for TSIG, OPT, and TKEY.
ee1d55
@@ -1328,10 +1434,13 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
ee1d55
 			if (sectionid != DNS_SECTION_ADDITIONAL ||
ee1d55
 			    rdclass != dns_rdataclass_any ||
ee1d55
 			    count != msg->counts[sectionid]  - 1)
ee1d55
+			{
ee1d55
 				DO_ERROR(DNS_R_BADTSIG);
ee1d55
-			msg->sigstart = recstart;
ee1d55
-			skip_name_search = ISC_TRUE;
ee1d55
-			skip_type_search = ISC_TRUE;
ee1d55
+			} else {
ee1d55
+				skip_name_search = ISC_TRUE;
ee1d55
+				skip_type_search = ISC_TRUE;
ee1d55
+				istsig = ISC_TRUE;
ee1d55
+			}
ee1d55
 		} else if (rdtype == dns_rdatatype_opt) {
ee1d55
 			/*
ee1d55
 			 * The name of an OPT record must be ".", it
ee1d55
@@ -1341,9 +1450,13 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
ee1d55
 			if (!dns_name_equal(dns_rootname, name) ||
ee1d55
 			    sectionid != DNS_SECTION_ADDITIONAL ||
ee1d55
 			    msg->opt != NULL)
ee1d55
+			{
ee1d55
 				DO_ERROR(DNS_R_FORMERR);
ee1d55
-			skip_name_search = ISC_TRUE;
ee1d55
-			skip_type_search = ISC_TRUE;
ee1d55
+			} else {
ee1d55
+				skip_name_search = ISC_TRUE;
ee1d55
+				skip_type_search = ISC_TRUE;
ee1d55
+				isedns = ISC_TRUE;
ee1d55
+			}
ee1d55
 		} else if (rdtype == dns_rdatatype_tkey) {
ee1d55
 			/*
ee1d55
 			 * A TKEY must be in the additional section if this
ee1d55
@@ -1354,13 +1467,16 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
ee1d55
 			 */
ee1d55
 			dns_section_t tkeysection;
ee1d55
 
ee1d55
-			if ((msg->flags & DNS_MESSAGEFLAG_QR) == 0)
ee1d55
+			if ((msg->flags & DNS_MESSAGEFLAG_QR) == 0) {
ee1d55
 				tkeysection = DNS_SECTION_ADDITIONAL;
ee1d55
-			else
ee1d55
+			} else {
ee1d55
 				tkeysection = DNS_SECTION_ANSWER;
ee1d55
+			}
ee1d55
 			if (sectionid != tkeysection &&
ee1d55
 			    sectionid != DNS_SECTION_ANSWER)
ee1d55
+			{
ee1d55
 				DO_ERROR(DNS_R_FORMERR);
ee1d55
+			}
ee1d55
 		}
ee1d55
 
ee1d55
 		/*
ee1d55
@@ -1386,7 +1502,8 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
ee1d55
 			goto cleanup;
ee1d55
 		}
ee1d55
 		if (msg->opcode == dns_opcode_update &&
ee1d55
-		    update(sectionid, rdclass)) {
ee1d55
+		    update(sectionid, rdclass))
ee1d55
+		{
ee1d55
 			if (rdatalen != 0) {
ee1d55
 				result = DNS_R_FORMERR;
ee1d55
 				goto cleanup;
ee1d55
@@ -1406,46 +1523,54 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
ee1d55
 			result = ISC_R_SUCCESS;
ee1d55
 		} else if (rdclass == dns_rdataclass_none &&
ee1d55
 			   msg->opcode == dns_opcode_update &&
ee1d55
-			   sectionid == DNS_SECTION_UPDATE) {
ee1d55
+			   sectionid == DNS_SECTION_UPDATE)
ee1d55
+		{
ee1d55
 			result = getrdata(source, msg, dctx, msg->rdclass,
ee1d55
 					  rdtype, rdatalen, rdata);
ee1d55
-		} else
ee1d55
-			result = getrdata(source, msg, dctx, rdclass,
ee1d55
-					  rdtype, rdatalen, rdata);
ee1d55
-		if (result != ISC_R_SUCCESS)
ee1d55
+		} else {
ee1d55
+			result = getrdata(source, msg, dctx, rdclass, rdtype,
ee1d55
+					  rdatalen, rdata);
ee1d55
+		}
ee1d55
+		if (result != ISC_R_SUCCESS) {
ee1d55
 			goto cleanup;
ee1d55
+		}
ee1d55
 		rdata->rdclass = rdclass;
ee1d55
-		issigzero = ISC_FALSE;
ee1d55
-		if (rdtype == dns_rdatatype_rrsig &&
ee1d55
-		    rdata->flags == 0) {
ee1d55
+		if (rdtype == dns_rdatatype_rrsig && rdata->flags == 0) {
ee1d55
 			covers = dns_rdata_covers(rdata);
ee1d55
-			if (covers == 0)
ee1d55
+			if (covers == 0) {
ee1d55
 				DO_ERROR(DNS_R_FORMERR);
ee1d55
+			}
ee1d55
 		} else if (rdtype == dns_rdatatype_sig /* SIG(0) */ &&
ee1d55
-			   rdata->flags == 0) {
ee1d55
+			   rdata->flags == 0)
ee1d55
+		{
ee1d55
 			covers = dns_rdata_covers(rdata);
ee1d55
 			if (covers == 0) {
ee1d55
 				if (sectionid != DNS_SECTION_ADDITIONAL ||
ee1d55
 				    count != msg->counts[sectionid]  - 1)
ee1d55
+				{
ee1d55
 					DO_ERROR(DNS_R_BADSIG0);
ee1d55
-				msg->sigstart = recstart;
ee1d55
-				skip_name_search = ISC_TRUE;
ee1d55
-				skip_type_search = ISC_TRUE;
ee1d55
-				issigzero = ISC_TRUE;
ee1d55
+				} else {
ee1d55
+					skip_name_search = ISC_TRUE;
ee1d55
+					skip_type_search = ISC_TRUE;
ee1d55
+					issigzero = ISC_TRUE;
ee1d55
+				}
ee1d55
 			} else {
ee1d55
 				if (msg->rdclass != dns_rdataclass_any &&
ee1d55
 				    msg->rdclass != rdclass)
ee1d55
+				{
ee1d55
 					DO_ERROR(DNS_R_FORMERR);
ee1d55
+				}
ee1d55
 			}
ee1d55
-		} else
ee1d55
+		} else {
ee1d55
 			covers = 0;
ee1d55
+		}
ee1d55
 
ee1d55
 		/*
ee1d55
 		 * Check the ownername of NSEC3 records
ee1d55
 		 */
ee1d55
 		if (rdtype == dns_rdatatype_nsec3 &&
ee1d55
-		    !dns_rdata_checkowner(name, msg->rdclass, rdtype,
ee1d55
-					  ISC_FALSE)) {
ee1d55
+		    !dns_rdata_checkowner(name, msg->rdclass, rdtype, ISC_FALSE))
ee1d55
+		{
ee1d55
 			result = DNS_R_BADOWNERNAME;
ee1d55
 			goto cleanup;
ee1d55
 		}
ee1d55
@@ -1456,109 +1581,156 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
ee1d55
 		 * to the end of the message.
ee1d55
 		 */
ee1d55
 		if (preserve_order || msg->opcode == dns_opcode_update ||
ee1d55
-		    skip_name_search) {
ee1d55
-			if (rdtype != dns_rdatatype_opt &&
ee1d55
-			    rdtype != dns_rdatatype_tsig &&
ee1d55
-			    !issigzero)
ee1d55
-			{
ee1d55
+		    skip_name_search)
ee1d55
+		{
ee1d55
+			if (!isedns && !istsig && !issigzero) {
ee1d55
 				ISC_LIST_APPEND(*section, name, link);
ee1d55
 				free_name = ISC_FALSE;
ee1d55
 			}
ee1d55
 		} else {
ee1d55
+			if (name_map == NULL) {
ee1d55
+				result = ISC_R_SUCCESS;
ee1d55
+				goto skip_name_check;
ee1d55
+			}
ee1d55
+
ee1d55
 			/*
ee1d55
 			 * Run through the section, looking to see if this name
ee1d55
 			 * is already there.  If it is found, put back the
ee1d55
 			 * allocated name since we no longer need it, and set
ee1d55
 			 * our name pointer to point to the name we found.
ee1d55
 			 */
ee1d55
-			result = findname(&name2, name, section);
ee1d55
+			result = name_hash_add(name_map, name, &name2);
ee1d55
 
ee1d55
 			/*
ee1d55
 			 * If it is a new name, append to the section.
ee1d55
 			 */
ee1d55
-			if (result == ISC_R_SUCCESS) {
ee1d55
-				isc_mempool_put(msg->namepool, name);
ee1d55
-				name = name2;
ee1d55
-			} else {
ee1d55
+		skip_name_check:
ee1d55
+			switch (result) {
ee1d55
+			case ISC_R_SUCCESS:
ee1d55
 				ISC_LIST_APPEND(*section, name, link);
ee1d55
+				break;
ee1d55
+			case ISC_R_EXISTS:
ee1d55
+				dns_message_puttempname(msg, &name);
ee1d55
+				name = name2;
ee1d55
+				name2 = NULL;
ee1d55
+				break;
ee1d55
+			default:
ee1d55
+				/*
ee1d55
+				 * Unreachable
ee1d55
+				 */
ee1d55
+				break;
ee1d55
 			}
ee1d55
 			free_name = ISC_FALSE;
ee1d55
 		}
ee1d55
 
ee1d55
+		dns_message_gettemprdataset(msg, &rdataset);
ee1d55
+		if (rdataset == NULL) {
ee1d55
+			result = ISC_R_NOMEMORY;
ee1d55
+			goto cleanup;
ee1d55
+		}
ee1d55
+
ee1d55
+		rdatalist = newrdatalist(msg);
ee1d55
+		if (rdatalist == NULL) {
ee1d55
+			result = ISC_R_NOMEMORY;
ee1d55
+			goto cleanup;
ee1d55
+		}
ee1d55
+
ee1d55
+		rdatalist->type = rdtype;
ee1d55
+		rdatalist->covers = covers;
ee1d55
+		rdatalist->rdclass = rdclass;
ee1d55
+		rdatalist->ttl = ttl;
ee1d55
+
ee1d55
+		RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist, rdataset) ==
ee1d55
+			      ISC_R_SUCCESS);
ee1d55
+		dns_rdataset_setownercase(rdataset, name);
ee1d55
+		rdatalist = NULL;
ee1d55
+
ee1d55
 		/*
ee1d55
 		 * Search name for the particular type and class.
ee1d55
 		 * Skip this stage if in update mode or this is a meta-type.
ee1d55
 		 */
ee1d55
-		if (preserve_order || msg->opcode == dns_opcode_update ||
ee1d55
-		    skip_type_search)
ee1d55
-			result = ISC_R_NOTFOUND;
ee1d55
-		else {
ee1d55
+		if (isedns || istsig || issigzero) {
ee1d55
+			/* Skip adding the rdataset to the tables */
ee1d55
+		} else if (preserve_order || msg->opcode == dns_opcode_update ||
ee1d55
+			   skip_type_search)
ee1d55
+		{
ee1d55
+			result = ISC_R_SUCCESS;
ee1d55
+
ee1d55
+			ISC_LIST_APPEND(name->list, rdataset, link);
ee1d55
+		} else {
ee1d55
 			/*
ee1d55
 			 * If this is a type that can only occur in
ee1d55
 			 * the question section, fail.
ee1d55
 			 */
ee1d55
-			if (dns_rdatatype_questiononly(rdtype))
ee1d55
+			if (dns_rdatatype_questiononly(rdtype)) {
ee1d55
 				DO_ERROR(DNS_R_FORMERR);
ee1d55
+			}
ee1d55
+			if (ISC_LIST_EMPTY(name->list)) {
ee1d55
+				result = ISC_R_SUCCESS;
ee1d55
+				goto skip_rds_check;
ee1d55
+			}
ee1d55
+			if (name->ht == NULL) {
ee1d55
+				isc_ht_init(&name->ht, msg->mctx, 1,
ee1d55
+					    ISC_HT_CASE_SENSITIVE);
ee1d55
+				free_ht = ISC_TRUE;
ee1d55
 
ee1d55
-			rdataset = NULL;
ee1d55
-			result = dns_message_find(name, rdclass, rdtype,
ee1d55
-						   covers, &rdataset);
ee1d55
-		}
ee1d55
+				INSIST(ISC_LIST_HEAD(name->list) ==
ee1d55
+				       ISC_LIST_TAIL(name->list));
ee1d55
 
ee1d55
-		/*
ee1d55
-		 * If we found an rdataset that matches, we need to
ee1d55
-		 * append this rdata to that set.  If we did not, we need
ee1d55
-		 * to create a new rdatalist, store the important bits there,
ee1d55
-		 * convert it to an rdataset, and link the latter to the name.
ee1d55
-		 * Yuck.  When appending, make certain that the type isn't
ee1d55
-		 * a singleton type, such as SOA or CNAME.
ee1d55
-		 *
ee1d55
-		 * Note that this check will be bypassed when preserving order,
ee1d55
-		 * the opcode is an update, or the type search is skipped.
ee1d55
-		 */
ee1d55
-		if (result == ISC_R_SUCCESS) {
ee1d55
-			if (dns_rdatatype_issingleton(rdtype)) {
ee1d55
-				dns_rdata_t *first;
ee1d55
-				dns_rdatalist_fromrdataset(rdataset,
ee1d55
-							   &rdatalist);
ee1d55
-				first = ISC_LIST_HEAD(rdatalist->rdata);
ee1d55
-				INSIST(first != NULL);
ee1d55
-				if (dns_rdata_compare(rdata, first) != 0)
ee1d55
-					DO_ERROR(DNS_R_FORMERR);
ee1d55
-			}
ee1d55
-		}
ee1d55
+				dns_rdataset_t *old_rdataset =
ee1d55
+					ISC_LIST_HEAD(name->list);
ee1d55
 
ee1d55
-		if (result == ISC_R_NOTFOUND) {
ee1d55
-			rdataset = isc_mempool_get(msg->rdspool);
ee1d55
-			if (rdataset == NULL) {
ee1d55
-				result = ISC_R_NOMEMORY;
ee1d55
-				goto cleanup;
ee1d55
-			}
ee1d55
-			free_rdataset = ISC_TRUE;
ee1d55
+				result = rds_hash_add(name->ht, old_rdataset,
ee1d55
+						      NULL);
ee1d55
 
ee1d55
-			rdatalist = newrdatalist(msg);
ee1d55
-			if (rdatalist == NULL) {
ee1d55
-				result = ISC_R_NOMEMORY;
ee1d55
-				goto cleanup;
ee1d55
+				INSIST(result == ISC_R_SUCCESS);
ee1d55
 			}
ee1d55
+			result = rds_hash_add(name->ht, rdataset,
ee1d55
+					      &found_rdataset);
ee1d55
 
ee1d55
-			rdatalist->type = rdtype;
ee1d55
-			rdatalist->covers = covers;
ee1d55
-			rdatalist->rdclass = rdclass;
ee1d55
-			rdatalist->ttl = ttl;
ee1d55
-
ee1d55
-			dns_rdataset_init(rdataset);
ee1d55
-			RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist,
ee1d55
-							       rdataset)
ee1d55
-				      == ISC_R_SUCCESS);
ee1d55
-			dns_rdataset_setownercase(rdataset, name);
ee1d55
+			/*
ee1d55
+			 * If we found an rdataset that matches, we need to
ee1d55
+			 * append this rdata to that set.  If we did not, we
ee1d55
+			 * need to create a new rdatalist, store the important
ee1d55
+			 * bits there, convert it to an rdataset, and link the
ee1d55
+			 * latter to the name. Yuck.  When appending, make
ee1d55
+			 * certain that the type isn't a singleton type, such as
ee1d55
+			 * SOA or CNAME.
ee1d55
+			 *
ee1d55
+			 * Note that this check will be bypassed when preserving
ee1d55
+			 * order, the opcode is an update, or the type search is
ee1d55
+			 * skipped.
ee1d55
+			 */
ee1d55
+		skip_rds_check:
ee1d55
+			switch (result) {
ee1d55
+			case ISC_R_EXISTS:
ee1d55
+				/* Free the rdataset we used as the key */
ee1d55
+				dns_rdataset_disassociate(rdataset);
ee1d55
+				dns__message_puttemprdataset(msg, &rdataset);
ee1d55
+				result = ISC_R_SUCCESS;
ee1d55
+				rdataset = found_rdataset;
ee1d55
+
ee1d55
+				if (!dns_rdatatype_issingleton(rdtype)) {
ee1d55
+					break;
ee1d55
+				}
ee1d55
 
ee1d55
-			if (rdtype != dns_rdatatype_opt &&
ee1d55
-			    rdtype != dns_rdatatype_tsig &&
ee1d55
-			    !issigzero)
ee1d55
-			{
ee1d55
+				dns_rdatalist_fromrdataset(rdataset,
ee1d55
+							   &rdatalist);
ee1d55
+				dns_rdata_t *first =
ee1d55
+					ISC_LIST_HEAD(rdatalist->rdata);
ee1d55
+				INSIST(first != NULL);
ee1d55
+				if (dns_rdata_compare(rdata, first) != 0) {
ee1d55
+					DO_ERROR(DNS_R_FORMERR);
ee1d55
+				}
ee1d55
+				break;
ee1d55
+			case ISC_R_SUCCESS:
ee1d55
 				ISC_LIST_APPEND(name->list, rdataset, link);
ee1d55
-				free_rdataset = ISC_FALSE;
ee1d55
+				break;
ee1d55
+			default:
ee1d55
+				/*
ee1d55
+				 * Unreachable
ee1d55
+				 */
ee1d55
+				break;
ee1d55
 			}
ee1d55
 		}
ee1d55
 
ee1d55
@@ -1572,8 +1744,9 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
ee1d55
 		 */
ee1d55
 		if (ttl != rdataset->ttl) {
ee1d55
 			rdataset->attributes |= DNS_RDATASETATTR_TTLADJUSTED;
ee1d55
-			if (ttl < rdataset->ttl)
ee1d55
+			if (ttl < rdataset->ttl) {
ee1d55
 				rdataset->ttl = ttl;
ee1d55
+			}
ee1d55
 		}
ee1d55
 
ee1d55
 		/* Append this rdata to the rdataset. */
ee1d55
@@ -1588,43 +1761,40 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
ee1d55
 		 * already set if best-effort parsing is enabled otherwise
ee1d55
 		 * there will only be at most one of each.
ee1d55
 		 */
ee1d55
-		if (rdtype == dns_rdatatype_opt && msg->opt == NULL) {
ee1d55
+		if (isedns) {
ee1d55
 			dns_rcode_t ercode;
ee1d55
 
ee1d55
 			msg->opt = rdataset;
ee1d55
-			rdataset = NULL;
ee1d55
-			free_rdataset = ISC_FALSE;
ee1d55
 			ercode = (dns_rcode_t)
ee1d55
 				((msg->opt->ttl & DNS_MESSAGE_EDNSRCODE_MASK)
ee1d55
 				 >> 20);
ee1d55
 			msg->rcode |= ercode;
ee1d55
-			isc_mempool_put(msg->namepool, name);
ee1d55
+			dns_message_puttempname(msg, &name);
ee1d55
 			free_name = ISC_FALSE;
ee1d55
-		} else if (issigzero && msg->sig0 == NULL) {
ee1d55
+		} else if (issigzero) {
ee1d55
 			msg->sig0 = rdataset;
ee1d55
 			msg->sig0name = name;
ee1d55
-			rdataset = NULL;
ee1d55
-			free_rdataset = ISC_FALSE;
ee1d55
+			msg->sigstart = recstart;
ee1d55
 			free_name = ISC_FALSE;
ee1d55
-		} else if (rdtype == dns_rdatatype_tsig && msg->tsig == NULL) {
ee1d55
+		} else if (istsig) {
ee1d55
 			msg->tsig = rdataset;
ee1d55
 			msg->tsigname = name;
ee1d55
-			/* Windows doesn't like TSIG names to be compressed. */
ee1d55
+			msg->sigstart = recstart;
ee1d55
+			/*
ee1d55
+			 * Windows doesn't like TSIG names to be compressed.
ee1d55
+			 */
ee1d55
 			msg->tsigname->attributes |= DNS_NAMEATTR_NOCOMPRESS;
ee1d55
-			rdataset = NULL;
ee1d55
-			free_rdataset = ISC_FALSE;
ee1d55
 			free_name = ISC_FALSE;
ee1d55
 		}
ee1d55
+		rdataset = NULL;
ee1d55
 
ee1d55
 		if (seen_problem) {
ee1d55
-			if (free_name)
ee1d55
-				isc_mempool_put(msg->namepool, name);
ee1d55
-			if (free_rdataset)
ee1d55
-				isc_mempool_put(msg->rdspool, rdataset);
ee1d55
-			free_name = free_rdataset = ISC_FALSE;
ee1d55
+			if (free_name) {
ee1d55
+				dns_message_puttempname(msg, &name);
ee1d55
+			}
ee1d55
+			free_name = ISC_FALSE;
ee1d55
 		}
ee1d55
 		INSIST(free_name == ISC_FALSE);
ee1d55
-		INSIST(free_rdataset == ISC_FALSE);
ee1d55
 	}
ee1d55
 
ee1d55
 	/*
ee1d55
@@ -1635,20 +1805,32 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx,
ee1d55
 	if (sectionid == DNS_SECTION_AUTHORITY &&
ee1d55
 	    msg->opcode == dns_opcode_query &&
ee1d55
 	    ((msg->flags & DNS_MESSAGEFLAG_QR) != 0) &&
ee1d55
-	    ((msg->flags & DNS_MESSAGEFLAG_TC) == 0) &&
ee1d55
-	    !preserve_order &&
ee1d55
+	    ((msg->flags & DNS_MESSAGEFLAG_TC) == 0) && !preserve_order &&
ee1d55
 	    !auth_signed(section))
ee1d55
+	{
ee1d55
 		DO_ERROR(DNS_R_FORMERR);
ee1d55
+	}
ee1d55
 
ee1d55
-	if (seen_problem)
ee1d55
-		return (DNS_R_RECOVERABLE);
ee1d55
-	return (ISC_R_SUCCESS);
ee1d55
+	if (seen_problem) {
ee1d55
+		result = DNS_R_RECOVERABLE;
ee1d55
+	}
ee1d55
 
ee1d55
- cleanup:
ee1d55
-	if (free_name)
ee1d55
-		isc_mempool_put(msg->namepool, name);
ee1d55
-	if (free_rdataset)
ee1d55
+cleanup:
ee1d55
+	if (rdataset != NULL && rdataset != found_rdataset) {
ee1d55
+		dns_rdataset_disassociate(rdataset);
ee1d55
 		isc_mempool_put(msg->rdspool, rdataset);
ee1d55
+	}
ee1d55
+	if (free_name) {
ee1d55
+		dns_message_puttempname(msg, &name);
ee1d55
+	}
ee1d55
+
ee1d55
+	if (free_ht) {
ee1d55
+		cleanup_name_hashmaps(section);
ee1d55
+	}
ee1d55
+
ee1d55
+	if (name_map != NULL) {
ee1d55
+		isc_ht_destroy(&name_map);
ee1d55
+	}
ee1d55
 
ee1d55
 	return (result);
ee1d55
 }
ee1d55
@@ -2380,11 +2562,11 @@ dns_message_renderreset(dns_message_t *msg) {
ee1d55
 		dns_message_puttempname(msg, &msg->tsigname);
ee1d55
 	if (msg->tsig != NULL) {
ee1d55
 		dns_rdataset_disassociate(msg->tsig);
ee1d55
-		dns_message_puttemprdataset(msg, &msg->tsig);
ee1d55
+		dns__message_puttemprdataset(msg, &msg->tsig);
ee1d55
 	}
ee1d55
 	if (msg->sig0 != NULL) {
ee1d55
 		dns_rdataset_disassociate(msg->sig0);
ee1d55
-		dns_message_puttemprdataset(msg, &msg->sig0);
ee1d55
+		dns__message_puttemprdataset(msg, &msg->sig0);
ee1d55
 	}
ee1d55
 }
ee1d55
 
ee1d55
@@ -2477,24 +2659,6 @@ dns_message_findname(dns_message_t *msg, dns_section_t section,
ee1d55
 	return (result);
ee1d55
 }
ee1d55
 
ee1d55
-void
ee1d55
-dns_message_movename(dns_message_t *msg, dns_name_t *name,
ee1d55
-		     dns_section_t fromsection,
ee1d55
-		     dns_section_t tosection)
ee1d55
-{
ee1d55
-	REQUIRE(msg != NULL);
ee1d55
-	REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER);
ee1d55
-	REQUIRE(name != NULL);
ee1d55
-	REQUIRE(VALID_NAMED_SECTION(fromsection));
ee1d55
-	REQUIRE(VALID_NAMED_SECTION(tosection));
ee1d55
-
ee1d55
-	/*
ee1d55
-	 * Unlink the name from the old section
ee1d55
-	 */
ee1d55
-	ISC_LIST_UNLINK(msg->sections[fromsection], name, link);
ee1d55
-	ISC_LIST_APPEND(msg->sections[tosection], name, link);
ee1d55
-}
ee1d55
-
ee1d55
 void
ee1d55
 dns_message_addname(dns_message_t *msg, dns_name_t *name,
ee1d55
 		    dns_section_t section)
ee1d55
@@ -2587,6 +2751,9 @@ dns_message_puttempname(dns_message_t *msg, dns_name_t **item) {
ee1d55
 	REQUIRE(DNS_MESSAGE_VALID(msg));
ee1d55
 	REQUIRE(item != NULL && *item != NULL);
ee1d55
 
ee1d55
+	if ((*item)->ht != NULL) {
ee1d55
+		isc_ht_destroy(&(*item)->ht);
ee1d55
+	}
ee1d55
 	if (dns_name_dynamic(*item))
ee1d55
 		dns_name_free(*item, msg->mctx);
ee1d55
 	isc_mempool_put(msg->namepool, *item);
ee1d55
@@ -2602,14 +2769,19 @@ dns_message_puttemprdata(dns_message_t *msg, dns_rdata_t **item) {
ee1d55
 	*item = NULL;
ee1d55
 }
ee1d55
 
ee1d55
+static void
ee1d55
+dns__message_puttemprdataset(dns_message_t *msg, dns_rdataset_t **item) {
ee1d55
+	isc_mempool_put(msg->rdspool, *item);
ee1d55
+	*item = NULL;
ee1d55
+}
ee1d55
+
ee1d55
 void
ee1d55
 dns_message_puttemprdataset(dns_message_t *msg, dns_rdataset_t **item) {
ee1d55
 	REQUIRE(DNS_MESSAGE_VALID(msg));
ee1d55
 	REQUIRE(item != NULL && *item != NULL);
ee1d55
 
ee1d55
 	REQUIRE(!dns_rdataset_isassociated(*item));
ee1d55
-	isc_mempool_put(msg->rdspool, *item);
ee1d55
-	*item = NULL;
ee1d55
+	dns__message_puttemprdataset(msg, item);
ee1d55
 }
ee1d55
 
ee1d55
 void
ee1d55
@@ -2774,7 +2946,7 @@ dns_message_setopt(dns_message_t *msg, dns_rdataset_t *opt) {
ee1d55
 
ee1d55
  cleanup:
ee1d55
 	dns_rdataset_disassociate(opt);
ee1d55
-	dns_message_puttemprdataset(msg, &opt;;
ee1d55
+	dns__message_puttemprdataset(msg, &opt;;
ee1d55
 	return (result);
ee1d55
 }
ee1d55
 
ee1d55
diff --git a/lib/dns/name.c b/lib/dns/name.c
ee1d55
index e597c63..b3b127b 100644
ee1d55
--- a/lib/dns/name.c
ee1d55
+++ b/lib/dns/name.c
ee1d55
@@ -213,6 +213,7 @@ dns_name_invalidate(dns_name_t *name) {
ee1d55
 	name->offsets = NULL;
ee1d55
 	name->buffer = NULL;
ee1d55
 	ISC_LINK_INIT(name, link);
ee1d55
+	INSIST(name->ht == NULL);
ee1d55
 }
ee1d55
 
ee1d55
 isc_boolean_t
ee1d55
diff --git a/lib/isc/ht.c b/lib/isc/ht.c
ee1d55
index a86e3ec..0cd3a2c 100644
ee1d55
--- a/lib/isc/ht.c
ee1d55
+++ b/lib/isc/ht.c
ee1d55
@@ -1,6 +1,8 @@
ee1d55
 /*
ee1d55
  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
ee1d55
  *
ee1d55
+ * SPDX-License-Identifier: MPL-2.0
ee1d55
+ *
ee1d55
  * This Source Code Form is subject to the terms of the Mozilla Public
ee1d55
  * License, v. 2.0. If a copy of the MPL was not distributed with this
ee1d55
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
ee1d55
@@ -19,71 +21,290 @@
ee1d55
 #include <isc/magic.h>
ee1d55
 #include <isc/mem.h>
ee1d55
 #include <isc/result.h>
ee1d55
+#include <isc/types.h>
ee1d55
 #include <isc/util.h>
ee1d55
 
ee1d55
-
ee1d55
 typedef struct isc_ht_node isc_ht_node_t;
ee1d55
 
ee1d55
-#define ISC_HT_MAGIC			ISC_MAGIC('H', 'T', 'a', 'b')
ee1d55
-#define ISC_HT_VALID(ht)		ISC_MAGIC_VALID(ht, ISC_HT_MAGIC)
ee1d55
+#define ISC_HT_MAGIC	 ISC_MAGIC('H', 'T', 'a', 'b')
ee1d55
+#define ISC_HT_VALID(ht) ISC_MAGIC_VALID(ht, ISC_HT_MAGIC)
ee1d55
+
ee1d55
+#define HT_NO_BITS    0
ee1d55
+#define HT_MIN_BITS   1
ee1d55
+#define HT_MAX_BITS   32
ee1d55
+#define HT_OVERCOMMIT 3
ee1d55
+
ee1d55
+#define HT_NEXTTABLE(idx)      ((idx == 0) ? 1 : 0)
ee1d55
+#define TRY_NEXTTABLE(idx, ht) (idx == ht->hindex && rehashing_in_progress(ht))
ee1d55
+
ee1d55
+#define GOLDEN_RATIO_32 0x61C88647
ee1d55
+
ee1d55
+#define HASHSIZE(bits) ((isc_uint64_t)(1) << (bits))
ee1d55
 
ee1d55
 struct isc_ht_node {
ee1d55
 	void *value;
ee1d55
 	isc_ht_node_t *next;
ee1d55
+	isc_uint32_t hashval;
ee1d55
 	size_t keysize;
ee1d55
-	unsigned char key[FLEXIBLE_ARRAY_MEMBER];
ee1d55
+	unsigned char key[];
ee1d55
 };
ee1d55
 
ee1d55
 struct isc_ht {
ee1d55
 	unsigned int magic;
ee1d55
 	isc_mem_t *mctx;
ee1d55
-	size_t size;
ee1d55
-	size_t mask;
ee1d55
-	unsigned int count;
ee1d55
-	isc_ht_node_t **table;
ee1d55
+	size_t count;
ee1d55
+	isc_boolean_t case_sensitive;
ee1d55
+	size_t size[2];
ee1d55
+	isc_uint8_t hashbits[2];
ee1d55
+	isc_ht_node_t **table[2];
ee1d55
+	isc_uint8_t hindex;
ee1d55
+	isc_uint32_t hiter; /* rehashing iterator */
ee1d55
 };
ee1d55
 
ee1d55
 struct isc_ht_iter {
ee1d55
 	isc_ht_t *ht;
ee1d55
 	size_t i;
ee1d55
+	isc_uint8_t hindex;
ee1d55
 	isc_ht_node_t *cur;
ee1d55
 };
ee1d55
 
ee1d55
-isc_result_t
ee1d55
-isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, isc_uint8_t bits) {
ee1d55
-	isc_ht_t *ht = NULL;
ee1d55
+static isc_ht_node_t *
ee1d55
+isc__ht_find(const isc_ht_t *ht, const unsigned char *key,
ee1d55
+	     const isc_uint32_t keysize, const isc_uint32_t hashval, const isc_uint8_t idx);
ee1d55
+static void
ee1d55
+isc__ht_add(isc_ht_t *ht, const unsigned char *key, const isc_uint32_t keysize,
ee1d55
+	    const isc_uint32_t hashval, const isc_uint8_t idx, void *value);
ee1d55
+static isc_result_t
ee1d55
+isc__ht_delete(isc_ht_t *ht, const unsigned char *key, const isc_uint32_t keysize,
ee1d55
+	       const isc_uint32_t hashval, const isc_uint8_t idx);
ee1d55
+
ee1d55
+static isc_uint32_t
ee1d55
+rehash_bits(isc_ht_t *ht, size_t newcount);
ee1d55
+
ee1d55
+static void
ee1d55
+hashtable_new(isc_ht_t *ht, const isc_uint8_t idx, const isc_uint8_t bits);
ee1d55
+static void
ee1d55
+hashtable_free(isc_ht_t *ht, const isc_uint8_t idx);
ee1d55
+static void
ee1d55
+hashtable_rehash(isc_ht_t *ht, isc_uint32_t newbits);
ee1d55
+static void
ee1d55
+hashtable_rehash_one(isc_ht_t *ht);
ee1d55
+static void
ee1d55
+maybe_rehash(isc_ht_t *ht, size_t newcount);
ee1d55
+
ee1d55
+static isc_result_t
ee1d55
+isc__ht_iter_next(isc_ht_iter_t *it);
ee1d55
+
ee1d55
+static isc_uint8_t maptolower[] = {
ee1d55
+	0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
ee1d55
+	0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
ee1d55
+	0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
ee1d55
+	0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
ee1d55
+	0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
ee1d55
+	0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
ee1d55
+	0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73,
ee1d55
+	0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
ee1d55
+	0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b,
ee1d55
+	0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
ee1d55
+	0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83,
ee1d55
+	0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
ee1d55
+	0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b,
ee1d55
+	0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
ee1d55
+	0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3,
ee1d55
+	0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
ee1d55
+	0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb,
ee1d55
+	0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
ee1d55
+	0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3,
ee1d55
+	0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
ee1d55
+	0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb,
ee1d55
+	0xfc, 0xfd, 0xfe, 0xff
ee1d55
+};
ee1d55
+
ee1d55
+static int
ee1d55
+memcasecmp(const void *vs1, const void *vs2, size_t len) {
ee1d55
+	isc_uint8_t const *s1 = vs1;
ee1d55
+	isc_uint8_t const *s2 = vs2;
ee1d55
 	size_t i;
ee1d55
+	for (i = 0; i < len; i++) {
ee1d55
+		isc_uint8_t u1 = s1[i];
ee1d55
+		isc_uint8_t u2 = s2[i];
ee1d55
+		int U1 = maptolower[u1];
ee1d55
+		int U2 = maptolower[u2];
ee1d55
+		int diff = U1 - U2;
ee1d55
+		if (diff) {
ee1d55
+			return diff;
ee1d55
+		}
ee1d55
+	}
ee1d55
+	return 0;
ee1d55
+}
ee1d55
 
ee1d55
-	REQUIRE(htp != NULL && *htp == NULL);
ee1d55
-	REQUIRE(mctx != NULL);
ee1d55
-	REQUIRE(bits >= 1 && bits <= (sizeof(size_t)*8 - 1));
ee1d55
+static isc_boolean_t
ee1d55
+isc__ht_node_match(isc_ht_node_t *node, const isc_uint32_t hashval,
ee1d55
+		const isc_uint8_t *key, isc_uint32_t keysize, isc_boolean_t case_sensitive) {
ee1d55
+	return (node->hashval == hashval && node->keysize == keysize &&
ee1d55
+		(case_sensitive ? (memcmp(node->key, key, keysize) == 0)
ee1d55
+				: (memcasecmp(node->key, key, keysize) == 0)));
ee1d55
+}
ee1d55
+
ee1d55
+static isc_uint32_t
ee1d55
+hash_32(isc_uint32_t val, unsigned int bits) {
ee1d55
+	REQUIRE(bits <= HT_MAX_BITS);
ee1d55
+	/* High bits are more random. */
ee1d55
+	return (val * GOLDEN_RATIO_32 >> (32 - bits));
ee1d55
+}
ee1d55
+
ee1d55
+static isc_boolean_t
ee1d55
+rehashing_in_progress(const isc_ht_t *ht) {
ee1d55
+	return (ht->table[HT_NEXTTABLE(ht->hindex)] != NULL);
ee1d55
+}
ee1d55
 
ee1d55
-	ht = isc_mem_get(mctx, sizeof(struct isc_ht));
ee1d55
-	if (ht == NULL) {
ee1d55
-		return (ISC_R_NOMEMORY);
ee1d55
+static isc_boolean_t
ee1d55
+hashtable_is_overcommited(isc_ht_t *ht) {
ee1d55
+	return (ht->count >= (ht->size[ht->hindex] * HT_OVERCOMMIT));
ee1d55
+}
ee1d55
+
ee1d55
+static isc_uint32_t
ee1d55
+rehash_bits(isc_ht_t *ht, size_t newcount) {
ee1d55
+	isc_uint32_t newbits = ht->hashbits[ht->hindex];
ee1d55
+
ee1d55
+	while (newcount >= HASHSIZE(newbits) && newbits <= HT_MAX_BITS) {
ee1d55
+		newbits += 1;
ee1d55
 	}
ee1d55
 
ee1d55
-	ht->mctx = NULL;
ee1d55
-	isc_mem_attach(mctx, &ht->mctx);
ee1d55
+	return (newbits);
ee1d55
+}
ee1d55
+
ee1d55
+/*
ee1d55
+ * Rebuild the hashtable to reduce the load factor
ee1d55
+ */
ee1d55
+static void
ee1d55
+hashtable_rehash(isc_ht_t *ht, isc_uint32_t newbits) {
ee1d55
+	isc_uint8_t oldindex = ht->hindex;
ee1d55
+	isc_uint32_t oldbits = ht->hashbits[oldindex];
ee1d55
+	isc_uint8_t newindex = HT_NEXTTABLE(oldindex);
ee1d55
+
ee1d55
+	REQUIRE(ht->hashbits[oldindex] >= HT_MIN_BITS);
ee1d55
+	REQUIRE(ht->hashbits[oldindex] <= HT_MAX_BITS);
ee1d55
+	REQUIRE(ht->table[oldindex] != NULL);
ee1d55
+
ee1d55
+	REQUIRE(newbits <= HT_MAX_BITS);
ee1d55
+	REQUIRE(ht->hashbits[newindex] == HT_NO_BITS);
ee1d55
+	REQUIRE(ht->table[newindex] == NULL);
ee1d55
+
ee1d55
+	REQUIRE(newbits > oldbits);
ee1d55
 
ee1d55
-	ht->size = ((size_t)1<
ee1d55
-	ht->mask = ((size_t)1<
ee1d55
-	ht->count = 0;
ee1d55
+	hashtable_new(ht, newindex, newbits);
ee1d55
 
ee1d55
-	ht->table = isc_mem_get(ht->mctx, ht->size * sizeof(isc_ht_node_t*));
ee1d55
-	if (ht->table == NULL) {
ee1d55
-		isc_mem_putanddetach(&ht->mctx, ht, sizeof(struct isc_ht));
ee1d55
-		return (ISC_R_NOMEMORY);
ee1d55
+	ht->hindex = newindex;
ee1d55
+
ee1d55
+	hashtable_rehash_one(ht);
ee1d55
+}
ee1d55
+
ee1d55
+static void
ee1d55
+hashtable_rehash_one(isc_ht_t *ht) {
ee1d55
+	isc_ht_node_t **newtable = ht->table[ht->hindex];
ee1d55
+	isc_uint32_t oldsize = ht->size[HT_NEXTTABLE(ht->hindex)];
ee1d55
+	isc_ht_node_t **oldtable = ht->table[HT_NEXTTABLE(ht->hindex)];
ee1d55
+	isc_ht_node_t *node = NULL;
ee1d55
+	isc_ht_node_t *nextnode;
ee1d55
+
ee1d55
+	/* Find first non-empty node */
ee1d55
+	while (ht->hiter < oldsize && oldtable[ht->hiter] == NULL) {
ee1d55
+		ht->hiter++;
ee1d55
 	}
ee1d55
 
ee1d55
-	for (i = 0; i < ht->size; i++) {
ee1d55
-		ht->table[i] = NULL;
ee1d55
+	/* Rehashing complete */
ee1d55
+	if (ht->hiter == oldsize) {
ee1d55
+		hashtable_free(ht, HT_NEXTTABLE(ht->hindex));
ee1d55
+		ht->hiter = 0;
ee1d55
+		return;
ee1d55
 	}
ee1d55
 
ee1d55
+	/* Move the first non-empty node from old hashtable to new hashtable */
ee1d55
+	for (node = oldtable[ht->hiter]; node != NULL; node = nextnode) {
ee1d55
+		isc_uint32_t hash = hash_32(node->hashval,
ee1d55
+					ht->hashbits[ht->hindex]);
ee1d55
+		nextnode = node->next;
ee1d55
+		node->next = newtable[hash];
ee1d55
+		newtable[hash] = node;
ee1d55
+	}
ee1d55
+
ee1d55
+	oldtable[ht->hiter] = NULL;
ee1d55
+
ee1d55
+	ht->hiter++;
ee1d55
+}
ee1d55
+
ee1d55
+static void
ee1d55
+maybe_rehash(isc_ht_t *ht, size_t newcount) {
ee1d55
+	isc_uint32_t newbits = rehash_bits(ht, newcount);
ee1d55
+
ee1d55
+	if (ht->hashbits[ht->hindex] < newbits && newbits <= HT_MAX_BITS) {
ee1d55
+		hashtable_rehash(ht, newbits);
ee1d55
+	}
ee1d55
+}
ee1d55
+
ee1d55
+static void
ee1d55
+hashtable_new(isc_ht_t *ht, const isc_uint8_t idx, const isc_uint8_t bits) {
ee1d55
+	size_t size;
ee1d55
+	REQUIRE(ht->hashbits[idx] == HT_NO_BITS);
ee1d55
+	REQUIRE(ht->table[idx] == NULL);
ee1d55
+	REQUIRE(bits >= HT_MIN_BITS);
ee1d55
+	REQUIRE(bits <= HT_MAX_BITS);
ee1d55
+
ee1d55
+	ht->hashbits[idx] = bits;
ee1d55
+	ht->size[idx] = HASHSIZE(ht->hashbits[idx]);
ee1d55
+
ee1d55
+	size = ht->size[idx] * sizeof(isc_ht_node_t *);
ee1d55
+
ee1d55
+	ht->table[idx] = isc_mem_get(ht->mctx, size);
ee1d55
+	INSIST(ht->table[idx] != NULL);
ee1d55
+	memset(ht->table[idx], 0, size);
ee1d55
+}
ee1d55
+
ee1d55
+static void
ee1d55
+hashtable_free(isc_ht_t *ht, const isc_uint8_t idx) {
ee1d55
+	size_t size = ht->size[idx] * sizeof(isc_ht_node_t *);
ee1d55
+	size_t i;
ee1d55
+
ee1d55
+	for (i = 0; i < ht->size[idx]; i++) {
ee1d55
+		isc_ht_node_t *node = ht->table[idx][i];
ee1d55
+		while (node != NULL) {
ee1d55
+			isc_ht_node_t *next = node->next;
ee1d55
+			ht->count--;
ee1d55
+			isc_mem_put(ht->mctx, node,
ee1d55
+				    sizeof(*node) + node->keysize);
ee1d55
+			node = next;
ee1d55
+		}
ee1d55
+	}
ee1d55
+
ee1d55
+	isc_mem_put(ht->mctx, ht->table[idx], size);
ee1d55
+	ht->hashbits[idx] = HT_NO_BITS;
ee1d55
+	ht->table[idx] = NULL;
ee1d55
+}
ee1d55
+
ee1d55
+void
ee1d55
+isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, isc_uint8_t bits,
ee1d55
+	    unsigned int options) {
ee1d55
+	isc_ht_t *ht = NULL;
ee1d55
+	isc_boolean_t case_sensitive = ((options & ISC_HT_CASE_INSENSITIVE) == 0);
ee1d55
+
ee1d55
+	REQUIRE(htp != NULL && *htp == NULL);
ee1d55
+	REQUIRE(mctx != NULL);
ee1d55
+	REQUIRE(bits >= 1 && bits <= HT_MAX_BITS);
ee1d55
+
ee1d55
+	ht = isc_mem_get(mctx, sizeof(*ht));
ee1d55
+	INSIST(ht != NULL);
ee1d55
+	*ht = (isc_ht_t){
ee1d55
+		.case_sensitive = case_sensitive,
ee1d55
+	};
ee1d55
+
ee1d55
+	isc_mem_attach(mctx, &ht->mctx);
ee1d55
+
ee1d55
+	hashtable_new(ht, 0, bits);
ee1d55
+
ee1d55
 	ht->magic = ISC_HT_MAGIC;
ee1d55
 
ee1d55
 	*htp = ht;
ee1d55
-	return (ISC_R_SUCCESS);
ee1d55
 }
ee1d55
 
ee1d55
 void
ee1d55
@@ -91,125 +312,190 @@ isc_ht_destroy(isc_ht_t **htp) {
ee1d55
 	isc_ht_t *ht;
ee1d55
 	size_t i;
ee1d55
 
ee1d55
+
ee1d55
 	REQUIRE(htp != NULL);
ee1d55
+	REQUIRE(ISC_HT_VALID(*htp));
ee1d55
 
ee1d55
 	ht = *htp;
ee1d55
-	REQUIRE(ISC_HT_VALID(ht));
ee1d55
-
ee1d55
+	*htp = NULL;
ee1d55
 	ht->magic = 0;
ee1d55
 
ee1d55
-	for (i = 0; i < ht->size; i++) {
ee1d55
-		isc_ht_node_t *node = ht->table[i];
ee1d55
-		while (node != NULL) {
ee1d55
-			isc_ht_node_t *next = node->next;
ee1d55
-			ht->count--;
ee1d55
-			isc_mem_put(ht->mctx, node,
ee1d55
-				    offsetof(isc_ht_node_t, key) +
ee1d55
-				    node->keysize);
ee1d55
-			node = next;
ee1d55
+	for (i = 0; i <= 1; i++) {
ee1d55
+		if (ht->table[i] != NULL) {
ee1d55
+			hashtable_free(ht, i);
ee1d55
 		}
ee1d55
 	}
ee1d55
 
ee1d55
 	INSIST(ht->count == 0);
ee1d55
 
ee1d55
-	isc_mem_put(ht->mctx, ht->table, ht->size * sizeof(isc_ht_node_t*));
ee1d55
-	isc_mem_putanddetach(&ht->mctx, ht, sizeof(struct isc_ht));
ee1d55
-
ee1d55
-	*htp = NULL;
ee1d55
+	isc_mem_putanddetach(&ht->mctx, ht, sizeof(*ht));
ee1d55
 }
ee1d55
 
ee1d55
-isc_result_t
ee1d55
-isc_ht_add(isc_ht_t *ht, const unsigned char *key,
ee1d55
-	   isc_uint32_t keysize, void *value)
ee1d55
-{
ee1d55
+static void
ee1d55
+isc__ht_add(isc_ht_t *ht, const unsigned char *key, const isc_uint32_t keysize,
ee1d55
+	    const isc_uint32_t hashval, const isc_uint8_t idx, void *value) {
ee1d55
 	isc_ht_node_t *node;
ee1d55
 	isc_uint32_t hash;
ee1d55
 
ee1d55
+	hash = hash_32(hashval, ht->hashbits[idx]);
ee1d55
+
ee1d55
+	node = isc_mem_get(ht->mctx, sizeof(*node) + keysize);
ee1d55
+	INSIST(node != NULL);
ee1d55
+	*node = (isc_ht_node_t){
ee1d55
+		.keysize = keysize,
ee1d55
+		.hashval = hashval,
ee1d55
+		.next = ht->table[idx][hash],
ee1d55
+		.value = value,
ee1d55
+	};
ee1d55
+
ee1d55
+	memmove(node->key, key, keysize);
ee1d55
+
ee1d55
+	ht->count++;
ee1d55
+	ht->table[idx][hash] = node;
ee1d55
+}
ee1d55
+
ee1d55
+isc_result_t
ee1d55
+isc_ht_add(isc_ht_t *ht, const unsigned char *key, const isc_uint32_t keysize,
ee1d55
+	   void *value) {
ee1d55
+	isc_uint32_t hashval;
ee1d55
+
ee1d55
 	REQUIRE(ISC_HT_VALID(ht));
ee1d55
 	REQUIRE(key != NULL && keysize > 0);
ee1d55
 
ee1d55
-	hash = isc_hash_function(key, keysize, ISC_TRUE, NULL);
ee1d55
-	node = ht->table[hash & ht->mask];
ee1d55
-	while (node != NULL) {
ee1d55
-		if (keysize == node->keysize &&
ee1d55
-		    memcmp(key, node->key, keysize) == 0) {
ee1d55
-			return (ISC_R_EXISTS);
ee1d55
-		}
ee1d55
-		node = node->next;
ee1d55
+	if (rehashing_in_progress(ht)) {
ee1d55
+		/* Rehash in progress */
ee1d55
+		hashtable_rehash_one(ht);
ee1d55
+	} else if (hashtable_is_overcommited(ht)) {
ee1d55
+		/* Rehash requested */
ee1d55
+		maybe_rehash(ht, ht->count);
ee1d55
 	}
ee1d55
 
ee1d55
-	node = isc_mem_get(ht->mctx, offsetof(isc_ht_node_t, key) + keysize);
ee1d55
-	if (node == NULL)
ee1d55
-		return (ISC_R_NOMEMORY);
ee1d55
+	hashval = isc_hash_function(key, keysize, ht->case_sensitive, NULL);
ee1d55
 
ee1d55
-	memmove(node->key, key, keysize);
ee1d55
-	node->keysize = keysize;
ee1d55
-	node->next = ht->table[hash & ht->mask];
ee1d55
-	node->value = value;
ee1d55
+	if (isc__ht_find(ht, key, keysize, hashval, ht->hindex) != NULL) {
ee1d55
+		return (ISC_R_EXISTS);
ee1d55
+	}
ee1d55
+
ee1d55
+	isc__ht_add(ht, key, keysize, hashval, ht->hindex, value);
ee1d55
 
ee1d55
-	ht->count++;
ee1d55
-	ht->table[hash & ht->mask] = node;
ee1d55
 	return (ISC_R_SUCCESS);
ee1d55
 }
ee1d55
 
ee1d55
+static isc_ht_node_t *
ee1d55
+isc__ht_find(const isc_ht_t *ht, const unsigned char *key,
ee1d55
+	     const isc_uint32_t keysize, const isc_uint32_t hashval,
ee1d55
+	     const isc_uint8_t idx) {
ee1d55
+	isc_uint32_t hash;
ee1d55
+	isc_uint8_t findex = idx;
ee1d55
+	isc_ht_node_t *node;
ee1d55
+
ee1d55
+nexttable:
ee1d55
+	hash = hash_32(hashval, ht->hashbits[findex]);
ee1d55
+	for (node = ht->table[findex][hash]; node != NULL;
ee1d55
+	     node = node->next)
ee1d55
+	{
ee1d55
+		if (isc__ht_node_match(node, hashval, key, keysize,
ee1d55
+				       ht->case_sensitive))
ee1d55
+		{
ee1d55
+			return (node);
ee1d55
+		}
ee1d55
+	}
ee1d55
+	if (TRY_NEXTTABLE(findex, ht)) {
ee1d55
+		/*
ee1d55
+		 * Rehashing in progress, check the other table
ee1d55
+		 */
ee1d55
+		findex = HT_NEXTTABLE(findex);
ee1d55
+		goto nexttable;
ee1d55
+	}
ee1d55
+
ee1d55
+	return (NULL);
ee1d55
+}
ee1d55
+
ee1d55
 isc_result_t
ee1d55
 isc_ht_find(const isc_ht_t *ht, const unsigned char *key,
ee1d55
-	    isc_uint32_t keysize, void **valuep)
ee1d55
-{
ee1d55
+	    const isc_uint32_t keysize, void **valuep) {
ee1d55
+	isc_uint32_t hashval;
ee1d55
 	isc_ht_node_t *node;
ee1d55
-	isc_uint32_t hash;
ee1d55
 
ee1d55
 	REQUIRE(ISC_HT_VALID(ht));
ee1d55
 	REQUIRE(key != NULL && keysize > 0);
ee1d55
 	REQUIRE(valuep == NULL || *valuep == NULL);
ee1d55
 
ee1d55
-	hash = isc_hash_function(key, keysize, ISC_TRUE, NULL);
ee1d55
-	node = ht->table[hash & ht->mask];
ee1d55
-	while (node != NULL) {
ee1d55
-		if (keysize == node->keysize &&
ee1d55
-		    memcmp(key, node->key, keysize) == 0) {
ee1d55
-			*valuep = node->value;
ee1d55
-			return (ISC_R_SUCCESS);
ee1d55
-		}
ee1d55
-		node = node->next;
ee1d55
+	hashval = isc_hash_function(key, keysize, ht->case_sensitive, NULL);
ee1d55
+
ee1d55
+	node = isc__ht_find(ht, key, keysize, hashval, ht->hindex);
ee1d55
+	if (node == NULL) {
ee1d55
+		return (ISC_R_NOTFOUND);
ee1d55
 	}
ee1d55
 
ee1d55
-	return (ISC_R_NOTFOUND);
ee1d55
+	if (valuep != NULL) {
ee1d55
+		*valuep = node->value;
ee1d55
+	}
ee1d55
+	return (ISC_R_SUCCESS);
ee1d55
 }
ee1d55
 
ee1d55
-isc_result_t
ee1d55
-isc_ht_delete(isc_ht_t *ht, const unsigned char *key, isc_uint32_t keysize) {
ee1d55
-	isc_ht_node_t *node, *prev;
ee1d55
+static isc_result_t
ee1d55
+isc__ht_delete(isc_ht_t *ht, const unsigned char *key, const isc_uint32_t keysize,
ee1d55
+	       const isc_uint32_t hashval, const isc_uint8_t idx) {
ee1d55
+	isc_ht_node_t *prev = NULL;
ee1d55
 	isc_uint32_t hash;
ee1d55
+	isc_ht_node_t *node;
ee1d55
 
ee1d55
-	REQUIRE(ISC_HT_VALID(ht));
ee1d55
-	REQUIRE(key != NULL && keysize > 0);
ee1d55
-
ee1d55
-	prev = NULL;
ee1d55
-	hash = isc_hash_function(key, keysize, ISC_TRUE, NULL);
ee1d55
-	node = ht->table[hash & ht->mask];
ee1d55
-	while (node != NULL) {
ee1d55
-		if (keysize == node->keysize &&
ee1d55
-		    memcmp(key, node->key, keysize) == 0) {
ee1d55
-			if (prev == NULL)
ee1d55
-				ht->table[hash & ht->mask] = node->next;
ee1d55
-			else
ee1d55
+	hash = hash_32(hashval, ht->hashbits[idx]);
ee1d55
+
ee1d55
+	for (node = ht->table[idx][hash]; node != NULL;
ee1d55
+	     prev = node, node = node->next)
ee1d55
+	{
ee1d55
+		if (isc__ht_node_match(node, hashval, key, keysize,
ee1d55
+				       ht->case_sensitive))
ee1d55
+		{
ee1d55
+			if (prev == NULL) {
ee1d55
+				ht->table[idx][hash] = node->next;
ee1d55
+			} else {
ee1d55
 				prev->next = node->next;
ee1d55
+			}
ee1d55
 			isc_mem_put(ht->mctx, node,
ee1d55
-				    offsetof(isc_ht_node_t, key) +
ee1d55
-				    node->keysize);
ee1d55
+				    sizeof(*node) + node->keysize);
ee1d55
 			ht->count--;
ee1d55
 
ee1d55
 			return (ISC_R_SUCCESS);
ee1d55
 		}
ee1d55
-
ee1d55
-		prev = node;
ee1d55
-		node = node->next;
ee1d55
 	}
ee1d55
+
ee1d55
 	return (ISC_R_NOTFOUND);
ee1d55
 }
ee1d55
 
ee1d55
 isc_result_t
ee1d55
+isc_ht_delete(isc_ht_t *ht, const unsigned char *key, const isc_uint32_t keysize) {
ee1d55
+	isc_uint32_t hashval;
ee1d55
+	isc_uint8_t hindex;
ee1d55
+	isc_result_t result;
ee1d55
+
ee1d55
+	REQUIRE(ISC_HT_VALID(ht));
ee1d55
+	REQUIRE(key != NULL && keysize > 0);
ee1d55
+
ee1d55
+	if (rehashing_in_progress(ht)) {
ee1d55
+		/* Rehash in progress */
ee1d55
+		hashtable_rehash_one(ht);
ee1d55
+	}
ee1d55
+
ee1d55
+	hindex = ht->hindex;
ee1d55
+	hashval = isc_hash_function(key, keysize, ht->case_sensitive, NULL);
ee1d55
+nexttable:
ee1d55
+	result = isc__ht_delete(ht, key, keysize, hashval, hindex);
ee1d55
+
ee1d55
+	if (result == ISC_R_NOTFOUND && TRY_NEXTTABLE(hindex, ht)) {
ee1d55
+		/*
ee1d55
+		 * Rehashing in progress, check the other table
ee1d55
+		 */
ee1d55
+		hindex = HT_NEXTTABLE(hindex);
ee1d55
+		goto nexttable;
ee1d55
+	}
ee1d55
+
ee1d55
+	return (result);
ee1d55
+}
ee1d55
+
ee1d55
+void
ee1d55
 isc_ht_iter_create(isc_ht_t *ht, isc_ht_iter_t **itp) {
ee1d55
 	isc_ht_iter_t *it;
ee1d55
 
ee1d55
@@ -217,16 +503,13 @@ isc_ht_iter_create(isc_ht_t *ht, isc_ht_iter_t **itp) {
ee1d55
 	REQUIRE(itp != NULL && *itp == NULL);
ee1d55
 
ee1d55
 	it = isc_mem_get(ht->mctx, sizeof(isc_ht_iter_t));
ee1d55
-	if (it == NULL)
ee1d55
-		return (ISC_R_NOMEMORY);
ee1d55
-
ee1d55
-	it->ht = ht;
ee1d55
-	it->i = 0;
ee1d55
-	it->cur = NULL;
ee1d55
+	INSIST(it != NULL);
ee1d55
+	*it = (isc_ht_iter_t){
ee1d55
+		.ht = ht,
ee1d55
+		.hindex = ht->hindex,
ee1d55
+	};
ee1d55
 
ee1d55
 	*itp = it;
ee1d55
-
ee1d55
-	return (ISC_R_SUCCESS);
ee1d55
 }
ee1d55
 
ee1d55
 void
ee1d55
@@ -237,26 +520,48 @@ isc_ht_iter_destroy(isc_ht_iter_t **itp) {
ee1d55
 	REQUIRE(itp != NULL && *itp != NULL);
ee1d55
 
ee1d55
 	it = *itp;
ee1d55
-	ht = it->ht;
ee1d55
-	isc_mem_put(ht->mctx, it, sizeof(isc_ht_iter_t));
ee1d55
-
ee1d55
 	*itp = NULL;
ee1d55
+	ht = it->ht;
ee1d55
+	isc_mem_put(ht->mctx, it, sizeof(*it));
ee1d55
 }
ee1d55
 
ee1d55
 isc_result_t
ee1d55
 isc_ht_iter_first(isc_ht_iter_t *it) {
ee1d55
+	isc_ht_t *ht;
ee1d55
+
ee1d55
 	REQUIRE(it != NULL);
ee1d55
 
ee1d55
+	ht = it->ht;
ee1d55
+
ee1d55
+	it->hindex = ht->hindex;
ee1d55
 	it->i = 0;
ee1d55
-	while (it->i < it->ht->size && it->ht->table[it->i] == NULL)
ee1d55
+
ee1d55
+	return (isc__ht_iter_next(it));
ee1d55
+}
ee1d55
+
ee1d55
+static isc_result_t
ee1d55
+isc__ht_iter_next(isc_ht_iter_t *it) {
ee1d55
+	isc_ht_t *ht = it->ht;
ee1d55
+
ee1d55
+	while (it->i < ht->size[it->hindex] &&
ee1d55
+	       ht->table[it->hindex][it->i] == NULL)
ee1d55
+	{
ee1d55
 		it->i++;
ee1d55
+	}
ee1d55
 
ee1d55
-	if (it->i == it->ht->size)
ee1d55
-		return (ISC_R_NOMORE);
ee1d55
+	if (it->i < ht->size[it->hindex]) {
ee1d55
+		it->cur = ht->table[it->hindex][it->i];
ee1d55
 
ee1d55
-	it->cur = it->ht->table[it->i];
ee1d55
+		return (ISC_R_SUCCESS);
ee1d55
+	}
ee1d55
 
ee1d55
-	return (ISC_R_SUCCESS);
ee1d55
+	if (TRY_NEXTTABLE(it->hindex, ht)) {
ee1d55
+		it->hindex = HT_NEXTTABLE(it->hindex);
ee1d55
+		it->i = 0;
ee1d55
+		return (isc__ht_iter_next(it));
ee1d55
+	}
ee1d55
+
ee1d55
+	return (ISC_R_NOMORE);
ee1d55
 }
ee1d55
 
ee1d55
 isc_result_t
ee1d55
@@ -265,58 +570,35 @@ isc_ht_iter_next(isc_ht_iter_t *it) {
ee1d55
 	REQUIRE(it->cur != NULL);
ee1d55
 
ee1d55
 	it->cur = it->cur->next;
ee1d55
-	if (it->cur == NULL) {
ee1d55
-		do {
ee1d55
-			it->i++;
ee1d55
-		} while (it->i < it->ht->size && it->ht->table[it->i] == NULL);
ee1d55
-		if (it->i >= it->ht->size)
ee1d55
-			return (ISC_R_NOMORE);
ee1d55
-		it->cur = it->ht->table[it->i];
ee1d55
+
ee1d55
+	if (it->cur != NULL) {
ee1d55
+		return (ISC_R_SUCCESS);
ee1d55
 	}
ee1d55
 
ee1d55
-	return (ISC_R_SUCCESS);
ee1d55
+	it->i++;
ee1d55
+
ee1d55
+	return (isc__ht_iter_next(it));
ee1d55
 }
ee1d55
 
ee1d55
 isc_result_t
ee1d55
 isc_ht_iter_delcurrent_next(isc_ht_iter_t *it) {
ee1d55
 	isc_result_t result = ISC_R_SUCCESS;
ee1d55
-	isc_ht_node_t *to_delete = NULL;
ee1d55
-	isc_ht_node_t *prev = NULL;
ee1d55
-	isc_ht_node_t *node = NULL;
ee1d55
-	isc_uint32_t hash;
ee1d55
+	isc_ht_node_t *dnode = NULL;
ee1d55
+	isc_uint8_t dindex;
ee1d55
 	isc_ht_t *ht;
ee1d55
+	isc_result_t dresult;
ee1d55
+
ee1d55
 	REQUIRE(it != NULL);
ee1d55
 	REQUIRE(it->cur != NULL);
ee1d55
-	to_delete = it->cur;
ee1d55
 	ht = it->ht;
ee1d55
+	dnode = it->cur;
ee1d55
+	dindex = it->hindex;
ee1d55
 
ee1d55
-	it->cur = it->cur->next;
ee1d55
-	if (it->cur == NULL) {
ee1d55
-		do {
ee1d55
-			it->i++;
ee1d55
-		} while (it->i < ht->size && ht->table[it->i] == NULL);
ee1d55
-		if (it->i >= ht->size)
ee1d55
-			result = ISC_R_NOMORE;
ee1d55
-		else
ee1d55
-			it->cur = ht->table[it->i];
ee1d55
-	}
ee1d55
-
ee1d55
-	hash = isc_hash_function(to_delete->key, to_delete->keysize, ISC_TRUE,
ee1d55
-				 NULL);
ee1d55
-	node = ht->table[hash & ht->mask];
ee1d55
-	while (node != to_delete) {
ee1d55
-		prev = node;
ee1d55
-		node = node->next;
ee1d55
-		INSIST(node != NULL);
ee1d55
-	}
ee1d55
+	result = isc_ht_iter_next(it);
ee1d55
 
ee1d55
-	if (prev == NULL)
ee1d55
-		ht->table[hash & ht->mask] = node->next;
ee1d55
-	else
ee1d55
-		prev->next = node->next;
ee1d55
-	isc_mem_put(ht->mctx, node,
ee1d55
-		    offsetof(isc_ht_node_t, key) + node->keysize);
ee1d55
-	ht->count--;
ee1d55
+	dresult = isc__ht_delete(ht, dnode->key, dnode->keysize, dnode->hashval,
ee1d55
+				 dindex);
ee1d55
+	INSIST(dresult == ISC_R_SUCCESS);
ee1d55
 
ee1d55
 	return (result);
ee1d55
 }
ee1d55
@@ -331,8 +613,8 @@ isc_ht_iter_current(isc_ht_iter_t *it, void **valuep) {
ee1d55
 }
ee1d55
 
ee1d55
 void
ee1d55
-isc_ht_iter_currentkey(isc_ht_iter_t *it, unsigned char **key, size_t *keysize)
ee1d55
-{
ee1d55
+isc_ht_iter_currentkey(isc_ht_iter_t *it, unsigned char **key,
ee1d55
+		       size_t *keysize) {
ee1d55
 	REQUIRE(it != NULL);
ee1d55
 	REQUIRE(it->cur != NULL);
ee1d55
 	REQUIRE(key != NULL && *key == NULL);
ee1d55
@@ -341,9 +623,9 @@ isc_ht_iter_currentkey(isc_ht_iter_t *it, unsigned char **key, size_t *keysize)
ee1d55
 	*keysize = it->cur->keysize;
ee1d55
 }
ee1d55
 
ee1d55
-unsigned int
ee1d55
-isc_ht_count(isc_ht_t *ht) {
ee1d55
+size_t
ee1d55
+isc_ht_count(const isc_ht_t *ht) {
ee1d55
 	REQUIRE(ISC_HT_VALID(ht));
ee1d55
 
ee1d55
-	return(ht->count);
ee1d55
+	return (ht->count);
ee1d55
 }
ee1d55
diff --git a/lib/isc/include/isc/ht.h b/lib/isc/include/isc/ht.h
ee1d55
index ea9eab7..843e671 100644
ee1d55
--- a/lib/isc/include/isc/ht.h
ee1d55
+++ b/lib/isc/include/isc/ht.h
ee1d55
@@ -1,6 +1,8 @@
ee1d55
 /*
ee1d55
  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
ee1d55
  *
ee1d55
+ * SPDX-License-Identifier: MPL-2.0
ee1d55
+ *
ee1d55
  * This Source Code Form is subject to the terms of the Mozilla Public
ee1d55
  * License, v. 2.0. If a copy of the MPL was not distributed with this
ee1d55
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
ee1d55
@@ -11,30 +13,33 @@
ee1d55
 
ee1d55
 /* ! \file */
ee1d55
 
ee1d55
-#ifndef ISC_HT_H
ee1d55
-#define ISC_HT_H 1
ee1d55
+#pragma once
ee1d55
 
ee1d55
 #include <string.h>
ee1d55
-#include <isc/types.h>
ee1d55
 #include <isc/result.h>
ee1d55
+#include <isc/types.h>
ee1d55
 
ee1d55
-typedef struct isc_ht isc_ht_t;
ee1d55
+typedef struct isc_ht	   isc_ht_t;
ee1d55
 typedef struct isc_ht_iter isc_ht_iter_t;
ee1d55
 
ee1d55
+enum { ISC_HT_CASE_SENSITIVE = 0x00, ISC_HT_CASE_INSENSITIVE = 0x01 };
ee1d55
+
ee1d55
 /*%
ee1d55
  * Initialize hashtable at *htp, using memory context and size of (1<
ee1d55
  *
ee1d55
+ * If 'options' contains ISC_HT_CASE_INSENSITIVE, then upper- and lower-case
ee1d55
+ * letters in key values will generate the same hash values; this can be used
ee1d55
+ * when the key for a hash table is a DNS name.
ee1d55
+ *
ee1d55
  * Requires:
ee1d55
  *\li	'htp' is not NULL and '*htp' is NULL.
ee1d55
  *\li	'mctx' is a valid memory context.
ee1d55
  *\li	'bits' >=1 and 'bits' <=32
ee1d55
  *
ee1d55
- * Returns:
ee1d55
- *\li	#ISC_R_NOMEMORY		-- not enough memory to create pool
ee1d55
- *\li	#ISC_R_SUCCESS		-- all is well.
ee1d55
  */
ee1d55
-isc_result_t
ee1d55
-isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, isc_uint8_t bits);
ee1d55
+void
ee1d55
+isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, isc_uint8_t bits,
ee1d55
+		unsigned int options);
ee1d55
 
ee1d55
 /*%
ee1d55
  * Destroy hashtable, freeing everything
ee1d55
@@ -51,6 +56,7 @@ isc_ht_destroy(isc_ht_t **htp);
ee1d55
  *
ee1d55
  * Requires:
ee1d55
  *\li	'ht' is a valid hashtable
ee1d55
+ *\li   write-lock
ee1d55
  *
ee1d55
  * Returns:
ee1d55
  *\li	#ISC_R_NOMEMORY		-- not enough memory to create pool
ee1d55
@@ -58,15 +64,18 @@ isc_ht_destroy(isc_ht_t **htp);
ee1d55
  *\li	#ISC_R_SUCCESS		-- all is well.
ee1d55
  */
ee1d55
 isc_result_t
ee1d55
-isc_ht_add(isc_ht_t *ht, const unsigned char *key, isc_uint32_t keysize,
ee1d55
-		   void *value);
ee1d55
+isc_ht_add(isc_ht_t *ht, const unsigned char *key, const isc_uint32_t keysize,
ee1d55
+	   void *value);
ee1d55
 
ee1d55
 /*%
ee1d55
  * Find a node matching 'key'/'keysize' in hashtable 'ht';
ee1d55
- * if found, set 'value' to its value
ee1d55
+ * if found, set '*valuep' to its value. (If 'valuep' is NULL,
ee1d55
+ * then simply return SUCCESS or NOTFOUND to indicate whether the
ee1d55
+ * key exists in the hashtable.)
ee1d55
  *
ee1d55
  * Requires:
ee1d55
  * \li	'ht' is a valid hashtable
ee1d55
+ * \li  read-lock
ee1d55
  *
ee1d55
  * Returns:
ee1d55
  * \li	#ISC_R_SUCCESS		-- success
ee1d55
@@ -74,20 +83,21 @@ isc_ht_add(isc_ht_t *ht, const unsigned char *key, isc_uint32_t keysize,
ee1d55
  */
ee1d55
 isc_result_t
ee1d55
 isc_ht_find(const isc_ht_t *ht, const unsigned char *key,
ee1d55
-	    isc_uint32_t keysize, void **valuep);
ee1d55
+	    const isc_uint32_t keysize, void **valuep);
ee1d55
 
ee1d55
 /*%
ee1d55
  * Delete node from hashtable
ee1d55
  *
ee1d55
  * Requires:
ee1d55
  *\li	ht is a valid hashtable
ee1d55
+ *\li   write-lock
ee1d55
  *
ee1d55
  * Returns:
ee1d55
  *\li	#ISC_R_NOTFOUND		-- key not found
ee1d55
  *\li	#ISC_R_SUCCESS		-- all is well
ee1d55
  */
ee1d55
 isc_result_t
ee1d55
-isc_ht_delete(isc_ht_t *ht, const unsigned char *key, isc_uint32_t keysize);
ee1d55
+isc_ht_delete(isc_ht_t *ht, const unsigned char *key, const isc_uint32_t keysize);
ee1d55
 
ee1d55
 /*%
ee1d55
  * Create an iterator for the hashtable; point '*itp' to it.
ee1d55
@@ -96,7 +106,7 @@ isc_ht_delete(isc_ht_t *ht, const unsigned char *key, isc_uint32_t keysize);
ee1d55
  *\li	'ht' is a valid hashtable
ee1d55
  *\li	'itp' is non NULL and '*itp' is NULL.
ee1d55
  */
ee1d55
-isc_result_t
ee1d55
+void
ee1d55
 isc_ht_iter_create(isc_ht_t *ht, isc_ht_iter_t **itp);
ee1d55
 
ee1d55
 /*%
ee1d55
@@ -176,6 +186,5 @@ isc_ht_iter_currentkey(isc_ht_iter_t *it, unsigned char **key, size_t *keysize);
ee1d55
  * Requires:
ee1d55
  *\li	'ht' is a valid hashtable
ee1d55
  */
ee1d55
-unsigned int
ee1d55
-isc_ht_count(isc_ht_t *ht);
ee1d55
-#endif
ee1d55
+size_t
ee1d55
+isc_ht_count(const isc_ht_t *ht);
ee1d55
diff --git a/lib/isc/tests/ht_test.c b/lib/isc/tests/ht_test.c
ee1d55
index 11ffb79..f1b2782 100644
ee1d55
--- a/lib/isc/tests/ht_test.c
ee1d55
+++ b/lib/isc/tests/ht_test.c
ee1d55
@@ -52,8 +52,7 @@ static void test_ht_full(int bits, uintptr_t count) {
ee1d55
 				  NULL, &mctx, 0);
ee1d55
 	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
ee1d55
 
ee1d55
-	result = isc_ht_init(&ht, mctx, bits);
ee1d55
-	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
ee1d55
+	isc_ht_init(&ht, mctx, bits, ISC_HT_CASE_SENSITIVE);
ee1d55
 	ATF_REQUIRE(ht != NULL);
ee1d55
 
ee1d55
 	for (i = 1; i < count; i++) {
ee1d55
@@ -203,8 +202,7 @@ static void test_ht_iterator() {
ee1d55
 				  NULL, &mctx, 0);
ee1d55
 	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
ee1d55
 
ee1d55
-	result = isc_ht_init(&ht, mctx, 16);
ee1d55
-	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
ee1d55
+	isc_ht_init(&ht, mctx, 16, ISC_HT_CASE_SENSITIVE);
ee1d55
 	ATF_REQUIRE(ht != NULL);
ee1d55
 	for (i = 1; i <= count; i++) {
ee1d55
 		/*
ee1d55
@@ -218,8 +216,7 @@ static void test_ht_iterator() {
ee1d55
 	}
ee1d55
 
ee1d55
 	walked = 0;
ee1d55
-	result = isc_ht_iter_create(ht, &iter);
ee1d55
-	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
ee1d55
+	isc_ht_iter_create(ht, &iter);
ee1d55
 
ee1d55
 	for (result = isc_ht_iter_first(iter);
ee1d55
 	     result == ISC_R_SUCCESS;
ee1d55
@@ -303,6 +300,61 @@ static void test_ht_iterator() {
ee1d55
 	ATF_REQUIRE_EQ(ht, NULL);
ee1d55
 }
ee1d55
 
ee1d55
+static void test_ht_case() {
ee1d55
+
ee1d55
+	isc_ht_t *ht = NULL;
ee1d55
+	void *f = NULL;
ee1d55
+	isc_result_t result = ISC_R_UNSET;
ee1d55
+	isc_mem_t *mctx = NULL;
ee1d55
+
ee1d55
+	result = isc_mem_createx2(0, 0, default_memalloc, default_memfree,
ee1d55
+				  NULL, &mctx, 0);
ee1d55
+	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
ee1d55
+
ee1d55
+	unsigned char lower[16] = { "test case" };
ee1d55
+	unsigned char same[16] = { "test case" };
ee1d55
+	unsigned char upper[16] = { "TEST CASE" };
ee1d55
+	unsigned char mixed[16] = { "tEsT CaSe" };
ee1d55
+
ee1d55
+	isc_ht_init(&ht, mctx, 8, ISC_HT_CASE_SENSITIVE);
ee1d55
+	ATF_REQUIRE(ht != NULL);
ee1d55
+
ee1d55
+	result = isc_ht_add(ht, lower, 16, (void *)lower);
ee1d55
+	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
ee1d55
+
ee1d55
+	result = isc_ht_add(ht, same, 16, (void *)same);
ee1d55
+	ATF_REQUIRE_EQ(result, ISC_R_EXISTS);
ee1d55
+
ee1d55
+	result = isc_ht_add(ht, upper, 16, (void *)upper);
ee1d55
+	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
ee1d55
+
ee1d55
+	result = isc_ht_find(ht, mixed, 16, &f);
ee1d55
+	ATF_REQUIRE_EQ(result, ISC_R_NOTFOUND);
ee1d55
+	ATF_REQUIRE_EQ(f, NULL);
ee1d55
+
ee1d55
+	isc_ht_destroy(&ht;;
ee1d55
+	ATF_REQUIRE_EQ(ht, NULL);
ee1d55
+
ee1d55
+	isc_ht_init(&ht, mctx, 8, ISC_HT_CASE_INSENSITIVE);
ee1d55
+	ATF_REQUIRE(ht != NULL);
ee1d55
+
ee1d55
+	result = isc_ht_add(ht, lower, 16, (void *)lower);
ee1d55
+	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
ee1d55
+
ee1d55
+	result = isc_ht_add(ht, same, 16, (void *)same);
ee1d55
+	ATF_REQUIRE_EQ(result, ISC_R_EXISTS);
ee1d55
+
ee1d55
+	result = isc_ht_add(ht, upper, 16, (void *)upper);
ee1d55
+	ATF_REQUIRE_EQ(result, ISC_R_EXISTS);
ee1d55
+
ee1d55
+	result = isc_ht_find(ht, mixed, 16, &f);
ee1d55
+	ATF_REQUIRE_EQ(result, ISC_R_SUCCESS);
ee1d55
+	ATF_REQUIRE_EQ(f, &lower);
ee1d55
+
ee1d55
+	isc_ht_destroy(&ht;;
ee1d55
+	ATF_REQUIRE_EQ(ht, NULL);
ee1d55
+}
ee1d55
+
ee1d55
 ATF_TC(isc_ht_20);
ee1d55
 ATF_TC_HEAD(isc_ht_20, tc) {
ee1d55
 	atf_tc_set_md_var(tc, "descr", "20 bit, 200K elements test");
ee1d55
@@ -357,6 +409,16 @@ ATF_TC_BODY(isc_ht_iterator, tc) {
ee1d55
 	test_ht_iterator();
ee1d55
 }
ee1d55
 
ee1d55
+ATF_TC(isc_ht_case);
ee1d55
+ATF_TC_HEAD(isc_ht_case, tc) {
ee1d55
+	atf_tc_set_md_var(tc, "descr", "case sensitivity test");
ee1d55
+}
ee1d55
+
ee1d55
+ATF_TC_BODY(isc_ht_case, tc) {
ee1d55
+	UNUSED(tc);
ee1d55
+	test_ht_case();
ee1d55
+}
ee1d55
+
ee1d55
 /*
ee1d55
  * Main
ee1d55
  */
ee1d55
@@ -366,5 +428,6 @@ ATF_TP_ADD_TCS(tp) {
ee1d55
 	ATF_TP_ADD_TC(tp, isc_ht_1);
ee1d55
 /*	ATF_TP_ADD_TC(tp, isc_ht_32); */
ee1d55
 	ATF_TP_ADD_TC(tp, isc_ht_iterator);
ee1d55
+	ATF_TP_ADD_TC(tp, isc_ht_case);
ee1d55
 	return (atf_no_error());
ee1d55
 }
ee1d55
-- 
ee1d55
2.45.0
ee1d55