From 1030b5cef8643ebd692f75a8bf671d650fbcb377 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Sur=C3=BD?= Date: Mon, 11 Sep 2023 10:35:28 +0200 Subject: [PATCH] Use hashtable when parsing a message When parsing messages use a hashtable instead of a linear search to reduce the amount of work done in findname when there's more than one name in the section. There are two hashtables: 1) hashtable for owner names - that's constructed for each section when we hit the second name in the section and destroyed right after parsing that section; 2) per-name hashtable - for each name in the section, we construct a new hashtable for that name if there are more than one rdataset for that particular name. (cherry picked from commit b8a96317544c7b310b4f74360825a87b6402ddc2) (cherry picked from commit 0ceed03ebea395da1a39ad1cb39205ce75a3f768) Backport isc_ht API changes from BIND 9.18 To prevent allocating large hashtable in dns_message, we need to backport the improvements to isc_ht API from BIND 9.18+ that includes support for case insensitive keys and incremental rehashing of the hashtables. (cherry picked from commit a4baf324159ec3764195c949cb56c861d9f173ff) (cherry picked from commit 2fc28056b33018f7f78b625409eb44c32d5c9b11) fix a message parsing regression the fix for CVE-2023-4408 introduced a regression in the message parser, which could cause a crash if duplicate rdatasets were found in the question section. this commit ensures that rdatasets are correctly disassociated and freed when this occurs. (cherry picked from commit 4c19d35614f8cd80d8748156a5bad361e19abc28) (cherry picked from commit 98ab8c81cc7739dc220aa3f50efa3061774de8ba) fix another message parsing regression The fix for CVE-2023-4408 introduced a regression in the message parser, which could cause a crash if an rdata type that can only occur in the question was found in another section. (cherry picked from commit 510f1de8a6add516b842a55750366944701d3d9a) (cherry picked from commit bbbcaf8b2ec17d2cad28841ea86078168072ae2f) Apply various tweaks specific to BIND 9.11 (cherry picked from commit c6026cbbaa9d297910af350fa6cc45788cc9f397) Fix case insensitive matching in isc_ht hash table implementation The case insensitive matching in isc_ht was basically completely broken as only the hashvalue computation was case insensitive, but the key comparison was always case sensitive. (cherry picked from commit c462d65b2fd0db362947db4a18a87df78f8d8e5d) (cherry picked from commit 418b3793598a1e1c7e391bb317866d405cd03501) Add a system test for mixed-case data for the same owner We were missing a test where a single owner name would have multiple types with a different case. The generated RRSIGs and NSEC records will then have different case than the signed records and message parser have to cope with that and treat everything as the same owner. (cherry picked from commit c8b623d87f0fb8f9cba8dea5c6a4b600953895e7) (cherry picked from commit 1f9bbe1fe34b7a2c9765431e8a86b460afc9b323) 6315. [security] Speed up parsing of DNS messages with many different names. (CVE-2023-4408) [GL #4234] --- .../system/dnssec/ns3/secure.example.db.in | 5 + bin/tests/system/dnssec/ns3/sign.sh | 321 +++++----- bin/tests/system/dnssec/tests.sh | 15 + lib/dns/catz.c | 50 +- lib/dns/include/dns/message.h | 39 -- lib/dns/include/dns/name.h | 9 +- lib/dns/message.c | 571 +++++++++++------ lib/dns/name.c | 1 + lib/isc/ht.c | 600 +++++++++++++----- lib/isc/include/isc/ht.h | 52 +- lib/isc/tests/ht_test.c | 69 +- 11 files changed, 1097 insertions(+), 635 deletions(-) diff --git a/bin/tests/system/dnssec/ns3/secure.example.db.in b/bin/tests/system/dnssec/ns3/secure.example.db.in index a097227278..be6b3f45b1 100644 --- a/bin/tests/system/dnssec/ns3/secure.example.db.in +++ b/bin/tests/system/dnssec/ns3/secure.example.db.in @@ -44,3 +44,8 @@ rrsigonly A 10.0.0.29 cnameandkey CNAME @ cnamenokey CNAME @ dnameandkey DNAME @ + +mixedcase A 10.0.0.30 +mixedCASE TXT "mixed case" +MIXEDcase AAAA 2002:: +mIxEdCaSe LOC 37 52 56.788 N 121 54 55.02 W 1120m 10m 100m 10m diff --git a/bin/tests/system/dnssec/ns3/sign.sh b/bin/tests/system/dnssec/ns3/sign.sh index 7caf29c3b2..9af0ca5a47 100644 --- a/bin/tests/system/dnssec/ns3/sign.sh +++ b/bin/tests/system/dnssec/ns3/sign.sh @@ -15,39 +15,38 @@ SYSTEMTESTTOP=../.. echo_i "ns3/sign.sh" infile=key.db.in -for tld in managed trusted -do +for tld in managed trusted; do # A secure zone to test. zone=secure.${tld} zonefile=${zone}.db - keyname1=`$KEYGEN -f KSK -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` - cat $infile $keyname1.key > $zonefile - $SIGNER -z -P -3 - -o $zone -r $RANDFILE -O full -f ${zonefile}.signed $zonefile > /dev/null 2>&1 - DSFILE=dsset-`echo ${zone} |sed -e "s/\.$//g"`$TP - $DSFROMKEY -A -f ${zonefile}.signed $zone > $DSFILE + keyname1=$($KEYGEN -f KSK -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) + cat $infile $keyname1.key >$zonefile + $SIGNER -z -P -3 - -o $zone -r $RANDFILE -O full -f ${zonefile}.signed $zonefile >/dev/null 2>&1 + DSFILE=dsset-$(echo ${zone} | sed -e "s/\.$//g")$TP + $DSFROMKEY -A -f ${zonefile}.signed $zone >$DSFILE # Zone to test trust anchor with unsupported algorithm. zone=unsupported.${tld} zonefile=${zone}.db - keyname2=`$KEYGEN -f KSK -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` - cat $infile $keyname2.key > $zonefile - $SIGNER -z -P -3 - -o $zone -r $RANDFILE -O full -f ${zonefile}.tmp $zonefile > /dev/null 2>&1 - awk '$4 == "DNSKEY" { $7 = 255 } $4 == "RRSIG" { $6 = 255 } { print }' ${zonefile}.tmp > ${zonefile}.signed - DSFILE=dsset-`echo ${zone} |sed -e "s/\.$//g"`$TP - $DSFROMKEY -A -f ${zonefile}.signed $zone > $DSFILE + keyname2=$($KEYGEN -f KSK -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) + cat $infile $keyname2.key >$zonefile + $SIGNER -z -P -3 - -o $zone -r $RANDFILE -O full -f ${zonefile}.tmp $zonefile >/dev/null 2>&1 + awk '$4 == "DNSKEY" { $7 = 255 } $4 == "RRSIG" { $6 = 255 } { print }' ${zonefile}.tmp >${zonefile}.signed + DSFILE=dsset-$(echo ${zone} | sed -e "s/\.$//g")$TP + $DSFROMKEY -A -f ${zonefile}.signed $zone >$DSFILE # Make trusted-keys and managed keys conf sections for ns8. mv ${keyname2}.key ${keyname2}.tmp - awk '$1 == "unsupported.'"${tld}"'." { $6 = 255 } { print }' ${keyname2}.tmp > ${keyname2}.key + awk '$1 == "unsupported.'"${tld}"'." { $6 = 255 } { print }' ${keyname2}.tmp >${keyname2}.key case $tld in "managed") - keyfile_to_managed_keys $keyname1 $keyname2 > ../ns8/managed.conf + keyfile_to_managed_keys $keyname1 $keyname2 >../ns8/managed.conf ;; "trusted") - keyfile_to_trusted_keys $keyname1 $keyname2 > ../ns8/trusted.conf + keyfile_to_trusted_keys $keyname1 $keyname2 >../ns8/trusted.conf ;; esac done @@ -58,49 +57,51 @@ zone=secure.example. infile=secure.example.db.in zonefile=secure.example.db -cnameandkey=`$KEYGEN -T KEY -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n host cnameandkey.$zone` -dnameandkey=`$KEYGEN -T KEY -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n host dnameandkey.$zone` -keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` +cnameandkey=$($KEYGEN -T KEY -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n host cnameandkey.$zone) +dnameandkey=$($KEYGEN -T KEY -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n host dnameandkey.$zone) +keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) cat $infile $cnameandkey.key $dnameandkey.key $keyname.key >$zonefile -$SIGNER -P -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 +$SIGNER -P -D -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 +cat "$zonefile" "$zonefile".signed >"$zonefile".tmp +mv "$zonefile".tmp "$zonefile".signed zone=bogus.example. infile=bogus.example.db.in zonefile=bogus.example.db -keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` +keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) cat $infile $keyname.key >$zonefile -$SIGNER -P -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 +$SIGNER -P -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 zone=dynamic.example. infile=dynamic.example.db.in zonefile=dynamic.example.db -keyname1=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` -keyname2=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b 1024 -n zone -f KSK $zone` +keyname1=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) +keyname2=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b 1024 -n zone -f KSK $zone) cat $infile $keyname1.key $keyname2.key >$zonefile -$SIGNER -P -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 +$SIGNER -P -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 zone=keyless.example. infile=generic.example.db.in zonefile=keyless.example.db -keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` +keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) cat $infile $keyname.key >$zonefile -$SIGNER -P -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 +$SIGNER -P -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 # Change the signer field of the a.b.keyless.example SIG A # to point to a provably nonexistent KEY record. mv $zonefile.signed $zonefile.tmp -<$zonefile.tmp $PERL -p -e 's/ keyless.example/ b.keyless.example/ +$PERL <$zonefile.tmp -p -e 's/ keyless.example/ b.keyless.example/ if /^a.b.keyless.example/../NXT/;' >$zonefile.signed rm -f $zonefile.tmp @@ -111,11 +112,11 @@ zone=secure.nsec3.example. infile=secure.nsec3.example.db.in zonefile=secure.nsec3.example.db -keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` +keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) cat $infile $keyname.key >$zonefile -$SIGNER -P -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 +$SIGNER -P -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 # # NSEC3/NSEC3 test zone @@ -124,11 +125,11 @@ zone=nsec3.nsec3.example. infile=nsec3.nsec3.example.db.in zonefile=nsec3.nsec3.example.db -keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` +keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) cat $infile $keyname.key >$zonefile -$SIGNER -P -3 - -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 +$SIGNER -P -3 - -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 # # OPTOUT/NSEC3 test zone @@ -137,11 +138,11 @@ zone=optout.nsec3.example. infile=optout.nsec3.example.db.in zonefile=optout.nsec3.example.db -keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` +keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) cat $infile $keyname.key >$zonefile -$SIGNER -P -3 - -A -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 +$SIGNER -P -3 - -A -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 # # A nsec3 zone (non-optout). @@ -150,11 +151,11 @@ zone=nsec3.example. infile=nsec3.example.db.in zonefile=nsec3.example.db -keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` +keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) cat $infile $keyname.key >$zonefile -$SIGNER -P -g -3 - -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 +$SIGNER -P -g -3 - -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 # # OPTOUT/NSEC test zone @@ -163,11 +164,11 @@ zone=secure.optout.example. infile=secure.optout.example.db.in zonefile=secure.optout.example.db -keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` +keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) cat $infile $keyname.key >$zonefile -$SIGNER -P -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 +$SIGNER -P -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 # # OPTOUT/NSEC3 test zone @@ -176,11 +177,11 @@ zone=nsec3.optout.example. infile=nsec3.optout.example.db.in zonefile=nsec3.optout.example.db -keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` +keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) cat $infile $keyname.key >$zonefile -$SIGNER -P -3 - -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 +$SIGNER -P -3 - -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 # # OPTOUT/OPTOUT test zone @@ -189,11 +190,11 @@ zone=optout.optout.example. infile=optout.optout.example.db.in zonefile=optout.optout.example.db -keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` +keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) cat $infile $keyname.key >$zonefile -$SIGNER -P -3 - -A -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 +$SIGNER -P -3 - -A -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 # # A optout nsec3 zone. @@ -202,11 +203,11 @@ zone=optout.example. infile=optout.example.db.in zonefile=optout.example.db -keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` +keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) cat $infile $keyname.key >$zonefile -$SIGNER -P -g -3 - -A -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 +$SIGNER -P -g -3 - -A -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 # # A nsec3 zone (non-optout) with unknown nsec3 hash algorithm (-U). @@ -215,11 +216,11 @@ zone=nsec3-unknown.example. infile=nsec3-unknown.example.db.in zonefile=nsec3-unknown.example.db -keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` +keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) cat $infile $keyname.key >$zonefile -$SIGNER -P -3 - -U -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 +$SIGNER -P -3 - -U -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 # # A optout nsec3 zone with a unknown nsec3 hash algorithm (-U). @@ -228,11 +229,11 @@ zone=optout-unknown.example. infile=optout-unknown.example.db.in zonefile=optout-unknown.example.db -keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` +keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) cat $infile $keyname.key >$zonefile -$SIGNER -P -3 - -U -A -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 +$SIGNER -P -3 - -U -A -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 # # A zone that is signed with an unknown DNSKEY algorithm. @@ -242,16 +243,16 @@ zone=dnskey-unknown.example infile=dnskey-unknown.example.db.in zonefile=dnskey-unknown.example.db -keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` +keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) cat $infile $keyname.key >$zonefile -$SIGNER -P -3 - -r $RANDFILE -o $zone -O full -f ${zonefile}.tmp $zonefile > /dev/null 2>&1 +$SIGNER -P -3 - -r $RANDFILE -o $zone -O full -f ${zonefile}.tmp $zonefile >/dev/null 2>&1 -awk '$4 == "DNSKEY" { $7 = 100 } $4 == "RRSIG" { $6 = 100 } { print }' ${zonefile}.tmp > ${zonefile}.signed +awk '$4 == "DNSKEY" { $7 = 100 } $4 == "RRSIG" { $6 = 100 } { print }' ${zonefile}.tmp >${zonefile}.signed DSFILE=dsset-${zone}${TP} -$DSFROMKEY -A -f ${zonefile}.signed $zone > $DSFILE +$DSFROMKEY -A -f ${zonefile}.signed $zone >$DSFILE # # A zone that is signed with an unsupported DNSKEY algorithm (3). @@ -261,16 +262,16 @@ zone=dnskey-unsupported.example infile=dnskey-unsupported.example.db.in zonefile=dnskey-unsupported.example.db -keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` +keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) -cat $infile $keyname.key > $zonefile +cat $infile $keyname.key >$zonefile -$SIGNER -P -3 - -r $RANDFILE -o $zone -O full -f ${zonefile}.tmp $zonefile > /dev/null 2>&1 +$SIGNER -P -3 - -r $RANDFILE -o $zone -O full -f ${zonefile}.tmp $zonefile >/dev/null 2>&1 -awk '$4 == "DNSKEY" { $7 = 255 } $4 == "RRSIG" { $6 = 255 } { print }' ${zonefile}.tmp > ${zonefile}.signed +awk '$4 == "DNSKEY" { $7 = 255 } $4 == "RRSIG" { $6 = 255 } { print }' ${zonefile}.tmp >${zonefile}.signed DSFILE="dsset-${zone}${TP}" -$DSFROMKEY -A -f ${zonefile}.signed $zone > $DSFILE +$DSFROMKEY -A -f ${zonefile}.signed $zone >$DSFILE # # A zone with a published unsupported DNSKEY algorithm (Reserved). @@ -280,12 +281,12 @@ zone=dnskey-unsupported-2.example infile=dnskey-unsupported-2.example.db.in zonefile=dnskey-unsupported-2.example.db -ksk=`$KEYGEN -f KSK -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` -zsk=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` +ksk=$($KEYGEN -f KSK -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) +zsk=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) -cat $infile $ksk.key $zsk.key unsupported-algorithm.key > $zonefile +cat $infile $ksk.key $zsk.key unsupported-algorithm.key >$zonefile -$SIGNER -P -3 - -r $RANDFILE -o $zone -f ${zonefile}.signed $zonefile > /dev/null 2>&1 +$SIGNER -P -3 - -r $RANDFILE -o $zone -f ${zonefile}.signed $zonefile >/dev/null 2>&1 # # A zone with a unknown DNSKEY algorithm + unknown NSEC3 hash algorithm (-U). @@ -295,16 +296,16 @@ zone=dnskey-nsec3-unknown.example infile=dnskey-nsec3-unknown.example.db.in zonefile=dnskey-nsec3-unknown.example.db -keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` +keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) cat $infile $keyname.key >$zonefile -$SIGNER -P -3 - -r $RANDFILE -o $zone -U -O full -f ${zonefile}.tmp $zonefile > /dev/null 2>&1 +$SIGNER -P -3 - -r $RANDFILE -o $zone -U -O full -f ${zonefile}.tmp $zonefile >/dev/null 2>&1 -awk '$4 == "DNSKEY" { $7 = 100; print } $4 == "RRSIG" { $6 = 100; print } { print }' ${zonefile}.tmp > ${zonefile}.signed +awk '$4 == "DNSKEY" { $7 = 100; print } $4 == "RRSIG" { $6 = 100; print } { print }' ${zonefile}.tmp >${zonefile}.signed DSFILE=dsset-${zone}${TP} -$DSFROMKEY -A -f ${zonefile}.signed $zone > $DSFILE +$DSFROMKEY -A -f ${zonefile}.signed $zone >$DSFILE # # A multiple parameter nsec3 zone. @@ -313,21 +314,21 @@ zone=multiple.example. infile=multiple.example.db.in zonefile=multiple.example.db -keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` +keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) cat $infile $keyname.key >$zonefile -$SIGNER -P -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 +$SIGNER -P -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 mv $zonefile.signed $zonefile -$SIGNER -P -u3 - -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 +$SIGNER -P -u3 - -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 mv $zonefile.signed $zonefile -$SIGNER -P -u3 AAAA -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 +$SIGNER -P -u3 AAAA -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 mv $zonefile.signed $zonefile -$SIGNER -P -u3 BBBB -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 +$SIGNER -P -u3 BBBB -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 mv $zonefile.signed $zonefile -$SIGNER -P -u3 CCCC -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 +$SIGNER -P -u3 CCCC -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 mv $zonefile.signed $zonefile -$SIGNER -P -u3 DDDD -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 +$SIGNER -P -u3 DDDD -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 # # A RSASHA256 zone. @@ -336,11 +337,11 @@ zone=rsasha256.example. infile=rsasha256.example.db.in zonefile=rsasha256.example.db -keyname=`$KEYGEN -q -r $RANDFILE -a RSASHA256 -b 1024 -n zone $zone` +keyname=$($KEYGEN -q -r $RANDFILE -a RSASHA256 -b 1024 -n zone $zone) cat $infile $keyname.key >$zonefile -$SIGNER -P -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 +$SIGNER -P -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 # # A RSASHA512 zone. @@ -349,11 +350,11 @@ zone=rsasha512.example. infile=rsasha512.example.db.in zonefile=rsasha512.example.db -keyname=`$KEYGEN -q -r $RANDFILE -a RSASHA512 -b 1024 -n zone $zone` +keyname=$($KEYGEN -q -r $RANDFILE -a RSASHA512 -b 1024 -n zone $zone) cat $infile $keyname.key >$zonefile -$SIGNER -P -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 +$SIGNER -P -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 # # A zone with the DNSKEY set only signed by the KSK @@ -362,10 +363,10 @@ zone=kskonly.example. infile=kskonly.example.db.in zonefile=kskonly.example.db -kskname=`$KEYGEN -q -r $RANDFILE -fk $zone` -zskname=`$KEYGEN -q -r $RANDFILE $zone` +kskname=$($KEYGEN -q -r $RANDFILE -fk $zone) +zskname=$($KEYGEN -q -r $RANDFILE $zone) cat $infile $kskname.key $zskname.key >$zonefile -$SIGNER -x -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 +$SIGNER -x -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 # # A zone with the expired signatures @@ -374,10 +375,10 @@ zone=expired.example. infile=expired.example.db.in zonefile=expired.example.db -kskname=`$KEYGEN -q -r $RANDFILE -fk $zone` -zskname=`$KEYGEN -q -r $RANDFILE $zone` +kskname=$($KEYGEN -q -r $RANDFILE -fk $zone) +zskname=$($KEYGEN -q -r $RANDFILE $zone) cat $infile $kskname.key $zskname.key >$zonefile -$SIGNER -P -r $RANDFILE -o $zone -s -1d -e +1h $zonefile > /dev/null 2>&1 +$SIGNER -P -r $RANDFILE -o $zone -s -1d -e +1h $zonefile >/dev/null 2>&1 rm -f $kskname.* $zskname.* # @@ -387,10 +388,10 @@ zone=update-nsec3.example. infile=update-nsec3.example.db.in zonefile=update-nsec3.example.db -kskname=`$KEYGEN -q -3 -r $RANDFILE -fk $zone` -zskname=`$KEYGEN -q -3 -r $RANDFILE $zone` +kskname=$($KEYGEN -q -3 -r $RANDFILE -fk $zone) +zskname=$($KEYGEN -q -3 -r $RANDFILE $zone) cat $infile $kskname.key $zskname.key >$zonefile -$SIGNER -P -3 - -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 +$SIGNER -P -3 - -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 # # A NSEC signed zone that will have auto-dnssec enabled and @@ -400,12 +401,12 @@ zone=auto-nsec.example. infile=auto-nsec.example.db.in zonefile=auto-nsec.example.db -kskname=`$KEYGEN -q -r $RANDFILE -fk $zone` -zskname=`$KEYGEN -q -r $RANDFILE $zone` -kskname=`$KEYGEN -q -r $RANDFILE -fk $zone` -zskname=`$KEYGEN -q -r $RANDFILE $zone` +kskname=$($KEYGEN -q -r $RANDFILE -fk $zone) +zskname=$($KEYGEN -q -r $RANDFILE $zone) +kskname=$($KEYGEN -q -r $RANDFILE -fk $zone) +zskname=$($KEYGEN -q -r $RANDFILE $zone) cat $infile $kskname.key $zskname.key >$zonefile -$SIGNER -P -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 +$SIGNER -P -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 # # A NSEC3 signed zone that will have auto-dnssec enabled and @@ -415,12 +416,12 @@ zone=auto-nsec3.example. infile=auto-nsec3.example.db.in zonefile=auto-nsec3.example.db -kskname=`$KEYGEN -q -3 -r $RANDFILE -fk $zone` -zskname=`$KEYGEN -q -3 -r $RANDFILE $zone` -kskname=`$KEYGEN -q -3 -r $RANDFILE -fk $zone` -zskname=`$KEYGEN -q -3 -r $RANDFILE $zone` +kskname=$($KEYGEN -q -3 -r $RANDFILE -fk $zone) +zskname=$($KEYGEN -q -3 -r $RANDFILE $zone) +kskname=$($KEYGEN -q -3 -r $RANDFILE -fk $zone) +zskname=$($KEYGEN -q -3 -r $RANDFILE $zone) cat $infile $kskname.key $zskname.key >$zonefile -$SIGNER -P -3 - -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 +$SIGNER -P -3 - -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 # # Secure below cname test zone. @@ -428,9 +429,9 @@ $SIGNER -P -3 - -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 zone=secure.below-cname.example. infile=secure.below-cname.example.db.in zonefile=secure.below-cname.example.db -keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` +keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) cat $infile $keyname.key >$zonefile -$SIGNER -P -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 +$SIGNER -P -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 # # Patched TTL test zone. @@ -441,12 +442,12 @@ zonefile=ttlpatch.example.db signedfile=ttlpatch.example.db.signed patchedfile=ttlpatch.example.db.patched -keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` +keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) cat $infile $keyname.key >$zonefile -$SIGNER -P -r $RANDFILE -f $signedfile -o $zone $zonefile > /dev/null 2>&1 -$CHECKZONE -D -s full $zone $signedfile 2> /dev/null | \ - awk '{$2 = "3600"; print}' > $patchedfile +$SIGNER -P -r $RANDFILE -f $signedfile -o $zone $zonefile >/dev/null 2>&1 +$CHECKZONE -D -s full $zone $signedfile 2>/dev/null | + awk '{$2 = "3600"; print}' >$patchedfile # # Separate DNSSEC records. @@ -456,11 +457,11 @@ infile=split-dnssec.example.db.in zonefile=split-dnssec.example.db signedfile=split-dnssec.example.db.signed -keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` +keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) cat $infile $keyname.key >$zonefile -echo '$INCLUDE "'"$signedfile"'"' >> $zonefile -: > $signedfile -$SIGNER -P -r $RANDFILE -D -o $zone $zonefile > /dev/null 2>&1 +echo '$INCLUDE "'"$signedfile"'"' >>$zonefile +: >$signedfile +$SIGNER -P -r $RANDFILE -D -o $zone $zonefile >/dev/null 2>&1 # # Separate DNSSEC records smart signing. @@ -470,11 +471,11 @@ infile=split-smart.example.db.in zonefile=split-smart.example.db signedfile=split-smart.example.db.signed -keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` +keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) cp $infile $zonefile -echo '$INCLUDE "'"$signedfile"'"' >> $zonefile -: > $signedfile -$SIGNER -P -S -r $RANDFILE -D -o $zone $zonefile > /dev/null 2>&1 +echo '$INCLUDE "'"$signedfile"'"' >>$zonefile +: >$signedfile +$SIGNER -P -S -r $RANDFILE -D -o $zone $zonefile >/dev/null 2>&1 # # Zone with signatures about to expire, but no private key to replace them @@ -483,10 +484,10 @@ zone="expiring.example." infile="expiring.example.db.in" zonefile="expiring.example.db" signedfile="expiring.example.db.signed" -kskname=`$KEYGEN -q -r $RANDFILE $zone` -zskname=`$KEYGEN -q -r $RANDFILE -f KSK $zone` +kskname=$($KEYGEN -q -r $RANDFILE $zone) +zskname=$($KEYGEN -q -r $RANDFILE -f KSK $zone) cp $infile $zonefile -$SIGNER -S -r $RANDFILE -e now+1mi -o $zone $zonefile > /dev/null 2>&1 +$SIGNER -S -r $RANDFILE -e now+1mi -o $zone $zonefile >/dev/null 2>&1 mv -f ${zskname}.private ${zskname}.private.moved mv -f ${kskname}.private ${kskname}.private.moved @@ -498,12 +499,12 @@ infile="upper.example.db.in" zonefile="upper.example.db" lower="upper.example.db.lower" signedfile="upper.example.db.signed" -kskname=`$KEYGEN -q -r $RANDFILE $zone` -zskname=`$KEYGEN -q -r $RANDFILE -f KSK $zone` +kskname=$($KEYGEN -q -r $RANDFILE $zone) +zskname=$($KEYGEN -q -r $RANDFILE -f KSK $zone) cp $infile $zonefile -$SIGNER -P -S -r $RANDFILE -o $zone -f $lower $zonefile > /dev/null 2>/dev/null -$CHECKZONE -D upper.example $lower 2>/dev/null | \ - sed '/RRSIG/s/ upper.example. / UPPER.EXAMPLE. /' > $signedfile +$SIGNER -P -S -r $RANDFILE -o $zone -f $lower $zonefile >/dev/null 2>/dev/null +$CHECKZONE -D upper.example $lower 2>/dev/null | + sed '/RRSIG/s/ upper.example. / UPPER.EXAMPLE. /' >$signedfile # # Check that the signer's name is in lower case when zone name is in @@ -513,10 +514,10 @@ zone="LOWER.EXAMPLE." infile="lower.example.db.in" zonefile="lower.example.db" signedfile="lower.example.db.signed" -kskname=`$KEYGEN -q -r $RANDFILE $zone` -zskname=`$KEYGEN -q -r $RANDFILE -f KSK $zone` +kskname=$($KEYGEN -q -r $RANDFILE $zone) +zskname=$($KEYGEN -q -r $RANDFILE -f KSK $zone) cp $infile $zonefile -$SIGNER -P -S -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 +$SIGNER -P -S -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 # # Zone with signatures about to expire, and dynamic, but configured @@ -526,21 +527,21 @@ zone="nosign.example." infile="nosign.example.db.in" zonefile="nosign.example.db" signedfile="nosign.example.db.signed" -kskname=`$KEYGEN -q -r $RANDFILE $zone` -zskname=`$KEYGEN -q -r $RANDFILE -f KSK $zone` +kskname=$($KEYGEN -q -r $RANDFILE $zone) +zskname=$($KEYGEN -q -r $RANDFILE -f KSK $zone) cp $infile $zonefile -$SIGNER -S -r $RANDFILE -e now+1mi -o $zone $zonefile > /dev/null 2>&1 +$SIGNER -S -r $RANDFILE -e now+1mi -o $zone $zonefile >/dev/null 2>&1 # preserve a normalized copy of the NS RRSIG for comparison later -$CHECKZONE -D nosign.example nosign.example.db.signed 2>/dev/null | \ - awk '$4 == "RRSIG" && $5 == "NS" {$2 = ""; print}' | \ - sed 's/[ ][ ]*/ /g'> ../nosign.before +$CHECKZONE -D nosign.example nosign.example.db.signed 2>/dev/null | + awk '$4 == "RRSIG" && $5 == "NS" {$2 = ""; print}' | + sed 's/[ ][ ]*/ /g' >../nosign.before # # An inline signing zone # zone=inline.example. -kskname=`$KEYGEN -q -3 -r $RANDFILE -fk $zone` -zskname=`$KEYGEN -q -3 -r $RANDFILE $zone` +kskname=$($KEYGEN -q -3 -r $RANDFILE -fk $zone) +zskname=$($KEYGEN -q -3 -r $RANDFILE $zone) # # publish a new key while deactivating another key at the same time. @@ -548,13 +549,13 @@ zskname=`$KEYGEN -q -3 -r $RANDFILE $zone` zone=publish-inactive.example infile=publish-inactive.example.db.in zonefile=publish-inactive.example.db -now=`date -u +%Y%m%d%H%M%S` -kskname=`$KEYGEN -q -r $RANDFILE -f KSK $zone` -kskname=`$KEYGEN -P $now+90s -A $now+3600s -q -r $RANDFILE -f KSK $zone` -kskname=`$KEYGEN -I $now+90s -q -r $RANDFILE -f KSK $zone` -zskname=`$KEYGEN -q -r $RANDFILE $zone` +now=$(date -u +%Y%m%d%H%M%S) +kskname=$($KEYGEN -q -r $RANDFILE -f KSK $zone) +kskname=$($KEYGEN -P $now+90s -A $now+3600s -q -r $RANDFILE -f KSK $zone) +kskname=$($KEYGEN -I $now+90s -q -r $RANDFILE -f KSK $zone) +zskname=$($KEYGEN -q -r $RANDFILE $zone) cp $infile $zonefile -$SIGNER -S -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 +$SIGNER -S -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 # # A zone which will change its sig-validity-interval @@ -562,8 +563,8 @@ $SIGNER -S -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 zone=siginterval.example infile=siginterval.example.db.in zonefile=siginterval.example.db -kskname=`$KEYGEN -q -3 -r $RANDFILE -fk $zone` -zskname=`$KEYGEN -q -3 -r $RANDFILE $zone` +kskname=$($KEYGEN -q -3 -r $RANDFILE -fk $zone) +zskname=$($KEYGEN -q -3 -r $RANDFILE $zone) cp $infile $zonefile # @@ -574,12 +575,12 @@ zone=badds.example. infile=bogus.example.db.in zonefile=badds.example.db -keyname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone` +keyname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n zone $zone) cat $infile $keyname.key >$zonefile -$SIGNER -P -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 -sed -e 's/bogus/badds/g' < dsset-bogus.example$TP > dsset-badds.example$TP +$SIGNER -P -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 +sed -e 's/bogus/badds/g' dsset-badds.example$TP # # A zone with future signatures. @@ -587,10 +588,10 @@ sed -e 's/bogus/badds/g' < dsset-bogus.example$TP > dsset-badds.example$TP zone=future.example infile=future.example.db.in zonefile=future.example.db -kskname=`$KEYGEN -q -r $RANDFILE -f KSK $zone` -zskname=`$KEYGEN -q -r $RANDFILE $zone` +kskname=$($KEYGEN -q -r $RANDFILE -f KSK $zone) +zskname=$($KEYGEN -q -r $RANDFILE $zone) cat $infile $kskname.key $zskname.key >$zonefile -$SIGNER -P -s +3600 -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 +$SIGNER -P -s +3600 -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 cp -f $kskname.key trusted-future.key # @@ -599,10 +600,10 @@ cp -f $kskname.key trusted-future.key zone=managed-future.example infile=managed-future.example.db.in zonefile=managed-future.example.db -kskname=`$KEYGEN -q -r $RANDFILE -f KSK $zone` -zskname=`$KEYGEN -q -r $RANDFILE $zone` +kskname=$($KEYGEN -q -r $RANDFILE -f KSK $zone) +zskname=$($KEYGEN -q -r $RANDFILE $zone) cat $infile $kskname.key $zskname.key >$zonefile -$SIGNER -P -s +3600 -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 +$SIGNER -P -s +3600 -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 # # A zone with a revoked key @@ -611,14 +612,14 @@ zone=revkey.example. infile=generic.example.db.in zonefile=revkey.example.db -ksk1=`$KEYGEN -q -r $RANDFILE -3fk $zone` -ksk1=`$REVOKE $ksk1` -ksk2=`$KEYGEN -q -r $RANDFILE -3fk $zone` -zsk1=`$KEYGEN -q -r $RANDFILE -3 $zone` +ksk1=$($KEYGEN -q -r $RANDFILE -3fk $zone) +ksk1=$($REVOKE $ksk1) +ksk2=$($KEYGEN -q -r $RANDFILE -3fk $zone) +zsk1=$($KEYGEN -q -r $RANDFILE -3 $zone) cat $infile ${ksk1}.key ${ksk2}.key ${zsk1}.key >$zonefile -$SIGNER -P -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 +$SIGNER -P -r $RANDFILE -o $zone $zonefile >/dev/null 2>&1 # # Check that NSEC3 are correctly signed and returned from below a DNAME @@ -626,10 +627,10 @@ $SIGNER -P -r $RANDFILE -o $zone $zonefile > /dev/null 2>&1 zone=dname-at-apex-nsec3.example infile=dname-at-apex-nsec3.example.db.in zonefile=dname-at-apex-nsec3.example.db -kskname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -3fk $zone` -zskname=`$KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -3 $zone` +kskname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -3fk $zone) +zskname=$($KEYGEN -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -3 $zone) cat $infile $kskname.key $zskname.key >$zonefile -$SIGNER -P -r $RANDFILE -3 - -o $zone $zonefile > /dev/null 2>&1 +$SIGNER -P -r $RANDFILE -3 - -o $zone $zonefile >/dev/null 2>&1 # # A NSEC zone with occuded data at the delegation @@ -637,11 +638,11 @@ $SIGNER -P -r $RANDFILE -3 - -o $zone $zonefile > /dev/null 2>&1 zone=occluded.example infile=occluded.example.db.in zonefile=occluded.example.db -kskname=`"$KEYGEN" -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -fk "$zone"` -zskname=`"$KEYGEN" -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS "$zone"` -dnskeyname=`"$KEYGEN" -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -fk "delegation.$zone"` -keyname=`"$KEYGEN" -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n ENTITY -T KEY "delegation.$zone"` -$DSFROMKEY "$dnskeyname.key" > "dsset-delegation.${zone}$TP" +kskname=$("$KEYGEN" -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -fk "$zone") +zskname=$("$KEYGEN" -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS "$zone") +dnskeyname=$("$KEYGEN" -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -fk "delegation.$zone") +keyname=$("$KEYGEN" -q -r $RANDFILE -a $DEFAULT_ALGORITHM -b $DEFAULT_BITS -n ENTITY -T KEY "delegation.$zone") +$DSFROMKEY "$dnskeyname.key" >"dsset-delegation.${zone}$TP" cat "$infile" "${kskname}.key" "${zskname}.key" "${keyname}.key" \ - "${dnskeyname}.key" "dsset-delegation.${zone}$TP" >"$zonefile" -"$SIGNER" -P -r $RANDFILE -o "$zone" "$zonefile" > /dev/null 2>&1 + "${dnskeyname}.key" "dsset-delegation.${zone}$TP" >"$zonefile" +"$SIGNER" -P -r $RANDFILE -o "$zone" "$zonefile" >/dev/null 2>&1 diff --git a/bin/tests/system/dnssec/tests.sh b/bin/tests/system/dnssec/tests.sh index 2f34b6d118..1eb97f42c5 100644 --- a/bin/tests/system/dnssec/tests.sh +++ b/bin/tests/system/dnssec/tests.sh @@ -743,6 +743,21 @@ n=`expr $n + 1` if [ $ret != 0 ]; then echo_i "failed"; fi status=`expr $status + $ret` +echo_i "checking mixed-case positive validation ($n)" +ret=0 +for type in a txt aaaa loc; do + $DIG $DIGOPTS +noauth mixedcase.secure.example. \ + @10.53.0.3 $type >dig.out.$type.ns3.test$n || ret=1 + $DIG $DIGOPTS +noauth mixedcase.secure.example. \ + @10.53.0.4 $type >dig.out.$type.ns4.test$n || ret=1 + digcomp --lc dig.out.$type.ns3.test$n dig.out.$type.ns4.test$n || ret=1 + grep "status: NOERROR" dig.out.$type.ns4.test$n >/dev/null || ret=1 + grep "flags:.*ad.*QUERY" dig.out.$type.ns4.test$n >/dev/null || ret=1 +done +n=$((n + 1)) +test "$ret" -eq 0 || echo_i "failed" +status=$((status + ret)) + echo_i "checking multi-stage positive validation NSEC/NSEC3 ($n)" ret=0 $DIG $DIGOPTS +noauth a.nsec3.example. \ diff --git a/lib/dns/catz.c b/lib/dns/catz.c index 304b038637..df101e3225 100644 --- a/lib/dns/catz.c +++ b/lib/dns/catz.c @@ -398,33 +398,21 @@ dns_catz_zones_merge(dns_catz_zone_t *target, dns_catz_zone_t *newzone) { dns_name_format(&target->name, czname, DNS_NAME_FORMATSIZE); - result = isc_ht_init(&toadd, target->catzs->mctx, 16); - if (result != ISC_R_SUCCESS) - goto cleanup; + isc_ht_init(&toadd, target->catzs->mctx, 16, ISC_HT_CASE_SENSITIVE); - result = isc_ht_init(&tomod, target->catzs->mctx, 16); - if (result != ISC_R_SUCCESS) - goto cleanup; + isc_ht_init(&tomod, target->catzs->mctx, 16, ISC_HT_CASE_SENSITIVE); - result = isc_ht_iter_create(newzone->entries, &iter1); - if (result != ISC_R_SUCCESS) - goto cleanup; + isc_ht_iter_create(newzone->entries, &iter1); - result = isc_ht_iter_create(target->entries, &iter2); - if (result != ISC_R_SUCCESS) - goto cleanup; + isc_ht_iter_create(target->entries, &iter2); /* * We can create those iterators now, even though toadd and tomod are * empty */ - result = isc_ht_iter_create(toadd, &iteradd); - if (result != ISC_R_SUCCESS) - goto cleanup; + isc_ht_iter_create(toadd, &iteradd); - result = isc_ht_iter_create(tomod, &itermod); - if (result != ISC_R_SUCCESS) - goto cleanup; + isc_ht_iter_create(tomod, &itermod); /* * First - walk the new zone and find all nodes that are not in the @@ -570,7 +558,6 @@ dns_catz_zones_merge(dns_catz_zone_t *target, dns_catz_zone_t *newzone) { result = ISC_R_SUCCESS; -cleanup: if (iter1 != NULL) isc_ht_iter_destroy(&iter1); if (iter2 != NULL) @@ -610,9 +597,7 @@ dns_catz_new_zones(dns_catz_zones_t **catzsp, dns_catz_zonemodmethods_t *zmm, if (result != ISC_R_SUCCESS) goto cleanup_mutex; - result = isc_ht_init(&new_zones->zones, mctx, 4); - if (result != ISC_R_SUCCESS) - goto cleanup_refcount; + isc_ht_init(&new_zones->zones, mctx, 4, ISC_HT_CASE_SENSITIVE); isc_mem_attach(mctx, &new_zones->mctx); new_zones->zmm = zmm; @@ -629,7 +614,6 @@ dns_catz_new_zones(dns_catz_zones_t **catzsp, dns_catz_zonemodmethods_t *zmm, cleanup_ht: isc_ht_destroy(&new_zones->zones); - cleanup_refcount: isc_refcount_destroy(&new_zones->refs); cleanup_mutex: isc_mutex_destroy(&new_zones->lock); @@ -672,9 +656,7 @@ dns_catz_new_zone(dns_catz_zones_t *catzs, dns_catz_zone_t **zonep, if (result != ISC_R_SUCCESS) goto cleanup_newzone; - result = isc_ht_init(&new_zone->entries, catzs->mctx, 4); - if (result != ISC_R_SUCCESS) - goto cleanup_name; + isc_ht_init(&new_zone->entries, catzs->mctx, 4, ISC_HT_CASE_SENSITIVE); new_zone->updatetimer = NULL; result = isc_timer_create(catzs->timermgr, isc_timertype_inactive, @@ -703,7 +685,6 @@ dns_catz_new_zone(dns_catz_zones_t *catzs, dns_catz_zone_t **zonep, cleanup_ht: isc_ht_destroy(&new_zone->entries); - cleanup_name: dns_name_free(&new_zone->name, catzs->mctx); cleanup_newzone: isc_mem_put(catzs->mctx, new_zone, sizeof(*new_zone)); @@ -805,8 +786,7 @@ dns_catz_zone_detach(dns_catz_zone_t **zonep) { if (refs == 0) { isc_mem_t *mctx = zone->catzs->mctx; if (zone->entries != NULL) { - result = isc_ht_iter_create(zone->entries, &iter); - INSIST(result == ISC_R_SUCCESS); + isc_ht_iter_create(zone->entries, &iter); for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS; result = isc_ht_iter_delcurrent_next(iter)) @@ -865,8 +845,7 @@ dns_catz_catzs_detach(dns_catz_zones_t **catzsp) { catzs->magic = 0; DESTROYLOCK(&catzs->lock); if (catzs->zones != NULL) { - result = isc_ht_iter_create(catzs->zones, &iter); - INSIST(result == ISC_R_SUCCESS); + isc_ht_iter_create(catzs->zones, &iter); for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS;) { @@ -1996,8 +1975,7 @@ dns_catz_prereconfig(dns_catz_zones_t *catzs) { REQUIRE(DNS_CATZ_ZONES_VALID(catzs)); - result = isc_ht_iter_create(catzs->zones, &iter); - INSIST(result == ISC_R_SUCCESS); + isc_ht_iter_create(catzs->zones, &iter); for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS; result = isc_ht_iter_next(iter)) @@ -2019,8 +1997,7 @@ dns_catz_postreconfig(dns_catz_zones_t *catzs) { REQUIRE(DNS_CATZ_ZONES_VALID(catzs)); LOCK(&catzs->lock); - result = isc_ht_iter_create(catzs->zones, &iter); - INSIST(result == ISC_R_SUCCESS); + isc_ht_iter_create(catzs->zones, &iter); for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS;) { @@ -2062,5 +2039,6 @@ dns_catz_postreconfig(dns_catz_zones_t *catzs) { isc_result_t dns_catz_get_iterator(dns_catz_zone_t *catz, isc_ht_iter_t **itp) { REQUIRE(DNS_CATZ_ZONE_VALID(catz)); - return (isc_ht_iter_create(catz->entries, itp)); + isc_ht_iter_create(catz->entries, itp); + return (ISC_R_SUCCESS); } diff --git a/lib/dns/include/dns/message.h b/lib/dns/include/dns/message.h index f64522b43a..96c5ef1f79 100644 --- a/lib/dns/include/dns/message.h +++ b/lib/dns/include/dns/message.h @@ -758,45 +758,6 @@ dns_message_findtype(dns_name_t *name, dns_rdatatype_t type, *\li #ISC_R_NOTFOUND -- the desired type does not exist. */ -isc_result_t -dns_message_find(dns_name_t *name, dns_rdataclass_t rdclass, - dns_rdatatype_t type, dns_rdatatype_t covers, - dns_rdataset_t **rdataset); -/*%< - * Search the name for the specified rdclass and type. If it is found, - * *rdataset is filled in with a pointer to that rdataset. - * - * Requires: - *\li if '**rdataset' is non-NULL, *rdataset needs to be NULL. - * - *\li 'type' be a valid type, and NOT dns_rdatatype_any. - * - *\li If 'type' is dns_rdatatype_rrsig, 'covers' must be a valid type. - * Otherwise it should be 0. - * - * Returns: - *\li #ISC_R_SUCCESS -- all is well. - *\li #ISC_R_NOTFOUND -- the desired type does not exist. - */ - -void -dns_message_movename(dns_message_t *msg, dns_name_t *name, - dns_section_t fromsection, - dns_section_t tosection); -/*%< - * Move a name from one section to another. - * - * Requires: - * - *\li 'msg' be valid. - * - *\li 'name' must be a name already in 'fromsection'. - * - *\li 'fromsection' must be a valid section. - * - *\li 'tosection' must be a valid section. - */ - void dns_message_addname(dns_message_t *msg, dns_name_t *name, dns_section_t section); diff --git a/lib/dns/include/dns/name.h b/lib/dns/include/dns/name.h index 0ca5d47546..2b8c0390ff 100644 --- a/lib/dns/include/dns/name.h +++ b/lib/dns/include/dns/name.h @@ -68,6 +68,7 @@ #include #include +#include #include #include #include /* Required for storage size of dns_label_t. */ @@ -111,6 +112,7 @@ struct dns_name { isc_buffer_t * buffer; ISC_LINK(dns_name_t) link; ISC_LIST(dns_rdataset_t) list; + isc_ht_t *ht; }; #define DNS_NAME_MAGIC ISC_MAGIC('D','N','S','n') @@ -171,7 +173,7 @@ LIBDNS_EXTERNAL_DATA extern dns_name_t *dns_wildcardname; A, (sizeof(A) - 1), sizeof(B), \ DNS_NAMEATTR_READONLY, \ B, NULL, { (void *)-1, (void *)-1}, \ - {NULL, NULL} \ + {NULL, NULL}, NULL \ } #define DNS_NAME_INITABSOLUTE(A,B) { \ @@ -179,12 +181,12 @@ LIBDNS_EXTERNAL_DATA extern dns_name_t *dns_wildcardname; A, sizeof(A), sizeof(B), \ DNS_NAMEATTR_READONLY | DNS_NAMEATTR_ABSOLUTE, \ B, NULL, { (void *)-1, (void *)-1}, \ - {NULL, NULL} \ + {NULL, NULL}, NULL \ } #define DNS_NAME_INITEMPTY { \ DNS_NAME_MAGIC, NULL, 0, 0, 0, NULL, NULL, \ - { (void *)-1, (void *)-1 }, { NULL, NULL } \ + { (void *)-1, (void *)-1 }, { NULL, NULL }, NULL \ } /*% @@ -1374,6 +1376,7 @@ do { \ _n->buffer = NULL; \ ISC_LINK_INIT(_n, link); \ ISC_LIST_INIT(_n->list); \ + _n->ht = NULL; \ } while (0) #define DNS_NAME_RESET(n) \ diff --git a/lib/dns/message.c b/lib/dns/message.c index 2812ab5a37..a44eb2d1e9 100644 --- a/lib/dns/message.c +++ b/lib/dns/message.c @@ -21,6 +21,8 @@ #include #include +#include +#include #include #include #include @@ -189,6 +191,9 @@ msgblock_allocate(isc_mem_t *, unsigned int, unsigned int); #define msgblock_get(block, type) \ ((type *)msgblock_internalget(block, sizeof(type))) +static void +dns__message_puttemprdataset(dns_message_t *msg, dns_rdataset_t **rdatasetp); + static inline void * msgblock_internalget(dns_msgblock_t *, unsigned int); @@ -502,7 +507,7 @@ msgresetopt(dns_message_t *msg) } INSIST(dns_rdataset_isassociated(msg->opt)); dns_rdataset_disassociate(msg->opt); - isc_mempool_put(msg->rdspool, msg->opt); + dns__message_puttemprdataset(msg, &msg->opt); msg->opt = NULL; msg->cc_ok = 0; msg->cc_bad = 0; @@ -523,10 +528,11 @@ msgresetsigs(dns_message_t *msg, bool replying) { msg->querytsig = msg->tsig; } else { dns_rdataset_disassociate(msg->tsig); - isc_mempool_put(msg->rdspool, msg->tsig); + dns__message_puttemprdataset(msg, &msg->tsig); if (msg->querytsig != NULL) { dns_rdataset_disassociate(msg->querytsig); - isc_mempool_put(msg->rdspool, msg->querytsig); + dns__message_puttemprdataset(msg, + &msg->querytsig); } } if (dns_name_dynamic(msg->tsigname)) @@ -536,13 +542,10 @@ msgresetsigs(dns_message_t *msg, bool replying) { msg->tsigname = NULL; } else if (msg->querytsig != NULL && !replying) { dns_rdataset_disassociate(msg->querytsig); - isc_mempool_put(msg->rdspool, msg->querytsig); - msg->querytsig = NULL; + dns__message_puttemprdataset(msg, &msg->querytsig); } if (msg->sig0 != NULL) { - INSIST(dns_rdataset_isassociated(msg->sig0)); - dns_rdataset_disassociate(msg->sig0); - isc_mempool_put(msg->rdspool, msg->sig0); + dns__message_puttemprdataset(msg, &msg->sig0); if (msg->sig0name != NULL) { if (dns_name_dynamic(msg->sig0name)) dns_name_free(msg->sig0name, msg->mctx); @@ -863,6 +866,18 @@ dns_message_detach(dns_message_t **messagep) { } } +static isc_result_t +name_hash_add(isc_ht_t *ht, dns_name_t *name, dns_name_t **foundp) { + isc_result_t result = isc_ht_find(ht, name->ndata, name->length, + (void **)foundp); + if (result == ISC_R_SUCCESS) { + return (ISC_R_EXISTS); + } + result = isc_ht_add(ht, name->ndata, name->length, (void *)name); + INSIST(result == ISC_R_SUCCESS); + return (ISC_R_SUCCESS); +} + static isc_result_t findname(dns_name_t **foundname, dns_name_t *target, dns_namelist_t *section) @@ -882,28 +897,36 @@ findname(dns_name_t **foundname, dns_name_t *target, return (ISC_R_NOTFOUND); } -isc_result_t -dns_message_find(dns_name_t *name, dns_rdataclass_t rdclass, - dns_rdatatype_t type, dns_rdatatype_t covers, - dns_rdataset_t **rdataset) -{ - dns_rdataset_t *curr; - - REQUIRE(name != NULL); - REQUIRE(rdataset == NULL || *rdataset == NULL); +#ifdef _WIN32 +__pragma(pack(push, 1)) +typedef struct rds_key { + dns_rdataclass_t rdclass; + dns_rdatatype_t type; + dns_rdatatype_t covers; +} rds_key_t; +__pragma(pack(pop)) +#else +typedef struct __attribute__((__packed__)) rds_key { + dns_rdataclass_t rdclass; + dns_rdatatype_t type; + dns_rdatatype_t covers; +} rds_key_t; +#endif - for (curr = ISC_LIST_TAIL(name->list); - curr != NULL; - curr = ISC_LIST_PREV(curr, link)) { - if (curr->rdclass == rdclass && - curr->type == type && curr->covers == covers) { - if (rdataset != NULL) - *rdataset = curr; - return (ISC_R_SUCCESS); - } +static isc_result_t +rds_hash_add(isc_ht_t *ht, dns_rdataset_t *rds, dns_rdataset_t **foundp) { + rds_key_t key = { .rdclass = rds->rdclass, + .type = rds->type, + .covers = rds->covers }; + isc_result_t result = isc_ht_find(ht, (const unsigned char *)&key, + sizeof(key), (void **)foundp); + if (result == ISC_R_SUCCESS) { + return (ISC_R_EXISTS); } - - return (ISC_R_NOTFOUND); + result = isc_ht_add(ht, (const unsigned char *)&key, sizeof(key), + (void *)rds); + INSIST(result == ISC_R_SUCCESS); + return (ISC_R_SUCCESS); } isc_result_t @@ -1031,6 +1054,18 @@ getrdata(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, } \ } while (0) +static void +cleanup_name_hashmaps(dns_namelist_t *section) { + dns_name_t *name = NULL; + for (name = ISC_LIST_HEAD(*section); name != NULL; + name = ISC_LIST_NEXT(name, link)) + { + if (name->ht != NULL) { + isc_ht_destroy(&name->ht); + } + } +} + static isc_result_t getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, unsigned int options) @@ -1042,13 +1077,15 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, dns_offsets_t *offsets; dns_rdataset_t *rdataset; dns_rdatalist_t *rdatalist; - isc_result_t result; + isc_result_t result = ISC_R_SUCCESS; dns_rdatatype_t rdtype; dns_rdataclass_t rdclass; dns_namelist_t *section; - bool free_name; + bool free_name = false; bool best_effort; bool seen_problem; + isc_ht_t *name_map = NULL; + bool free_ht = false; section = &msg->sections[DNS_SECTION_QUESTION]; @@ -1056,9 +1093,14 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, seen_problem = false; name = NULL; + name2 = NULL; rdataset = NULL; rdatalist = NULL; + if (msg->counts[DNS_SECTION_QUESTION] > 1) { + isc_ht_init(&name_map, msg->mctx, 1, ISC_HT_CASE_INSENSITIVE); + } + for (count = 0; count < msg->counts[DNS_SECTION_QUESTION]; count++) { name = isc_mempool_get(msg->namepool); if (name == NULL) @@ -1081,13 +1123,20 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, if (result != ISC_R_SUCCESS) goto cleanup; + + /* If there is only one QNAME, skip the duplicity checks */ + if (name_map == NULL) { + result = ISC_R_SUCCESS; + goto skip_name_check; + } + /* * Run through the section, looking to see if this name * is already there. If it is found, put back the allocated * name since we no longer need it, and set our name pointer * to point to the name we found. */ - result = findname(&name2, name, section); + result = name_hash_add(name_map, name, &name2); /* * If it is the first name in the section, accept it. @@ -1099,18 +1148,25 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, * this should be legal or not. In either case we no longer * need this name pointer. */ - if (result != ISC_R_SUCCESS) { - if (!ISC_LIST_EMPTY(*section)) + skip_name_check: + switch (result) { + case ISC_R_SUCCESS: + if (!ISC_LIST_EMPTY(*section)) { DO_ERROR(DNS_R_FORMERR); + } ISC_LIST_APPEND(*section, name, link); - free_name = false; - } else { - isc_mempool_put(msg->namepool, name); + break; + case ISC_R_EXISTS: + dns_message_puttempname(msg, &name); name = name2; name2 = NULL; - free_name = false; + break; + default: + ISC_UNREACHABLE(); } + free_name = false; + /* * Get type and class. */ @@ -1138,13 +1194,6 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, if (rdtype == dns_rdatatype_tkey) msg->tkey = 1; - /* - * Can't ask the same question twice. - */ - result = dns_message_find(name, rdclass, rdtype, 0, NULL); - if (result == ISC_R_SUCCESS) - DO_ERROR(DNS_R_FORMERR); - /* * Allocate a new rdatalist. */ @@ -1153,7 +1202,7 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, result = ISC_R_NOMEMORY; goto cleanup; } - rdataset = isc_mempool_get(msg->rdspool); + dns_message_gettemprdataset(msg, &rdataset); if (rdataset == NULL) { result = ISC_R_NOMEMORY; goto cleanup; @@ -1166,32 +1215,74 @@ getquestions(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, rdatalist->type = rdtype; rdatalist->rdclass = rdclass; - dns_rdataset_init(rdataset); result = dns_rdatalist_tordataset(rdatalist, rdataset); - if (result != ISC_R_SUCCESS) - goto cleanup; + RUNTIME_CHECK(result == ISC_R_SUCCESS); rdataset->attributes |= DNS_RDATASETATTR_QUESTION; + /* + * Skip the duplicity check for first rdataset + */ + if (ISC_LIST_EMPTY(name->list)) { + goto skip_rds_check; + } + + /* + * Can't ask the same question twice. + */ + if (name->ht == NULL) { + isc_ht_init(&name->ht, msg->mctx, 1, + ISC_HT_CASE_SENSITIVE); + free_ht = true; + + dns_rdataset_t *old_rdataset = NULL; + for (old_rdataset = ISC_LIST_HEAD(name->list); + old_rdataset != NULL; + old_rdataset = ISC_LIST_NEXT(old_rdataset, link)) + { + result = rds_hash_add(name->ht, old_rdataset, + NULL); + INSIST(result == ISC_R_SUCCESS); + } + } + result = rds_hash_add(name->ht, rdataset, NULL); + if (result == ISC_R_EXISTS) { + DO_ERROR(DNS_R_FORMERR); + } + + skip_rds_check: ISC_LIST_APPEND(name->list, rdataset, link); + rdataset = NULL; } - if (seen_problem) - return (DNS_R_RECOVERABLE); - return (ISC_R_SUCCESS); + + if (seen_problem) { + result = DNS_R_RECOVERABLE; + } cleanup: if (rdataset != NULL) { - INSIST(!dns_rdataset_isassociated(rdataset)); - isc_mempool_put(msg->rdspool, rdataset); + if (dns_rdataset_isassociated(rdataset)) { + dns_rdataset_disassociate(rdataset); + } + dns_message_puttemprdataset(msg, &rdataset); } #if 0 if (rdatalist != NULL) isc_mempool_put(msg->rdlpool, rdatalist); #endif - if (free_name) - isc_mempool_put(msg->namepool, name); + if (free_name) { + dns_message_puttempname(msg, &name); + } + + if (free_ht) { + cleanup_name_hashmaps(section); + } + + if (name_map != NULL) { + isc_ht_destroy(&name_map); + } return (result); } @@ -1271,24 +1362,26 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, unsigned int count, rdatalen; dns_name_t *name = NULL; dns_name_t *name2 = NULL; - dns_offsets_t *offsets; - dns_rdataset_t *rdataset; - dns_rdatalist_t *rdatalist; - isc_result_t result; + dns_offsets_t *offsets = NULL; + dns_rdataset_t *rdataset = NULL; + dns_rdataset_t *found_rdataset = NULL; + dns_rdatalist_t *rdatalist = NULL; + isc_result_t result = ISC_R_SUCCESS; dns_rdatatype_t rdtype, covers; dns_rdataclass_t rdclass; - dns_rdata_t *rdata; + dns_rdata_t *rdata = NULL; dns_ttl_t ttl; - dns_namelist_t *section; - bool free_name = false, free_rdataset = false; - bool preserve_order, best_effort, seen_problem; + dns_namelist_t *section = &msg->sections[sectionid]; + bool free_name = false, seen_problem = false; + bool free_ht = false; + bool preserve_order = ((options & DNS_MESSAGEPARSE_PRESERVEORDER) != 0); + bool best_effort = ((options & DNS_MESSAGEPARSE_BESTEFFORT) != 0); bool isedns, issigzero, istsig; + isc_ht_t *name_map = NULL; - preserve_order = ((options & DNS_MESSAGEPARSE_PRESERVEORDER) != 0); - best_effort = ((options & DNS_MESSAGEPARSE_BESTEFFORT) != 0); - seen_problem = false; - - section = &msg->sections[sectionid]; + if (msg->counts[sectionid] > 1) { + isc_ht_init(&name_map, msg->mctx, 1, ISC_HT_CASE_INSENSITIVE); + } for (count = 0; count < msg->counts[sectionid]; count++) { int recstart = source->current; @@ -1296,14 +1389,15 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, skip_name_search = false; skip_type_search = false; - free_rdataset = false; isedns = false; issigzero = false; istsig = false; + found_rdataset = NULL; name = isc_mempool_get(msg->namepool); - if (name == NULL) + if (name == NULL) { return (ISC_R_NOMEMORY); + } free_name = true; offsets = newoffsets(msg); @@ -1319,8 +1413,9 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, isc_buffer_remainingregion(source, &r); isc_buffer_setactive(source, r.length); result = getname(name, source, msg, dctx); - if (result != ISC_R_SUCCESS) + if (result != ISC_R_SUCCESS) { goto cleanup; + } /* * Get type, class, ttl, and rdatalen. Verify that at least @@ -1340,9 +1435,10 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, * established a class. Do so now. */ if (msg->rdclass_set == 0 && - rdtype != dns_rdatatype_opt && /* class is UDP SIZE */ - rdtype != dns_rdatatype_tsig && /* class is ANY */ - rdtype != dns_rdatatype_tkey) { /* class is undefined */ + rdtype != dns_rdatatype_opt && /* class is UDP SIZE */ + rdtype != dns_rdatatype_tsig && /* class is ANY */ + rdtype != dns_rdatatype_tkey) /* class is undefined */ + { msg->rdclass = rdclass; msg->rdclass_set = 1; } @@ -1351,15 +1447,17 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, * If this class is different than the one in the question * section, bail. */ - if (msg->opcode != dns_opcode_update - && rdtype != dns_rdatatype_tsig - && rdtype != dns_rdatatype_opt - && rdtype != dns_rdatatype_key /* in a TKEY query */ - && rdtype != dns_rdatatype_sig /* SIG(0) */ - && rdtype != dns_rdatatype_tkey /* Win2000 TKEY */ - && msg->rdclass != dns_rdataclass_any - && msg->rdclass != rdclass) + if (msg->opcode != dns_opcode_update && + rdtype != dns_rdatatype_tsig && + rdtype != dns_rdatatype_opt && + rdtype != dns_rdatatype_key && /* in a TKEY query */ + rdtype != dns_rdatatype_sig && /* SIG(0) */ + rdtype != dns_rdatatype_tkey && /* Win2000 TKEY */ + msg->rdclass != dns_rdataclass_any && + msg->rdclass != rdclass) + { DO_ERROR(DNS_R_FORMERR); + } /* * If this is not a TKEY query/response then the KEY @@ -1369,7 +1467,9 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, rdtype == dns_rdatatype_key && msg->rdclass != dns_rdataclass_any && msg->rdclass != rdclass) + { DO_ERROR(DNS_R_FORMERR); + } /* * Special type handling for TSIG, OPT, and TKEY. @@ -1381,7 +1481,8 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, */ if (sectionid != DNS_SECTION_ADDITIONAL || rdclass != dns_rdataclass_any || - count != msg->counts[sectionid] - 1) { + count != msg->counts[sectionid] - 1) + { DO_ERROR(DNS_R_BADTSIG); } else { skip_name_search = true; @@ -1396,7 +1497,8 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, */ if (!dns_name_equal(dns_rootname, name) || sectionid != DNS_SECTION_ADDITIONAL || - msg->opt != NULL) { + msg->opt != NULL) + { DO_ERROR(DNS_R_FORMERR); } else { skip_name_search = true; @@ -1413,13 +1515,16 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, */ dns_section_t tkeysection; - if ((msg->flags & DNS_MESSAGEFLAG_QR) == 0) + if ((msg->flags & DNS_MESSAGEFLAG_QR) == 0) { tkeysection = DNS_SECTION_ADDITIONAL; - else + } else { tkeysection = DNS_SECTION_ANSWER; + } if (sectionid != tkeysection && sectionid != DNS_SECTION_ANSWER) + { DO_ERROR(DNS_R_FORMERR); + } } /* @@ -1445,7 +1550,8 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, goto cleanup; } if (msg->opcode == dns_opcode_update && - update(sectionid, rdclass)) { + update(sectionid, rdclass)) + { if (rdatalen != 0) { result = DNS_R_FORMERR; goto cleanup; @@ -1465,26 +1571,31 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, result = ISC_R_SUCCESS; } else if (rdclass == dns_rdataclass_none && msg->opcode == dns_opcode_update && - sectionid == DNS_SECTION_UPDATE) { + sectionid == DNS_SECTION_UPDATE) + { result = getrdata(source, msg, dctx, msg->rdclass, rdtype, rdatalen, rdata); - } else - result = getrdata(source, msg, dctx, rdclass, - rdtype, rdatalen, rdata); - if (result != ISC_R_SUCCESS) + } else { + result = getrdata(source, msg, dctx, rdclass, rdtype, + rdatalen, rdata); + } + if (result != ISC_R_SUCCESS) { goto cleanup; + } rdata->rdclass = rdclass; - if (rdtype == dns_rdatatype_rrsig && - rdata->flags == 0) { + if (rdtype == dns_rdatatype_rrsig && rdata->flags == 0) { covers = dns_rdata_covers(rdata); - if (covers == 0) + if (covers == 0) { DO_ERROR(DNS_R_FORMERR); + } } else if (rdtype == dns_rdatatype_sig /* SIG(0) */ && - rdata->flags == 0) { + rdata->flags == 0) + { covers = dns_rdata_covers(rdata); if (covers == 0) { if (sectionid != DNS_SECTION_ADDITIONAL || - count != msg->counts[sectionid] - 1) { + count != msg->counts[sectionid] - 1) + { DO_ERROR(DNS_R_BADSIG0); } else { skip_name_search = true; @@ -1494,17 +1605,20 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, } else { if (msg->rdclass != dns_rdataclass_any && msg->rdclass != rdclass) + { DO_ERROR(DNS_R_FORMERR); + } } - } else + } else { covers = 0; + } /* * Check the ownername of NSEC3 records */ if (rdtype == dns_rdatatype_nsec3 && - !dns_rdata_checkowner(name, msg->rdclass, rdtype, - false)) { + !dns_rdata_checkowner(name, msg->rdclass, rdtype, false)) + { result = DNS_R_BADOWNERNAME; goto cleanup; } @@ -1515,103 +1629,152 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, * to the end of the message. */ if (preserve_order || msg->opcode == dns_opcode_update || - skip_name_search) { + skip_name_search) + { if (!isedns && !istsig && !issigzero) { ISC_LIST_APPEND(*section, name, link); free_name = false; } } else { + if (name_map == NULL) { + result = ISC_R_SUCCESS; + goto skip_name_check; + } + /* * Run through the section, looking to see if this name * is already there. If it is found, put back the * allocated name since we no longer need it, and set * our name pointer to point to the name we found. */ - result = findname(&name2, name, section); + result = name_hash_add(name_map, name, &name2); /* * If it is a new name, append to the section. */ - if (result == ISC_R_SUCCESS) { - isc_mempool_put(msg->namepool, name); - name = name2; - } else { + skip_name_check: + switch (result) { + case ISC_R_SUCCESS: ISC_LIST_APPEND(*section, name, link); + break; + case ISC_R_EXISTS: + dns_message_puttempname(msg, &name); + name = name2; + name2 = NULL; + break; + default: + ISC_UNREACHABLE(); } free_name = false; } + dns_message_gettemprdataset(msg, &rdataset); + if (rdataset == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + + rdatalist = newrdatalist(msg); + if (rdatalist == NULL) { + result = ISC_R_NOMEMORY; + goto cleanup; + } + + rdatalist->type = rdtype; + rdatalist->covers = covers; + rdatalist->rdclass = rdclass; + rdatalist->ttl = ttl; + + RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist, rdataset) == + ISC_R_SUCCESS); + dns_rdataset_setownercase(rdataset, name); + rdatalist = NULL; + /* * Search name for the particular type and class. * Skip this stage if in update mode or this is a meta-type. */ - if (preserve_order || msg->opcode == dns_opcode_update || - skip_type_search) - result = ISC_R_NOTFOUND; - else { + if (isedns || istsig || issigzero) { + /* Skip adding the rdataset to the tables */ + } else if (preserve_order || msg->opcode == dns_opcode_update || + skip_type_search) + { + result = ISC_R_SUCCESS; + + ISC_LIST_APPEND(name->list, rdataset, link); + } else { /* * If this is a type that can only occur in * the question section, fail. */ - if (dns_rdatatype_questiononly(rdtype)) + if (dns_rdatatype_questiononly(rdtype)) { DO_ERROR(DNS_R_FORMERR); - - rdataset = NULL; - result = dns_message_find(name, rdclass, rdtype, - covers, &rdataset); - } - - /* - * If we found an rdataset that matches, we need to - * append this rdata to that set. If we did not, we need - * to create a new rdatalist, store the important bits there, - * convert it to an rdataset, and link the latter to the name. - * Yuck. When appending, make certain that the type isn't - * a singleton type, such as SOA or CNAME. - * - * Note that this check will be bypassed when preserving order, - * the opcode is an update, or the type search is skipped. - */ - if (result == ISC_R_SUCCESS) { - if (dns_rdatatype_issingleton(rdtype)) { - dns_rdata_t *first; - dns_rdatalist_fromrdataset(rdataset, - &rdatalist); - first = ISC_LIST_HEAD(rdatalist->rdata); - INSIST(first != NULL); - if (dns_rdata_compare(rdata, first) != 0) - DO_ERROR(DNS_R_FORMERR); } - } - if (result == ISC_R_NOTFOUND) { - rdataset = isc_mempool_get(msg->rdspool); - if (rdataset == NULL) { - result = ISC_R_NOMEMORY; - goto cleanup; + if (ISC_LIST_EMPTY(name->list)) { + result = ISC_R_SUCCESS; + goto skip_rds_check; } - free_rdataset = true; - rdatalist = newrdatalist(msg); - if (rdatalist == NULL) { - result = ISC_R_NOMEMORY; - goto cleanup; - } + if (name->ht == NULL) { + isc_ht_init(&name->ht, msg->mctx, 1, + ISC_HT_CASE_SENSITIVE); + free_ht = true; - rdatalist->type = rdtype; - rdatalist->covers = covers; - rdatalist->rdclass = rdclass; - rdatalist->ttl = ttl; + INSIST(ISC_LIST_HEAD(name->list) == + ISC_LIST_TAIL(name->list)); - dns_rdataset_init(rdataset); - RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist, - rdataset) - == ISC_R_SUCCESS); - dns_rdataset_setownercase(rdataset, name); + dns_rdataset_t *old_rdataset = + ISC_LIST_HEAD(name->list); - if (!isedns && !istsig && !issigzero) { + result = rds_hash_add(name->ht, old_rdataset, + NULL); + + INSIST(result == ISC_R_SUCCESS); + } + result = rds_hash_add(name->ht, rdataset, + &found_rdataset); + + /* + * If we found an rdataset that matches, we need to + * append this rdata to that set. If we did not, we + * need to create a new rdatalist, store the important + * bits there, convert it to an rdataset, and link the + * latter to the name. Yuck. When appending, make + * certain that the type isn't a singleton type, such as + * SOA or CNAME. + * + * Note that this check will be bypassed when preserving + * order, the opcode is an update, or the type search is + * skipped. + */ + skip_rds_check: + switch (result) { + case ISC_R_EXISTS: + /* Free the rdataset we used as the key */ + dns_rdataset_disassociate(rdataset); + dns__message_puttemprdataset(msg, &rdataset); + result = ISC_R_SUCCESS; + rdataset = found_rdataset; + + if (!dns_rdatatype_issingleton(rdtype)) { + break; + } + + dns_rdatalist_fromrdataset(rdataset, + &rdatalist); + dns_rdata_t *first = + ISC_LIST_HEAD(rdatalist->rdata); + INSIST(first != NULL); + if (dns_rdata_compare(rdata, first) != 0) { + DO_ERROR(DNS_R_FORMERR); + } + break; + case ISC_R_SUCCESS: ISC_LIST_APPEND(name->list, rdataset, link); - free_rdataset = false; + break; + default: + ISC_UNREACHABLE(); } } @@ -1625,8 +1788,9 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, */ if (ttl != rdataset->ttl) { rdataset->attributes |= DNS_RDATASETATTR_TTLADJUSTED; - if (ttl < rdataset->ttl) + if (ttl < rdataset->ttl) { rdataset->ttl = ttl; + } } /* Append this rdata to the rdataset. */ @@ -1645,20 +1809,16 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, dns_rcode_t ercode; msg->opt = rdataset; - rdataset = NULL; - free_rdataset = false; ercode = (dns_rcode_t) ((msg->opt->ttl & DNS_MESSAGE_EDNSRCODE_MASK) >> 20); msg->rcode |= ercode; - isc_mempool_put(msg->namepool, name); + dns_message_puttempname(msg, &name); free_name = false; } else if (issigzero) { msg->sig0 = rdataset; msg->sig0name = name; msg->sigstart = recstart; - rdataset = NULL; - free_rdataset = false; free_name = false; } else if (istsig) { msg->tsig = rdataset; @@ -1668,20 +1828,17 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, * Windows doesn't like TSIG names to be compressed. */ msg->tsigname->attributes |= DNS_NAMEATTR_NOCOMPRESS; - rdataset = NULL; - free_rdataset = false; free_name = false; } + rdataset = NULL; if (seen_problem) { - if (free_name) - isc_mempool_put(msg->namepool, name); - if (free_rdataset) - isc_mempool_put(msg->rdspool, rdataset); - free_name = free_rdataset = false; + if (free_name) { + dns_message_puttempname(msg, &name); + } + free_name = false; } INSIST(free_name == false); - INSIST(free_rdataset == false); } /* @@ -1692,20 +1849,32 @@ getsection(isc_buffer_t *source, dns_message_t *msg, dns_decompress_t *dctx, if (sectionid == DNS_SECTION_AUTHORITY && msg->opcode == dns_opcode_query && ((msg->flags & DNS_MESSAGEFLAG_QR) != 0) && - ((msg->flags & DNS_MESSAGEFLAG_TC) == 0) && - !preserve_order && + ((msg->flags & DNS_MESSAGEFLAG_TC) == 0) && !preserve_order && !auth_signed(section)) + { DO_ERROR(DNS_R_FORMERR); + } - if (seen_problem) - return (DNS_R_RECOVERABLE); - return (ISC_R_SUCCESS); + if (seen_problem) { + result = DNS_R_RECOVERABLE; + } - cleanup: - if (free_name) - isc_mempool_put(msg->namepool, name); - if (free_rdataset) +cleanup: + if (rdataset != NULL && rdataset != found_rdataset) { + dns_rdataset_disassociate(rdataset); isc_mempool_put(msg->rdspool, rdataset); + } + if (free_name) { + dns_message_puttempname(msg, &name); + } + + if (free_ht) { + cleanup_name_hashmaps(section); + } + + if (name_map != NULL) { + isc_ht_destroy(&name_map); + } return (result); } @@ -2438,11 +2607,11 @@ dns_message_renderreset(dns_message_t *msg) { dns_message_puttempname(msg, &msg->tsigname); if (msg->tsig != NULL) { dns_rdataset_disassociate(msg->tsig); - dns_message_puttemprdataset(msg, &msg->tsig); + dns__message_puttemprdataset(msg, &msg->tsig); } if (msg->sig0 != NULL) { dns_rdataset_disassociate(msg->sig0); - dns_message_puttemprdataset(msg, &msg->sig0); + dns__message_puttemprdataset(msg, &msg->sig0); } } @@ -2535,24 +2704,6 @@ dns_message_findname(dns_message_t *msg, dns_section_t section, return (result); } -void -dns_message_movename(dns_message_t *msg, dns_name_t *name, - dns_section_t fromsection, - dns_section_t tosection) -{ - REQUIRE(msg != NULL); - REQUIRE(msg->from_to_wire == DNS_MESSAGE_INTENTRENDER); - REQUIRE(name != NULL); - REQUIRE(VALID_NAMED_SECTION(fromsection)); - REQUIRE(VALID_NAMED_SECTION(tosection)); - - /* - * Unlink the name from the old section - */ - ISC_LIST_UNLINK(msg->sections[fromsection], name, link); - ISC_LIST_APPEND(msg->sections[tosection], name, link); -} - void dns_message_addname(dns_message_t *msg, dns_name_t *name, dns_section_t section) @@ -2645,6 +2796,9 @@ dns_message_puttempname(dns_message_t *msg, dns_name_t **item) { REQUIRE(DNS_MESSAGE_VALID(msg)); REQUIRE(item != NULL && *item != NULL); + if ((*item)->ht != NULL) { + isc_ht_destroy(&(*item)->ht); + } if (dns_name_dynamic(*item)) dns_name_free(*item, msg->mctx); isc_mempool_put(msg->namepool, *item); @@ -2660,14 +2814,19 @@ dns_message_puttemprdata(dns_message_t *msg, dns_rdata_t **item) { *item = NULL; } +static void +dns__message_puttemprdataset(dns_message_t *msg, dns_rdataset_t **item) { + isc_mempool_put(msg->rdspool, *item); + *item = NULL; +} + void dns_message_puttemprdataset(dns_message_t *msg, dns_rdataset_t **item) { REQUIRE(DNS_MESSAGE_VALID(msg)); REQUIRE(item != NULL && *item != NULL); REQUIRE(!dns_rdataset_isassociated(*item)); - isc_mempool_put(msg->rdspool, *item); - *item = NULL; + dns__message_puttemprdataset(msg, item); } void @@ -2832,7 +2991,7 @@ dns_message_setopt(dns_message_t *msg, dns_rdataset_t *opt) { cleanup: dns_rdataset_disassociate(opt); - dns_message_puttemprdataset(msg, &opt); + dns__message_puttemprdataset(msg, &opt); return (result); } diff --git a/lib/dns/name.c b/lib/dns/name.c index 9713cf5f4e..a0a53b26d6 100644 --- a/lib/dns/name.c +++ b/lib/dns/name.c @@ -215,6 +215,7 @@ dns_name_invalidate(dns_name_t *name) { name->offsets = NULL; name->buffer = NULL; ISC_LINK_INIT(name, link); + INSIST(name->ht == NULL); } bool diff --git a/lib/isc/ht.c b/lib/isc/ht.c index 3234ea8029..19f0b2c0b9 100644 --- a/lib/isc/ht.c +++ b/lib/isc/ht.c @@ -1,6 +1,8 @@ /* * Copyright (C) Internet Systems Consortium, Inc. ("ISC") * + * SPDX-License-Identifier: MPL-2.0 + * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, you can obtain one at https://mozilla.org/MPL/2.0/. @@ -10,208 +12,483 @@ */ #include - #include #include #include #include -#include #include #include #include +#include #include - typedef struct isc_ht_node isc_ht_node_t; -#define ISC_HT_MAGIC ISC_MAGIC('H', 'T', 'a', 'b') -#define ISC_HT_VALID(ht) ISC_MAGIC_VALID(ht, ISC_HT_MAGIC) +#define ISC_HT_MAGIC ISC_MAGIC('H', 'T', 'a', 'b') +#define ISC_HT_VALID(ht) ISC_MAGIC_VALID(ht, ISC_HT_MAGIC) + +#define HT_NO_BITS 0 +#define HT_MIN_BITS 1 +#define HT_MAX_BITS 32 +#define HT_OVERCOMMIT 3 + +#define HT_NEXTTABLE(idx) ((idx == 0) ? 1 : 0) +#define TRY_NEXTTABLE(idx, ht) (idx == ht->hindex && rehashing_in_progress(ht)) + +#define GOLDEN_RATIO_32 0x61C88647 + +#define HASHSIZE(bits) (UINT64_C(1) << (bits)) struct isc_ht_node { void *value; isc_ht_node_t *next; + uint32_t hashval; size_t keysize; - unsigned char key[FLEXIBLE_ARRAY_MEMBER]; + unsigned char key[]; }; struct isc_ht { unsigned int magic; isc_mem_t *mctx; - size_t size; - size_t mask; - unsigned int count; - isc_ht_node_t **table; + size_t count; + bool case_sensitive; + size_t size[2]; + uint8_t hashbits[2]; + isc_ht_node_t **table[2]; + uint8_t hindex; + uint32_t hiter; /* rehashing iterator */ }; struct isc_ht_iter { isc_ht_t *ht; size_t i; + uint8_t hindex; isc_ht_node_t *cur; }; -isc_result_t -isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, uint8_t bits) { - isc_ht_t *ht = NULL; - size_t i; +static isc_ht_node_t * +isc__ht_find(const isc_ht_t *ht, const unsigned char *key, + const uint32_t keysize, const uint32_t hashval, const uint8_t idx); +static void +isc__ht_add(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize, + const uint32_t hashval, const uint8_t idx, void *value); +static isc_result_t +isc__ht_delete(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize, + const uint32_t hashval, const uint8_t idx); + +static uint32_t +rehash_bits(isc_ht_t *ht, size_t newcount); + +static void +hashtable_new(isc_ht_t *ht, const uint8_t idx, const uint8_t bits); +static void +hashtable_free(isc_ht_t *ht, const uint8_t idx); +static void +hashtable_rehash(isc_ht_t *ht, uint32_t newbits); +static void +hashtable_rehash_one(isc_ht_t *ht); +static void +maybe_rehash(isc_ht_t *ht, size_t newcount); + +static isc_result_t +isc__ht_iter_next(isc_ht_iter_t *it); + +static uint8_t maptolower[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, + 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, + 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, + 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, + 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, + 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, + 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, + 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, + 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, + 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, + 0xfc, 0xfd, 0xfe, 0xff +}; - REQUIRE(htp != NULL && *htp == NULL); - REQUIRE(mctx != NULL); - REQUIRE(bits >= 1 && bits <= (sizeof(size_t)*8 - 1)); +static int +memcasecmp(const void *vs1, const void *vs2, size_t len) { + uint8_t const *s1 = vs1; + uint8_t const *s2 = vs2; + for (size_t i = 0; i < len; i++) { + uint8_t u1 = s1[i]; + uint8_t u2 = s2[i]; + int U1 = maptolower[u1]; + int U2 = maptolower[u2]; + int diff = U1 - U2; + if (diff) { + return diff; + } + } + return 0; +} - ht = isc_mem_get(mctx, sizeof(struct isc_ht)); - if (ht == NULL) { - return (ISC_R_NOMEMORY); +static bool +isc__ht_node_match(isc_ht_node_t *node, const uint32_t hashval, + const uint8_t *key, uint32_t keysize, bool case_sensitive) { + return (node->hashval == hashval && node->keysize == keysize && + (case_sensitive ? (memcmp(node->key, key, keysize) == 0) + : (memcasecmp(node->key, key, keysize) == 0))); +} + +static uint32_t +hash_32(uint32_t val, unsigned int bits) { + REQUIRE(bits <= HT_MAX_BITS); + /* High bits are more random. */ + return (val * GOLDEN_RATIO_32 >> (32 - bits)); +} + +static bool +rehashing_in_progress(const isc_ht_t *ht) { + return (ht->table[HT_NEXTTABLE(ht->hindex)] != NULL); +} + +static bool +hashtable_is_overcommited(isc_ht_t *ht) { + return (ht->count >= (ht->size[ht->hindex] * HT_OVERCOMMIT)); +} + +static uint32_t +rehash_bits(isc_ht_t *ht, size_t newcount) { + uint32_t newbits = ht->hashbits[ht->hindex]; + + while (newcount >= HASHSIZE(newbits) && newbits <= HT_MAX_BITS) { + newbits += 1; } - ht->mctx = NULL; - isc_mem_attach(mctx, &ht->mctx); + return (newbits); +} + +/* + * Rebuild the hashtable to reduce the load factor + */ +static void +hashtable_rehash(isc_ht_t *ht, uint32_t newbits) { + uint8_t oldindex = ht->hindex; + uint32_t oldbits = ht->hashbits[oldindex]; + uint8_t newindex = HT_NEXTTABLE(oldindex); - ht->size = ((size_t)1<mask = ((size_t)1<count = 0; + REQUIRE(ht->hashbits[oldindex] >= HT_MIN_BITS); + REQUIRE(ht->hashbits[oldindex] <= HT_MAX_BITS); + REQUIRE(ht->table[oldindex] != NULL); + + REQUIRE(newbits <= HT_MAX_BITS); + REQUIRE(ht->hashbits[newindex] == HT_NO_BITS); + REQUIRE(ht->table[newindex] == NULL); + + REQUIRE(newbits > oldbits); + + hashtable_new(ht, newindex, newbits); + + ht->hindex = newindex; + + hashtable_rehash_one(ht); +} + +static void +hashtable_rehash_one(isc_ht_t *ht) { + isc_ht_node_t **newtable = ht->table[ht->hindex]; + uint32_t oldsize = ht->size[HT_NEXTTABLE(ht->hindex)]; + isc_ht_node_t **oldtable = ht->table[HT_NEXTTABLE(ht->hindex)]; + isc_ht_node_t *node = NULL; + isc_ht_node_t *nextnode; + + /* Find first non-empty node */ + while (ht->hiter < oldsize && oldtable[ht->hiter] == NULL) { + ht->hiter++; + } - ht->table = isc_mem_get(ht->mctx, ht->size * sizeof(isc_ht_node_t*)); - if (ht->table == NULL) { - isc_mem_putanddetach(&ht->mctx, ht, sizeof(struct isc_ht)); - return (ISC_R_NOMEMORY); + /* Rehashing complete */ + if (ht->hiter == oldsize) { + hashtable_free(ht, HT_NEXTTABLE(ht->hindex)); + ht->hiter = 0; + return; } - for (i = 0; i < ht->size; i++) { - ht->table[i] = NULL; + /* Move the first non-empty node from old hashtable to new hashtable */ + for (node = oldtable[ht->hiter]; node != NULL; node = nextnode) { + uint32_t hash = hash_32(node->hashval, + ht->hashbits[ht->hindex]); + nextnode = node->next; + node->next = newtable[hash]; + newtable[hash] = node; } + oldtable[ht->hiter] = NULL; + + ht->hiter++; +} + +static void +maybe_rehash(isc_ht_t *ht, size_t newcount) { + uint32_t newbits = rehash_bits(ht, newcount); + + if (ht->hashbits[ht->hindex] < newbits && newbits <= HT_MAX_BITS) { + hashtable_rehash(ht, newbits); + } +} + +static void +hashtable_new(isc_ht_t *ht, const uint8_t idx, const uint8_t bits) { + size_t size; + REQUIRE(ht->hashbits[idx] == HT_NO_BITS); + REQUIRE(ht->table[idx] == NULL); + REQUIRE(bits >= HT_MIN_BITS); + REQUIRE(bits <= HT_MAX_BITS); + + ht->hashbits[idx] = bits; + ht->size[idx] = HASHSIZE(ht->hashbits[idx]); + + size = ht->size[idx] * sizeof(isc_ht_node_t *); + + ht->table[idx] = isc_mem_get(ht->mctx, size); + INSIST(ht->table[idx] != NULL); + memset(ht->table[idx], 0, size); +} + +static void +hashtable_free(isc_ht_t *ht, const uint8_t idx) { + size_t size = ht->size[idx] * sizeof(isc_ht_node_t *); + + for (size_t i = 0; i < ht->size[idx]; i++) { + isc_ht_node_t *node = ht->table[idx][i]; + while (node != NULL) { + isc_ht_node_t *next = node->next; + ht->count--; + isc_mem_put(ht->mctx, node, + sizeof(*node) + node->keysize); + node = next; + } + } + + isc_mem_put(ht->mctx, ht->table[idx], size); + ht->hashbits[idx] = HT_NO_BITS; + ht->table[idx] = NULL; +} + +void +isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, uint8_t bits, + unsigned int options) { + isc_ht_t *ht = NULL; + bool case_sensitive = ((options & ISC_HT_CASE_INSENSITIVE) == 0); + + REQUIRE(htp != NULL && *htp == NULL); + REQUIRE(mctx != NULL); + REQUIRE(bits >= 1 && bits <= HT_MAX_BITS); + + ht = isc_mem_get(mctx, sizeof(*ht)); + INSIST(ht != NULL); + *ht = (isc_ht_t){ + .case_sensitive = case_sensitive, + }; + + isc_mem_attach(mctx, &ht->mctx); + + hashtable_new(ht, 0, bits); + ht->magic = ISC_HT_MAGIC; *htp = ht; - return (ISC_R_SUCCESS); } void isc_ht_destroy(isc_ht_t **htp) { isc_ht_t *ht; - size_t i; REQUIRE(htp != NULL); - REQUIRE(ISC_HT_VALID((*htp))); + REQUIRE(ISC_HT_VALID(*htp)); ht = *htp; + *htp = NULL; ht->magic = 0; - for (i = 0; i < ht->size; i++) { - isc_ht_node_t *node = ht->table[i]; - while (node != NULL) { - isc_ht_node_t *next = node->next; - ht->count--; - isc_mem_put(ht->mctx, node, - offsetof(isc_ht_node_t, key) + - node->keysize); - node = next; + for (size_t i = 0; i <= 1; i++) { + if (ht->table[i] != NULL) { + hashtable_free(ht, i); } } INSIST(ht->count == 0); - isc_mem_put(ht->mctx, ht->table, ht->size * sizeof(isc_ht_node_t*)); - isc_mem_putanddetach(&ht->mctx, ht, sizeof(struct isc_ht)); - - *htp = NULL; + isc_mem_putanddetach(&ht->mctx, ht, sizeof(*ht)); } -isc_result_t -isc_ht_add(isc_ht_t *ht, const unsigned char *key, - uint32_t keysize, void *value) -{ +static void +isc__ht_add(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize, + const uint32_t hashval, const uint8_t idx, void *value) { isc_ht_node_t *node; uint32_t hash; + hash = hash_32(hashval, ht->hashbits[idx]); + + node = isc_mem_get(ht->mctx, sizeof(*node) + keysize); + INSIST(node != NULL); + *node = (isc_ht_node_t){ + .keysize = keysize, + .hashval = hashval, + .next = ht->table[idx][hash], + .value = value, + }; + + memmove(node->key, key, keysize); + + ht->count++; + ht->table[idx][hash] = node; +} + +isc_result_t +isc_ht_add(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize, + void *value) { + uint32_t hashval; + REQUIRE(ISC_HT_VALID(ht)); REQUIRE(key != NULL && keysize > 0); - hash = isc_hash_function(key, keysize, true, NULL); - node = ht->table[hash & ht->mask]; - while (node != NULL) { - if (keysize == node->keysize && - memcmp(key, node->key, keysize) == 0) { - return (ISC_R_EXISTS); - } - node = node->next; + if (rehashing_in_progress(ht)) { + /* Rehash in progress */ + hashtable_rehash_one(ht); + } else if (hashtable_is_overcommited(ht)) { + /* Rehash requested */ + maybe_rehash(ht, ht->count); } - node = isc_mem_get(ht->mctx, offsetof(isc_ht_node_t, key) + keysize); - if (node == NULL) - return (ISC_R_NOMEMORY); + hashval = isc_hash_function(key, keysize, ht->case_sensitive, NULL); - memmove(node->key, key, keysize); - node->keysize = keysize; - node->next = ht->table[hash & ht->mask]; - node->value = value; + if (isc__ht_find(ht, key, keysize, hashval, ht->hindex) != NULL) { + return (ISC_R_EXISTS); + } + + isc__ht_add(ht, key, keysize, hashval, ht->hindex, value); - ht->count++; - ht->table[hash & ht->mask] = node; return (ISC_R_SUCCESS); } +static isc_ht_node_t * +isc__ht_find(const isc_ht_t *ht, const unsigned char *key, + const uint32_t keysize, const uint32_t hashval, + const uint8_t idx) { + uint32_t hash; + uint8_t findex = idx; + +nexttable: + hash = hash_32(hashval, ht->hashbits[findex]); + for (isc_ht_node_t *node = ht->table[findex][hash]; node != NULL; + node = node->next) + { + if (isc__ht_node_match(node, hashval, key, keysize, + ht->case_sensitive)) + { + return (node); + } + } + if (TRY_NEXTTABLE(findex, ht)) { + /* + * Rehashing in progress, check the other table + */ + findex = HT_NEXTTABLE(findex); + goto nexttable; + } + + return (NULL); +} + isc_result_t isc_ht_find(const isc_ht_t *ht, const unsigned char *key, - uint32_t keysize, void **valuep) -{ + const uint32_t keysize, void **valuep) { + uint32_t hashval; isc_ht_node_t *node; - uint32_t hash; REQUIRE(ISC_HT_VALID(ht)); REQUIRE(key != NULL && keysize > 0); REQUIRE(valuep == NULL || *valuep == NULL); - hash = isc_hash_function(key, keysize, true, NULL); - node = ht->table[hash & ht->mask]; - while (node != NULL) { - if (keysize == node->keysize && - memcmp(key, node->key, keysize) == 0) { - if (valuep != NULL) { - *valuep = node->value; - } - return (ISC_R_SUCCESS); - } - node = node->next; + hashval = isc_hash_function(key, keysize, ht->case_sensitive, NULL); + + node = isc__ht_find(ht, key, keysize, hashval, ht->hindex); + if (node == NULL) { + return (ISC_R_NOTFOUND); } - return (ISC_R_NOTFOUND); + if (valuep != NULL) { + *valuep = node->value; + } + return (ISC_R_SUCCESS); } -isc_result_t -isc_ht_delete(isc_ht_t *ht, const unsigned char *key, uint32_t keysize) { - isc_ht_node_t *node, *prev; +static isc_result_t +isc__ht_delete(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize, + const uint32_t hashval, const uint8_t idx) { + isc_ht_node_t *prev = NULL; uint32_t hash; - REQUIRE(ISC_HT_VALID(ht)); - REQUIRE(key != NULL && keysize > 0); - - prev = NULL; - hash = isc_hash_function(key, keysize, true, NULL); - node = ht->table[hash & ht->mask]; - while (node != NULL) { - if (keysize == node->keysize && - memcmp(key, node->key, keysize) == 0) { - if (prev == NULL) - ht->table[hash & ht->mask] = node->next; - else + hash = hash_32(hashval, ht->hashbits[idx]); + + for (isc_ht_node_t *node = ht->table[idx][hash]; node != NULL; + prev = node, node = node->next) + { + if (isc__ht_node_match(node, hashval, key, keysize, + ht->case_sensitive)) + { + if (prev == NULL) { + ht->table[idx][hash] = node->next; + } else { prev->next = node->next; + } isc_mem_put(ht->mctx, node, - offsetof(isc_ht_node_t, key) + - node->keysize); + sizeof(*node) + node->keysize); ht->count--; return (ISC_R_SUCCESS); } - - prev = node; - node = node->next; } + return (ISC_R_NOTFOUND); } isc_result_t +isc_ht_delete(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize) { + uint32_t hashval; + uint8_t hindex; + isc_result_t result; + + REQUIRE(ISC_HT_VALID(ht)); + REQUIRE(key != NULL && keysize > 0); + + if (rehashing_in_progress(ht)) { + /* Rehash in progress */ + hashtable_rehash_one(ht); + } + + hindex = ht->hindex; + hashval = isc_hash_function(key, keysize, ht->case_sensitive, NULL); +nexttable: + result = isc__ht_delete(ht, key, keysize, hashval, hindex); + + if (result == ISC_R_NOTFOUND && TRY_NEXTTABLE(hindex, ht)) { + /* + * Rehashing in progress, check the other table + */ + hindex = HT_NEXTTABLE(hindex); + goto nexttable; + } + + return (result); +} + +void isc_ht_iter_create(isc_ht_t *ht, isc_ht_iter_t **itp) { isc_ht_iter_t *it; @@ -219,16 +496,13 @@ isc_ht_iter_create(isc_ht_t *ht, isc_ht_iter_t **itp) { REQUIRE(itp != NULL && *itp == NULL); it = isc_mem_get(ht->mctx, sizeof(isc_ht_iter_t)); - if (it == NULL) - return (ISC_R_NOMEMORY); - - it->ht = ht; - it->i = 0; - it->cur = NULL; + INSIST(it != NULL); + *it = (isc_ht_iter_t){ + .ht = ht, + .hindex = ht->hindex, + }; *itp = it; - - return (ISC_R_SUCCESS); } void @@ -239,26 +513,48 @@ isc_ht_iter_destroy(isc_ht_iter_t **itp) { REQUIRE(itp != NULL && *itp != NULL); it = *itp; - ht = it->ht; - isc_mem_put(ht->mctx, it, sizeof(isc_ht_iter_t)); - *itp = NULL; + ht = it->ht; + isc_mem_put(ht->mctx, it, sizeof(*it)); } isc_result_t isc_ht_iter_first(isc_ht_iter_t *it) { + isc_ht_t *ht; + REQUIRE(it != NULL); + ht = it->ht; + + it->hindex = ht->hindex; it->i = 0; - while (it->i < it->ht->size && it->ht->table[it->i] == NULL) + + return (isc__ht_iter_next(it)); +} + +static isc_result_t +isc__ht_iter_next(isc_ht_iter_t *it) { + isc_ht_t *ht = it->ht; + + while (it->i < ht->size[it->hindex] && + ht->table[it->hindex][it->i] == NULL) + { it->i++; + } - if (it->i == it->ht->size) - return (ISC_R_NOMORE); + if (it->i < ht->size[it->hindex]) { + it->cur = ht->table[it->hindex][it->i]; - it->cur = it->ht->table[it->i]; + return (ISC_R_SUCCESS); + } - return (ISC_R_SUCCESS); + if (TRY_NEXTTABLE(it->hindex, ht)) { + it->hindex = HT_NEXTTABLE(it->hindex); + it->i = 0; + return (isc__ht_iter_next(it)); + } + + return (ISC_R_NOMORE); } isc_result_t @@ -267,58 +563,36 @@ isc_ht_iter_next(isc_ht_iter_t *it) { REQUIRE(it->cur != NULL); it->cur = it->cur->next; - if (it->cur == NULL) { - do { - it->i++; - } while (it->i < it->ht->size && it->ht->table[it->i] == NULL); - if (it->i >= it->ht->size) - return (ISC_R_NOMORE); - it->cur = it->ht->table[it->i]; + + if (it->cur != NULL) { + return (ISC_R_SUCCESS); } - return (ISC_R_SUCCESS); + it->i++; + + return (isc__ht_iter_next(it)); } isc_result_t isc_ht_iter_delcurrent_next(isc_ht_iter_t *it) { isc_result_t result = ISC_R_SUCCESS; - isc_ht_node_t *to_delete = NULL; - isc_ht_node_t *prev = NULL; - isc_ht_node_t *node = NULL; - uint32_t hash; + isc_ht_node_t *dnode = NULL; + uint8_t dindex; isc_ht_t *ht; + isc_result_t dresult; + REQUIRE(it != NULL); REQUIRE(it->cur != NULL); - to_delete = it->cur; - ht = it->ht; - it->cur = it->cur->next; - if (it->cur == NULL) { - do { - it->i++; - } while (it->i < ht->size && ht->table[it->i] == NULL); - if (it->i >= ht->size) - result = ISC_R_NOMORE; - else - it->cur = ht->table[it->i]; - } + ht = it->ht; + dnode = it->cur; + dindex = it->hindex; - hash = isc_hash_function(to_delete->key, to_delete->keysize, true, - NULL); - node = ht->table[hash & ht->mask]; - while (node != to_delete) { - prev = node; - node = node->next; - INSIST(node != NULL); - } + result = isc_ht_iter_next(it); - if (prev == NULL) - ht->table[hash & ht->mask] = node->next; - else - prev->next = node->next; - isc_mem_put(ht->mctx, node, - offsetof(isc_ht_node_t, key) + node->keysize); - ht->count--; + dresult = isc__ht_delete(ht, dnode->key, dnode->keysize, dnode->hashval, + dindex); + INSIST(dresult == ISC_R_SUCCESS); return (result); } @@ -333,8 +607,8 @@ isc_ht_iter_current(isc_ht_iter_t *it, void **valuep) { } void -isc_ht_iter_currentkey(isc_ht_iter_t *it, unsigned char **key, size_t *keysize) -{ +isc_ht_iter_currentkey(isc_ht_iter_t *it, unsigned char **key, + size_t *keysize) { REQUIRE(it != NULL); REQUIRE(it->cur != NULL); REQUIRE(key != NULL && *key == NULL); @@ -343,9 +617,9 @@ isc_ht_iter_currentkey(isc_ht_iter_t *it, unsigned char **key, size_t *keysize) *keysize = it->cur->keysize; } -unsigned int -isc_ht_count(isc_ht_t *ht) { +size_t +isc_ht_count(const isc_ht_t *ht) { REQUIRE(ISC_HT_VALID(ht)); - return(ht->count); + return (ht->count); } diff --git a/lib/isc/include/isc/ht.h b/lib/isc/include/isc/ht.h index a1315b0c5a..163fbefb79 100644 --- a/lib/isc/include/isc/ht.h +++ b/lib/isc/include/isc/ht.h @@ -1,6 +1,8 @@ /* * Copyright (C) Internet Systems Consortium, Inc. ("ISC") * + * SPDX-License-Identifier: MPL-2.0 + * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, you can obtain one at https://mozilla.org/MPL/2.0/. @@ -11,32 +13,35 @@ /* ! \file */ -#ifndef ISC_HT_H -#define ISC_HT_H 1 +#pragma once #include #include -#include #include +#include -typedef struct isc_ht isc_ht_t; +typedef struct isc_ht isc_ht_t; typedef struct isc_ht_iter isc_ht_iter_t; +enum { ISC_HT_CASE_SENSITIVE = 0x00, ISC_HT_CASE_INSENSITIVE = 0x01 }; + /*% * Initialize hashtable at *htp, using memory context and size of (1<=1 and 'bits' <=32 * - * Returns: - *\li #ISC_R_NOMEMORY -- not enough memory to create pool - *\li #ISC_R_SUCCESS -- all is well. */ -isc_result_t -isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, uint8_t bits); +void +isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, uint8_t bits, + unsigned int options); /*% * Destroy hashtable, freeing everything @@ -53,6 +58,7 @@ isc_ht_destroy(isc_ht_t **htp); * * Requires: *\li 'ht' is a valid hashtable + *\li write-lock * * Returns: *\li #ISC_R_NOMEMORY -- not enough memory to create pool @@ -60,15 +66,18 @@ isc_ht_destroy(isc_ht_t **htp); *\li #ISC_R_SUCCESS -- all is well. */ isc_result_t -isc_ht_add(isc_ht_t *ht, const unsigned char *key, uint32_t keysize, - void *value); +isc_ht_add(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize, + void *value); /*% * Find a node matching 'key'/'keysize' in hashtable 'ht'; - * if found, set 'value' to its value + * if found, set '*valuep' to its value. (If 'valuep' is NULL, + * then simply return SUCCESS or NOTFOUND to indicate whether the + * key exists in the hashtable.) * * Requires: * \li 'ht' is a valid hashtable + * \li read-lock * * Returns: * \li #ISC_R_SUCCESS -- success @@ -76,20 +85,21 @@ isc_ht_add(isc_ht_t *ht, const unsigned char *key, uint32_t keysize, */ isc_result_t isc_ht_find(const isc_ht_t *ht, const unsigned char *key, - uint32_t keysize, void **valuep); + const uint32_t keysize, void **valuep); /*% * Delete node from hashtable * * Requires: *\li ht is a valid hashtable + *\li write-lock * * Returns: *\li #ISC_R_NOTFOUND -- key not found *\li #ISC_R_SUCCESS -- all is well */ isc_result_t -isc_ht_delete(isc_ht_t *ht, const unsigned char *key, uint32_t keysize); +isc_ht_delete(isc_ht_t *ht, const unsigned char *key, const uint32_t keysize); /*% * Create an iterator for the hashtable; point '*itp' to it. @@ -98,7 +108,7 @@ isc_ht_delete(isc_ht_t *ht, const unsigned char *key, uint32_t keysize); *\li 'ht' is a valid hashtable *\li 'itp' is non NULL and '*itp' is NULL. */ -isc_result_t +void isc_ht_iter_create(isc_ht_t *ht, isc_ht_iter_t **itp); /*% @@ -117,7 +127,7 @@ isc_ht_iter_destroy(isc_ht_iter_t **itp); *\li 'it' is non NULL. * * Returns: - * \li #ISC_R_SUCCESS -- success + * \li #ISC_R_SUCCESS -- success * \li #ISC_R_NOMORE -- no data in the hashtable */ isc_result_t @@ -130,7 +140,7 @@ isc_ht_iter_first(isc_ht_iter_t *it); *\li 'it' is non NULL. * * Returns: - * \li #ISC_R_SUCCESS -- success + * \li #ISC_R_SUCCESS -- success * \li #ISC_R_NOMORE -- end of hashtable reached */ isc_result_t @@ -143,13 +153,12 @@ isc_ht_iter_next(isc_ht_iter_t *it); *\li 'it' is non NULL. * * Returns: - * \li #ISC_R_SUCCESS -- success + * \li #ISC_R_SUCCESS -- success * \li #ISC_R_NOMORE -- end of hashtable reached */ isc_result_t isc_ht_iter_delcurrent_next(isc_ht_iter_t *it); - /*% * Set 'value' to the current value under the iterator * @@ -178,6 +187,5 @@ isc_ht_iter_currentkey(isc_ht_iter_t *it, unsigned char **key, size_t *keysize); * Requires: *\li 'ht' is a valid hashtable */ -unsigned int -isc_ht_count(isc_ht_t *ht); -#endif +size_t +isc_ht_count(const isc_ht_t *ht); diff --git a/lib/isc/tests/ht_test.c b/lib/isc/tests/ht_test.c index 79e4260c2b..144d859639 100644 --- a/lib/isc/tests/ht_test.c +++ b/lib/isc/tests/ht_test.c @@ -60,8 +60,7 @@ test_ht_full(int bits, uintptr_t count) { NULL, &mctx, 0); assert_int_equal(result, ISC_R_SUCCESS); - result = isc_ht_init(&ht, mctx, bits); - assert_int_equal(result, ISC_R_SUCCESS); + isc_ht_init(&ht, mctx, bits, ISC_HT_CASE_SENSITIVE); assert_non_null(ht); for (i = 1; i < count; i++) { @@ -214,8 +213,7 @@ test_ht_iterator() { NULL, &mctx, 0); assert_int_equal(result, ISC_R_SUCCESS); - result = isc_ht_init(&ht, mctx, 16); - assert_int_equal(result, ISC_R_SUCCESS); + isc_ht_init(&ht, mctx, 16, ISC_HT_CASE_SENSITIVE); assert_non_null(ht); for (i = 1; i <= count; i++) { /* @@ -229,8 +227,7 @@ test_ht_iterator() { } walked = 0; - result = isc_ht_iter_create(ht, &iter); - assert_int_equal(result, ISC_R_SUCCESS); + isc_ht_iter_create(ht, &iter); for (result = isc_ht_iter_first(iter); result == ISC_R_SUCCESS; @@ -348,9 +345,69 @@ isc_ht_iterator_test(void **state) { test_ht_iterator(); } +static void +isc_ht_case(void **state) { + UNUSED(state); + + isc_ht_t *ht = NULL; + void *f = NULL; + isc_result_t result = ISC_R_UNSET; + isc_mem_t *mctx = NULL; + + result = isc_mem_createx2(0, 0, default_memalloc, default_memfree, + NULL, &mctx, 0); + assert_int_equal(result, ISC_R_SUCCESS); + + unsigned char lower[16] = { "test case" }; + unsigned char same[16] = { "test case" }; + unsigned char upper[16] = { "TEST CASE" }; + unsigned char mixed[16] = { "tEsT CaSe" }; + + isc_ht_init(&ht, mctx, 8, ISC_HT_CASE_SENSITIVE); + assert_non_null(ht); + + result = isc_ht_add(ht, lower, 16, (void *)lower); + assert_int_equal(result, ISC_R_SUCCESS); + + result = isc_ht_add(ht, same, 16, (void *)same); + assert_int_equal(result, ISC_R_EXISTS); + + result = isc_ht_add(ht, upper, 16, (void *)upper); + assert_int_equal(result, ISC_R_SUCCESS); + + result = isc_ht_find(ht, mixed, 16, &f); + assert_int_equal(result, ISC_R_NOTFOUND); + assert_null(f); + + isc_ht_destroy(&ht); + assert_null(ht); + + isc_ht_init(&ht, mctx, 8, ISC_HT_CASE_INSENSITIVE); + assert_non_null(ht); + + result = isc_ht_add(ht, lower, 16, (void *)lower); + assert_int_equal(result, ISC_R_SUCCESS); + + result = isc_ht_add(ht, same, 16, (void *)same); + assert_int_equal(result, ISC_R_EXISTS); + + result = isc_ht_add(ht, upper, 16, (void *)upper); + assert_int_equal(result, ISC_R_EXISTS); + + result = isc_ht_find(ht, mixed, 16, &f); + assert_int_equal(result, ISC_R_SUCCESS); + assert_ptr_equal(f, &lower); + + isc_ht_destroy(&ht); + assert_null(ht); + + isc_mem_detach(&mctx); +} + int main(void) { const struct CMUnitTest tests[] = { + cmocka_unit_test(isc_ht_case), cmocka_unit_test(isc_ht_20), cmocka_unit_test(isc_ht_8), cmocka_unit_test(isc_ht_1), -- 2.43.2