Merge branch '3405-security-limit-the-number-of-resource-records-in-rrset-nxdomain' into 'v9.20.0-release'

[CVE-2024-1737 (part 2)] Be smarter about refusing to add many RR types to the database

See merge request isc-private/bind9!712
This commit is contained in:
Nicki Křížek 2024-07-02 11:05:37 +00:00
commit 92a73bca2d
13 changed files with 2947 additions and 2671 deletions

View file

@ -361,7 +361,6 @@ n=$((n + 1))
status=$((status + ret))
# Update zone with nsupdate.
n=$((n + 1))
echo_i "add new type to zone and check that it fails ($n)"
ret=0
(

View file

@ -19,3 +19,5 @@ rm -f ns?/named.memstats
rm -f ns?/named.run
rm -f ns*/named.conf
rm -f ns*/managed-keys.bind*
rm -f ns*/signed.db*
rm -f ns*/*.db.signed

File diff suppressed because it is too large Load diff

View file

@ -32,3 +32,8 @@ zone "big." {
type primary;
file "big.db";
};
zone "signed." {
type primary;
file "signed.db.signed";
};

View file

@ -20,5 +20,8 @@ direct.example.net. 60 IN A 10.53.0.2
example.com. 60 IN NS direct.example.com.
direct.example.com. 60 IN A 10.53.0.4
big. in NS ns.big.
big. IN NS ns.big.
ns.big. 60 IN A 10.53.0.1
signed. IN NS ns.signed.
ns.signed. 60 IN A 10.53.0.1

View file

@ -15,3 +15,8 @@
copy_setports ns1/named.conf.in ns1/named.conf
copy_setports ns3/named1.conf.in ns3/named.conf
sed -e s/big[.]/signed./g <ns1/big.db >ns1/signed.db
$KEYGEN -K ns1 -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" -f KSK signed >/dev/null 2>&1
$KEYGEN -K ns1 -q -a "$DEFAULT_ALGORITHM" -b "$DEFAULT_BITS" signed >/dev/null 2>&1
$SIGNER -K ns1 -S -f ns1/signed.db.signed -o signed ns1/signed.db >/dev/null

View file

@ -15,7 +15,9 @@ set -e
. ../conf.sh
DIGOPTS="-p ${PORT}"
dig_with_opts() {
$DIG -p "${PORT}" +retries=0 "$@"
}
status=0
n=0
@ -26,6 +28,10 @@ ns3_reset() {
$RNDC -c ../_common/rndc.conf -s 10.53.0.3 -p ${CONTROLPORT} flush | sed 's/^/I:ns3 /'
}
ns3_flush() {
$RNDC -c ../_common/rndc.conf -s 10.53.0.3 -p ${CONTROLPORT} flush | sed 's/^/I:ns3 /'
}
ns3_sends_aaaa_queries() {
if grep "started AAAA fetch" ns3/named.run >/dev/null; then
return 0
@ -66,12 +72,12 @@ echo_i "attempt excessive-depth lookup ($n)"
ret=0
echo "1000" >ans2/ans.limit
echo "1000" >ans4/ans.limit
$DIG $DIGOPTS @10.53.0.2 reset >/dev/null || ret=1
$DIG $DIGOPTS @10.53.0.4 reset >/dev/null || ret=1
$DIG $DIGOPTS @10.53.0.3 indirect1.example.org >dig.out.1.test$n || ret=1
dig_with_opts @10.53.0.2 reset >/dev/null || ret=1
dig_with_opts @10.53.0.4 reset >/dev/null || ret=1
dig_with_opts @10.53.0.3 indirect1.example.org >dig.out.1.test$n || ret=1
grep "status: SERVFAIL" dig.out.1.test$n >/dev/null || ret=1
$DIG $DIGOPTS +short @10.53.0.2 count txt >dig.out.2.test$n || ret=1
$DIG $DIGOPTS +short @10.53.0.4 count txt >dig.out.4.test$n || ret=1
dig_with_opts +short @10.53.0.2 count txt >dig.out.2.test$n || ret=1
dig_with_opts +short @10.53.0.4 count txt >dig.out.4.test$n || ret=1
check_query_count dig.out.2.test$n dig.out.4.test$n 27 14
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
@ -82,12 +88,12 @@ ret=0
echo "12" >ans2/ans.limit
echo "12" >ans4/ans.limit
ns3_reset ns3/named1.conf.in
$DIG $DIGOPTS @10.53.0.2 reset >/dev/null || ret=1
$DIG $DIGOPTS @10.53.0.4 reset >/dev/null || ret=1
$DIG $DIGOPTS @10.53.0.3 indirect2.example.org >dig.out.1.test$n || ret=1
dig_with_opts @10.53.0.2 reset >/dev/null || ret=1
dig_with_opts @10.53.0.4 reset >/dev/null || ret=1
dig_with_opts @10.53.0.3 indirect2.example.org >dig.out.1.test$n || ret=1
grep "status: NOERROR" dig.out.1.test$n >/dev/null || ret=1
$DIG $DIGOPTS +short @10.53.0.2 count txt >dig.out.2.test$n || ret=1
$DIG $DIGOPTS +short @10.53.0.4 count txt >dig.out.4.test$n || ret=1
dig_with_opts +short @10.53.0.2 count txt >dig.out.2.test$n || ret=1
dig_with_opts +short @10.53.0.4 count txt >dig.out.4.test$n || ret=1
check_query_count dig.out.2.test$n dig.out.4.test$n 50 26
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
@ -99,12 +105,12 @@ echo_i "attempt excessive-depth lookup ($n)"
ret=0
echo "12" >ans2/ans.limit
ns3_reset ns3/named2.conf.in
$DIG $DIGOPTS @10.53.0.2 reset >/dev/null || ret=1
$DIG $DIGOPTS @10.53.0.4 reset >/dev/null || ret=1
$DIG $DIGOPTS @10.53.0.3 indirect3.example.org >dig.out.1.test$n || ret=1
dig_with_opts @10.53.0.2 reset >/dev/null || ret=1
dig_with_opts @10.53.0.4 reset >/dev/null || ret=1
dig_with_opts @10.53.0.3 indirect3.example.org >dig.out.1.test$n || ret=1
grep "status: SERVFAIL" dig.out.1.test$n >/dev/null || ret=1
$DIG $DIGOPTS +short @10.53.0.2 count txt >dig.out.2.test$n || ret=1
$DIG $DIGOPTS +short @10.53.0.4 count txt >dig.out.4.test$n || ret=1
dig_with_opts +short @10.53.0.2 count txt >dig.out.2.test$n || ret=1
dig_with_opts +short @10.53.0.4 count txt >dig.out.4.test$n || ret=1
check_query_count dig.out.2.test$n dig.out.4.test$n 13 7
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
@ -115,12 +121,12 @@ ret=0
echo "5" >ans2/ans.limit
echo "5" >ans4/ans.limit
ns3_reset ns3/named2.conf.in
$DIG $DIGOPTS @10.53.0.2 reset >/dev/null || ret=1
$DIG $DIGOPTS @10.53.0.4 reset >/dev/null || ret=1
$DIG $DIGOPTS @10.53.0.3 indirect4.example.org >dig.out.1.test$n || ret=1
dig_with_opts @10.53.0.2 reset >/dev/null || ret=1
dig_with_opts @10.53.0.4 reset >/dev/null || ret=1
dig_with_opts @10.53.0.3 indirect4.example.org >dig.out.1.test$n || ret=1
grep "status: NOERROR" dig.out.1.test$n >/dev/null || ret=1
$DIG $DIGOPTS +short @10.53.0.2 count txt >dig.out.2.test$n || ret=1
$DIG $DIGOPTS +short @10.53.0.4 count txt >dig.out.4.test$n || ret=1
dig_with_opts +short @10.53.0.2 count txt >dig.out.2.test$n || ret=1
dig_with_opts +short @10.53.0.4 count txt >dig.out.4.test$n || ret=1
check_query_count dig.out.2.test$n dig.out.4.test$n 22 12
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
@ -133,14 +139,14 @@ ret=0
echo "13" >ans2/ans.limit
echo "13" >ans4/ans.limit
ns3_reset ns3/named3.conf.in
$DIG $DIGOPTS @10.53.0.2 reset >/dev/null || ret=1
$DIG $DIGOPTS @10.53.0.4 reset >/dev/null || ret=1
$DIG $DIGOPTS @10.53.0.3 indirect5.example.org >dig.out.1.test$n || ret=1
dig_with_opts @10.53.0.2 reset >/dev/null || ret=1
dig_with_opts @10.53.0.4 reset >/dev/null || ret=1
dig_with_opts @10.53.0.3 indirect5.example.org >dig.out.1.test$n || ret=1
if ns3_sends_aaaa_queries; then
grep "status: SERVFAIL" dig.out.1.test$n >/dev/null || ret=1
fi
$DIG $DIGOPTS +short @10.53.0.2 count txt >dig.out.2.test$n || ret=1
$DIG $DIGOPTS +short @10.53.0.4 count txt >dig.out.4.test$n || ret=1
dig_with_opts +short @10.53.0.2 count txt >dig.out.2.test$n || ret=1
dig_with_opts +short @10.53.0.4 count txt >dig.out.4.test$n || ret=1
eval count=$(cat dig.out.2.test$n)
[ $count -le 50 ] || {
ret=1
@ -154,10 +160,10 @@ echo_i "attempt permissible lookup ($n)"
ret=0
echo "12" >ans2/ans.limit
ns3_reset ns3/named3.conf.in
$DIG $DIGOPTS @10.53.0.2 reset >/dev/null || ret=1
$DIG $DIGOPTS @10.53.0.3 indirect6.example.org >dig.out.1.test$n || ret=1
dig_with_opts @10.53.0.2 reset >/dev/null || ret=1
dig_with_opts @10.53.0.3 indirect6.example.org >dig.out.1.test$n || ret=1
grep "status: NOERROR" dig.out.1.test$n >/dev/null || ret=1
$DIG $DIGOPTS +short @10.53.0.2 count txt >dig.out.2.test$n || ret=1
dig_with_opts +short @10.53.0.2 count txt >dig.out.2.test$n || ret=1
eval count=$(cat dig.out.2.test$n)
[ $count -le 50 ] || {
ret=1
@ -173,12 +179,12 @@ echo_i "attempt excessive-queries lookup ($n)"
ret=0
echo "11" >ans2/ans.limit
ns3_reset ns3/named4.conf.in
$DIG $DIGOPTS @10.53.0.2 reset >/dev/null || ret=1
$DIG $DIGOPTS @10.53.0.3 indirect7.example.org >dig.out.1.test$n || ret=1
dig_with_opts @10.53.0.2 reset >/dev/null || ret=1
dig_with_opts @10.53.0.3 indirect7.example.org >dig.out.1.test$n || ret=1
if ns3_sends_aaaa_queries; then
grep "status: SERVFAIL" dig.out.1.test$n >/dev/null || ret=1
fi
$DIG $DIGOPTS +short @10.53.0.2 count txt >dig.out.2.test$n || ret=1
dig_with_opts +short @10.53.0.2 count txt >dig.out.2.test$n || ret=1
eval count=$(cat dig.out.2.test$n)
[ $count -le 40 ] || {
ret=1
@ -192,10 +198,10 @@ echo_i "attempt permissible lookup ($n)"
ret=0
echo "9" >ans2/ans.limit
ns3_reset ns3/named4.conf.in
$DIG $DIGOPTS @10.53.0.2 reset >/dev/null || ret=1
$DIG $DIGOPTS @10.53.0.3 indirect8.example.org >dig.out.1.test$n || ret=1
dig_with_opts @10.53.0.2 reset >/dev/null || ret=1
dig_with_opts @10.53.0.3 indirect8.example.org >dig.out.1.test$n || ret=1
grep "status: NOERROR" dig.out.1.test$n >/dev/null || ret=1
$DIG $DIGOPTS +short @10.53.0.2 count txt >dig.out.2.test$n || ret=1
dig_with_opts +short @10.53.0.2 count txt >dig.out.2.test$n || ret=1
eval count=$(cat dig.out.2.test$n)
[ $count -le 40 ] || {
ret=1
@ -208,12 +214,12 @@ n=$((n + 1))
echo_i "attempting NS explosion ($n)"
ret=0
ns3_reset ns3/named4.conf.in
$DIG $DIGOPTS @10.53.0.2 reset >/dev/null || ret=1
$DIG $DIGOPTS +short @10.53.0.3 ns1.1.example.net >dig.out.1.test$n || ret=1
$DIG $DIGOPTS +short @10.53.0.2 count txt >dig.out.2.test$n || ret=1
dig_with_opts @10.53.0.2 reset >/dev/null || ret=1
dig_with_opts +short @10.53.0.3 ns1.1.example.net >dig.out.1.test$n || ret=1
dig_with_opts +short @10.53.0.2 count txt >dig.out.2.test$n || ret=1
eval count=$(cat dig.out.2.test$n)
[ $count -lt 50 ] || ret=1
$DIG $DIGOPTS +short @10.53.0.7 count txt >dig.out.3.test$n || ret=1
dig_with_opts +short @10.53.0.7 count txt >dig.out.3.test$n || ret=1
eval count=$(cat dig.out.3.test$n)
[ $count -lt 50 ] || {
ret=1
@ -225,46 +231,253 @@ status=$((status + ret))
n=$((n + 1))
echo_i "checking RRset that exceeds max-records-per-type ($n)"
ret=0
$DIG $DIGOPTS @10.53.0.3 biganswer.big >dig.out.1.test$n || ret=1
dig_with_opts @10.53.0.3 biganswer.big >dig.out.1.test$n || ret=1
grep 'status: SERVFAIL' dig.out.1.test$n >/dev/null || ret=1
ns3_reset ns3/named5.conf.in
$DIG $DIGOPTS @10.53.0.3 biganswer.big >dig.out.2.test$n || ret=1
dig_with_opts @10.53.0.3 biganswer.big >dig.out.2.test$n || ret=1
grep 'status: NOERROR' dig.out.2.test$n >/dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
check_manytypes() (
i=$1
type=$2
expected=$3
name=$2
type=$3
expected=$4
exname=$5
extype=$6
ttl=$7
neq_ttl=$8
$DIG $DIGOPTS @10.53.0.3 IN $type manytypes.big >dig.out.$i.$type.test$n || exit 1
grep 'status: '"${expected}"'' dig.out.$i.$type.test$n >/dev/null || exit 1
if ! dig_with_opts @10.53.0.3 IN "$type" "$name" >"dig.out.$i.$type.test$n"; then
exit 1
fi
if ! grep 'status: '"${expected}"'' "dig.out.$i.$type.test$n" >/dev/null; then
exit 1
fi
if [ -n "$ttl" ] && ! grep -q "^$exname.[[:space:]]*${ttl}[[:space:]]*IN[[:space:]]*$extype" "dig.out.$i.$type.test$n"; then
exit 1
fi
if [ -n "${neq_ttl}" ] && grep -q "^$exname.[[:space:]]*${neq_ttl}[[:space:]]*IN[[:space:]]*$type" "dig.out.$i.$type.test$n"; then
exit 1
fi
exit 0
)
n=$((n + 1))
echo_i "checking name that exceeds max-types-per-name ($n)"
ret=0
echo_i "checking that priority names under the max-types-per-name limit get cached ($n)"
# Limited to 10 types - these should be fine
# Query for NXDOMAIN for items on our priority list - these should get cached
for rrtype in AAAA MX NS; do
check_manytypes 1 manytypes.big "${rrtype}" NOERROR big SOA 60 || ret=1
done
# Wait at least 1 second
for rrtype in AAAA MX NS; do
check_manytypes 2 manytypes.big "${rrtype}" NOERROR big SOA "" 60 || ret=1
done
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status + ret))
if [ $status -ne 0 ]; then exit 1; fi
ns3_flush
n=$((n + 1))
ret=0
echo_i "checking that NXDOMAIN names under the max-types-per-name limit get cached ($n)"
# Query for 10 NXDOMAIN types
for ntype in $(seq 65270 65279); do
check_manytypes 1 manytypes.big "TYPE${ntype}" NOERROR big SOA 60 || ret=1
done
# Wait at least 1 second
sleep 1
# Query for 10 NXDOMAIN types again - these should be cached
for ntype in $(seq 65270 65279); do
check_manytypes 2 manytypes.big "TYPE${ntype}" NOERROR big SOA "" 60 || ret=1
done
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status + ret))
if [ $status -ne 0 ]; then exit 1; fi
n=$((n + 1))
ret=0
echo_i "checking that existing names under the max-types-per-name limit get cached ($n)"
# Limited to 10 types - these should be cached and the previous record should be evicted
for ntype in $(seq 65280 65289); do
check_manytypes 1 "TYPE${ntype}" NOERROR || ret=1
check_manytypes 1 manytypes.big "TYPE${ntype}" NOERROR manytypes.big "TYPE${ntype}" 60 || ret=1
done
# Everything on top of that should SERVFAIL
for ntype in $(seq 65290 65534); do
check_manytypes 1 "TYPE${ntype}" SERVFAIL || ret=1
# Wait at least one second
sleep 1
# Limited to 10 types - these should be cached
for ntype in $(seq 65280 65289); do
check_manytypes 2 manytypes.big "TYPE${ntype}" NOERROR manytypes.big "TYPE${ntype}" "" 60 || ret=1
done
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status + ret))
if [ $status -ne 0 ]; then exit 1; fi
n=$((n + 1))
ret=0
echo_i "checking that NXDOMAIN names over the max-types-per-name limit don't get cached ($n)"
# Query for 10 NXDOMAIN types
for ntype in $(seq 65270 65279); do
check_manytypes 1 manytypes.big "TYPE${ntype}" NOERROR big SOA 0 || ret=1
done
# Wait at least 1 second
sleep 1
# Query for 10 NXDOMAIN types again - these should not be cached
for ntype in $(seq 65270 65279); do
check_manytypes 2 manytypes.big "TYPE${ntype}" NOERROR big SOA 0 || ret=1
done
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status + ret))
if [ $status -ne 0 ]; then exit 1; fi
n=$((n + 1))
ret=0
echo_i "checking that priority NXDOMAIN names over the max-types-per-name limit get cached ($n)"
# Query for NXDOMAIN for items on our priority list - these should get cached
for rrtype in AAAA MX NS; do
check_manytypes 1 manytypes.big "${rrtype}" NOERROR big SOA 60 || ret=1
done
# Wait at least 1 second
for rrtype in AAAA MX NS; do
check_manytypes 2 manytypes.big "${rrtype}" NOERROR big SOA "" 60 || ret=1
done
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status + ret))
if [ $status -ne 0 ]; then exit 1; fi
n=$((n + 1))
ret=0
echo_i "checking that priority name over the max-types-per-name get cached ($n)"
# Query for an item on our priority list - it should get cached
check_manytypes 1 manytypes.big "A" NOERROR manytypes.big A 60 || ret=1
# Wait at least 1 second
sleep 1
# Query the same name again - it should be in the cache
check_manytypes 2 manytypes.big "A" NOERROR big manytypes.A "" 60 || ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status + ret))
if [ $status -ne 0 ]; then exit 1; fi
ns3_flush
n=$((n + 1))
ret=0
echo_i "checking that priority name over the max-types-per-name don't get evicted ($n)"
# Query for an item on our priority list - it should get cached
check_manytypes 1 manytypes.big "A" NOERROR manytypes.big A 60 || ret=1
# Query for 10 more types - this should not evict A record
for ntype in $(seq 65280 65289); do
check_manytypes 1 manytypes.big "TYPE${ntype}" NOERROR manytypes.big || ret=1
done
# Wait at least 1 second
sleep 1
# Query the same name again - it should be in the cache
check_manytypes 2 manytypes.big "A" NOERROR manytypes.big A "" 60 || ret=1
# This one was first in the list and should have been evicted
check_manytypes 2 manytypes.big "TYPE65280" NOERROR manytypes.big TYPE65280 60 || ret=1
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status + ret))
if [ $status -ne 0 ]; then exit 1; fi
ns3_flush
n=$((n + 1))
ret=0
echo_i "checking that non-priority types cause eviction ($n)"
# Everything on top of that will cause the cache eviction
for ntype in $(seq 65280 65299); do
check_manytypes 1 manytypes.big "TYPE${ntype}" NOERROR manytypes.big "TYPE${ntype}" 60 || ret=1
done
# Wait at least one second
sleep 1
# These should have TTL != 60 now
for ntype in $(seq 65290 65299); do
check_manytypes 2 manytypes.big "TYPE${ntype}" NOERROR manytypes.big "TYPE${ntype}" "" 60 || ret=1
done
# These should have been evicted
for ntype in $(seq 65280 65289); do
check_manytypes 3 manytypes.big "TYPE${ntype}" NOERROR manytypes.big "TYPE${ntype}" 60 || ret=1
done
# These should have been evicted by the previous block
for ntype in $(seq 65290 65299); do
check_manytypes 4 manytypes.big "TYPE${ntype}" NOERROR manytypes.big "TYPE${ntype}" 60 || ret=1
done
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status + ret))
if [ $status -ne 0 ]; then exit 1; fi
ns3_flush
n=$((n + 1))
ret=0
echo_i "checking that signed names under the max-types-per-name limit get cached ($n)"
# Go through the 10 items, this should result in 20 items (type + rrsig(type))
for ntype in $(seq 65280 65289); do
check_manytypes 1 manytypes.signed "TYPE${ntype}" NOERROR manytypes.signed "TYPE${ntype}" 60 || ret=1
done
# Wait at least one second
sleep 1
# These should have TTL != 60 now
for ntype in $(seq 65285 65289); do
check_manytypes 2 manytypes.signed "TYPE${ntype}" NOERROR manytypes.signed "TYPE${ntype}" "" 60 || ret=1
done
# These should have been evicted
for ntype in $(seq 65280 65284); do
check_manytypes 3 manytypes.signed "TYPE${ntype}" NOERROR manytypes.signed "TYPE${ntype}" 60 || ret=1
done
# These should have been evicted by the previous block
for ntype in $(seq 65285 65289); do
check_manytypes 4 manytypes.signed "TYPE${ntype}" NOERROR manytypes.signed "TYPE${ntype}" 60 || ret=1
done
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status + ret))
if [ $status -ne 0 ]; then exit 1; fi
n=$((n + 1))
ret=0
echo_i "checking that lifting the limit will allow everything to get cached ($n)"
# Lift the limit
ns3_reset ns3/named6.conf.in
for ntype in $(seq 65280 65534); do
check_manytypes 2 "TYPE${ntype}" NOERROR || ret=1
check_manytypes 1 manytypes.big "TYPE${ntype}" NOERROR manytypes.big "TYPE${ntype}" 60 || ret=1
done
# Wait at least one second
sleep 1
for ntype in $(seq 65280 65534); do
check_manytypes 2 manytypes.big "TYPE${ntype}" NOERROR manytypes.big "TYPE${ntype}" "" 60 || ret=1
done
if [ $ret != 0 ]; then echo_i "failed"; fi
if [ $ret -ne 0 ]; then echo_i "failed"; fi
status=$((status + ret))
echo_i "exit status: $status"

View file

@ -474,18 +474,18 @@ n=$((n + 1))
echo_i "check prefetch qtype * (${n})"
ret=0
dig_with_opts @10.53.0.5 fetchall.tld any >dig.out.1.${n} || ret=1
ttl1=$(awk '/"A" "short" "ttl"/ { print $2 - 3 }' dig.out.1.${n})
ttl1=$(awk '/^fetchall.tld/ { print $2 - 3; exit }' dig.out.1.${n})
# sleep so we are in prefetch range
sleep "${ttl1:-0}"
# trigger prefetch
dig_with_opts @10.53.0.5 fetchall.tld any >dig.out.2.${n} || ret=1
ttl2=$(awk '/"A" "short" "ttl"/ { print $2 }' dig.out.2.${n})
ttl2=$(awk '/^fetchall.tld/ { print $2; exit }' dig.out.2.${n})
sleep 1
# check that prefetch occurred;
# note that only one record is prefetched, which is the AAAA record in this case,
# note that only the first record is prefetched,
# because of the order of the records in the cache
dig_with_opts @10.53.0.5 fetchall.tld any >dig.out.3.${n} || ret=1
ttl3=$(awk '/::1/ { print $2 }' dig.out.3.${n})
ttl3=$(awk '/^fetchall.tld/ { print $2; exit }' dig.out.3.${n})
test "${ttl3:-0}" -gt "${ttl2:-1}" || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))

View file

@ -145,4 +145,46 @@ typedef struct {
bool exiting;
} db_nodelock_t;
static inline bool
prio_type(dns_typepair_t type) {
switch (type) {
case dns_rdatatype_soa:
case DNS_SIGTYPE(dns_rdatatype_soa):
case dns_rdatatype_a:
case DNS_SIGTYPE(dns_rdatatype_a):
case dns_rdatatype_mx:
case DNS_SIGTYPE(dns_rdatatype_mx):
case dns_rdatatype_aaaa:
case DNS_SIGTYPE(dns_rdatatype_aaaa):
case dns_rdatatype_nsec:
case DNS_SIGTYPE(dns_rdatatype_nsec):
case dns_rdatatype_nsec3:
case DNS_SIGTYPE(dns_rdatatype_nsec3):
case dns_rdatatype_ns:
case DNS_SIGTYPE(dns_rdatatype_ns):
case dns_rdatatype_ds:
case DNS_SIGTYPE(dns_rdatatype_ds):
case dns_rdatatype_cname:
case DNS_SIGTYPE(dns_rdatatype_cname):
case dns_rdatatype_dname:
case DNS_SIGTYPE(dns_rdatatype_dname):
case dns_rdatatype_svcb:
case DNS_SIGTYPE(dns_rdatatype_svcb):
case dns_rdatatype_https:
case DNS_SIGTYPE(dns_rdatatype_https):
case dns_rdatatype_dnskey:
case DNS_SIGTYPE(dns_rdatatype_dnskey):
case dns_rdatatype_srv:
case DNS_SIGTYPE(dns_rdatatype_srv):
case dns_rdatatype_txt:
case DNS_SIGTYPE(dns_rdatatype_txt):
case dns_rdatatype_ptr:
case DNS_SIGTYPE(dns_rdatatype_ptr):
case dns_rdatatype_naptr:
case DNS_SIGTYPE(dns_rdatatype_naptr):
return (true);
}
return (false);
}
ISC_LANG_ENDDECLS

View file

@ -2438,30 +2438,6 @@ again:
}
}
static bool
prio_type(dns_typepair_t type) {
switch (type) {
case dns_rdatatype_soa:
case DNS_SIGTYPE(dns_rdatatype_soa):
case dns_rdatatype_a:
case DNS_SIGTYPE(dns_rdatatype_a):
case dns_rdatatype_aaaa:
case DNS_SIGTYPE(dns_rdatatype_aaaa):
case dns_rdatatype_nsec:
case DNS_SIGTYPE(dns_rdatatype_nsec):
case dns_rdatatype_nsec3:
case DNS_SIGTYPE(dns_rdatatype_nsec3):
case dns_rdatatype_ns:
case DNS_SIGTYPE(dns_rdatatype_ns):
case dns_rdatatype_ds:
case DNS_SIGTYPE(dns_rdatatype_ds):
case dns_rdatatype_cname:
case DNS_SIGTYPE(dns_rdatatype_cname):
return (true);
}
return (false);
}
/*%
* These functions allow the heap code to rank the priority of each
* element. It returns true if v1 happens "sooner" than v2.
@ -2871,6 +2847,24 @@ allrdatasets(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
return (ISC_R_SUCCESS);
}
static bool
overmaxtype(qpcache_t *qpdb, uint32_t ntypes) {
if (qpdb->maxtypepername == 0) {
return (false);
}
return (ntypes >= qpdb->maxtypepername);
}
static bool
prio_header(dns_slabheader_t *header) {
if (NEGATIVE(header) && prio_type(DNS_TYPEPAIR_COVERS(header->type))) {
return (true);
}
return (prio_type(header->type));
}
static isc_result_t
add(qpcache_t *qpdb, qpcnode_t *qpnode,
const dns_name_t *nodename ISC_ATTR_UNUSED, dns_slabheader_t *newheader,
@ -2879,14 +2873,13 @@ add(qpcache_t *qpdb, qpcnode_t *qpnode,
isc_rwlocktype_t tlocktype DNS__DB_FLARG) {
dns_slabheader_t *topheader = NULL, *topheader_prev = NULL;
dns_slabheader_t *header = NULL, *sigheader = NULL;
dns_slabheader_t *prioheader = NULL;
dns_slabheader_t *prioheader = NULL, *expireheader = NULL;
bool header_nx;
bool newheader_nx;
dns_rdatatype_t rdtype, covers;
dns_typepair_t negtype = 0, sigtype;
dns_typepair_t negtype = 0;
dns_trust_t trust;
int idx;
uint32_t ntypes;
uint32_t ntypes = 0;
if ((options & DNS_DBADD_FORCE) != 0) {
trust = dns_trust_ultimate;
@ -2895,10 +2888,11 @@ add(qpcache_t *qpdb, qpcnode_t *qpnode,
}
newheader_nx = NONEXISTENT(newheader) ? true : false;
if (!newheader_nx) {
rdtype = DNS_TYPEPAIR_TYPE(newheader->type);
covers = DNS_TYPEPAIR_COVERS(newheader->type);
sigtype = DNS_SIGTYPE(covers);
dns_rdatatype_t rdtype = DNS_TYPEPAIR_TYPE(newheader->type);
dns_rdatatype_t covers = DNS_TYPEPAIR_COVERS(newheader->type);
dns_typepair_t sigtype = DNS_SIGTYPE(covers);
if (NEGATIVE(newheader)) {
/*
* We're adding a negative cache entry.
@ -2919,7 +2913,6 @@ add(qpcache_t *qpdb, qpcnode_t *qpnode,
{
mark_ancient(topheader);
}
ntypes = 0; /* Always add the negative entry */
goto find_header;
}
/*
@ -2931,6 +2924,7 @@ add(qpcache_t *qpdb, qpcnode_t *qpnode,
{
if (topheader->type == sigtype) {
sigheader = topheader;
break;
}
}
negtype = DNS_TYPEPAIR_VALUE(covers, 0);
@ -2943,11 +2937,9 @@ add(qpcache_t *qpdb, qpcnode_t *qpnode,
* check for an extant non-ancient NODATA ncache
* entry which covers the same type as the RRSIG.
*/
ntypes = 0;
for (topheader = qpnode->data; topheader != NULL;
topheader = topheader->next)
{
++ntypes;
if ((topheader->type == RDATATYPE_NCACHEANY) ||
(newheader->type == sigtype &&
topheader->type ==
@ -2990,15 +2982,17 @@ add(qpcache_t *qpdb, qpcnode_t *qpnode,
}
}
ntypes = 0;
for (topheader = qpnode->data; topheader != NULL;
topheader = topheader->next)
{
++ntypes;
if (prio_type(topheader->type)) {
if (ACTIVE(topheader, now)) {
++ntypes;
expireheader = topheader;
}
if (prio_header(topheader)) {
prioheader = topheader;
}
if (topheader->type == newheader->type ||
topheader->type == negtype)
{
@ -3263,17 +3257,9 @@ find_header:
/*
* No rdatasets of the given type exist at the node.
*/
if (trust != dns_trust_ultimate &&
qpdb->maxtypepername > 0 &&
ntypes >= qpdb->maxtypepername)
{
dns_slabheader_destroy(&newheader);
return (DNS_R_TOOMANYRECORDS);
}
INSIST(newheader->down == NULL);
if (prio_type(newheader->type)) {
if (prio_header(newheader)) {
/* This is a priority type, prepend it */
newheader->next = qpnode->data;
qpnode->data = newheader;
@ -3286,6 +3272,30 @@ find_header:
newheader->next = qpnode->data;
qpnode->data = newheader;
}
if (overmaxtype(qpdb, ntypes)) {
if (expireheader == NULL) {
expireheader = newheader;
}
if (NEGATIVE(newheader) &&
!prio_header(newheader))
{
/*
* Add the new non-priority negative
* header to the database only
* temporarily.
*/
expireheader = newheader;
}
mark_ancient(expireheader);
/*
* FIXME: In theory, we should mark the RRSIG
* and the header at the same time, but there is
* no direct link between those two header, so
* we would have to check the whole list again.
*/
}
}
}

View file

@ -330,31 +330,6 @@ typedef struct qpdb_dbiterator {
*/
static atomic_uint_fast16_t init_count = 0;
static bool
prio_type(dns_typepair_t type) {
switch (type) {
case dns_rdatatype_soa:
case DNS_SIGTYPE(dns_rdatatype_soa):
case dns_rdatatype_a:
case DNS_SIGTYPE(dns_rdatatype_a):
case dns_rdatatype_aaaa:
case DNS_SIGTYPE(dns_rdatatype_aaaa):
case dns_rdatatype_nsec:
case DNS_SIGTYPE(dns_rdatatype_nsec):
case dns_rdatatype_ns:
case DNS_SIGTYPE(dns_rdatatype_ns):
case dns_rdatatype_ds:
case DNS_SIGTYPE(dns_rdatatype_ds):
case dns_rdatatype_nsec3:
case DNS_SIGTYPE(dns_rdatatype_nsec3):
case dns_rdatatype_cname:
case DNS_SIGTYPE(dns_rdatatype_cname):
return (true);
}
return (false);
}
/*
* Locking
*

View file

@ -351,30 +351,6 @@ dns__rbtdb_setttl(dns_slabheader_t *header, dns_ttl_t newttl) {
}
}
static bool
prio_type(dns_typepair_t type) {
switch (type) {
case dns_rdatatype_soa:
case DNS_SIGTYPE(dns_rdatatype_soa):
case dns_rdatatype_a:
case DNS_SIGTYPE(dns_rdatatype_a):
case dns_rdatatype_aaaa:
case DNS_SIGTYPE(dns_rdatatype_aaaa):
case dns_rdatatype_nsec:
case DNS_SIGTYPE(dns_rdatatype_nsec):
case dns_rdatatype_nsec3:
case DNS_SIGTYPE(dns_rdatatype_nsec3):
case dns_rdatatype_ns:
case DNS_SIGTYPE(dns_rdatatype_ns):
case dns_rdatatype_ds:
case DNS_SIGTYPE(dns_rdatatype_ds):
case dns_rdatatype_cname:
case DNS_SIGTYPE(dns_rdatatype_cname):
return (true);
}
return (false);
}
/*%
* These functions allow the heap code to rank the priority of each
* element. It returns true if v1 happens "sooner" than v2.
@ -2548,6 +2524,24 @@ update_recordsandxfrsize(bool add, dns_rbtdb_version_t *rbtversion,
RWUNLOCK(&rbtversion->rwlock, isc_rwlocktype_write);
}
static bool
overmaxtype(dns_rbtdb_t *rbtdb, uint32_t ntypes) {
if (rbtdb->maxtypepername == 0) {
return (false);
}
return (ntypes >= rbtdb->maxtypepername);
}
static bool
prio_header(dns_slabheader_t *header) {
if (NEGATIVE(header) && prio_type(DNS_TYPEPAIR_COVERS(header->type))) {
return (true);
}
return (prio_type(header->type));
}
isc_result_t
dns__rbtdb_add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode,
const dns_name_t *nodename, dns_rbtdb_version_t *rbtversion,
@ -2556,7 +2550,7 @@ dns__rbtdb_add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode,
rbtdb_changed_t *changed = NULL;
dns_slabheader_t *topheader = NULL, *topheader_prev = NULL;
dns_slabheader_t *header = NULL, *sigheader = NULL;
dns_slabheader_t *prioheader = NULL;
dns_slabheader_t *prioheader = NULL, *expireheader = NULL;
unsigned char *merged = NULL;
isc_result_t result;
bool header_nx;
@ -2619,7 +2613,6 @@ dns__rbtdb_add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode,
{
mark_ancient(topheader);
}
ntypes = 0; /* Always add the negative entry */
goto find_header;
}
/*
@ -2631,6 +2624,7 @@ dns__rbtdb_add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode,
{
if (topheader->type == sigtype) {
sigheader = topheader;
break;
}
}
negtype = DNS_TYPEPAIR_VALUE(covers, 0);
@ -2643,11 +2637,9 @@ dns__rbtdb_add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode,
* check for an extant non-ancient NODATA ncache
* entry which covers the same type as the RRSIG.
*/
ntypes = 0;
for (topheader = rbtnode->data; topheader != NULL;
topheader = topheader->next)
{
++ntypes;
if ((topheader->type == RDATATYPE_NCACHEANY) ||
(newheader->type == sigtype &&
topheader->type ==
@ -2690,12 +2682,16 @@ dns__rbtdb_add(dns_rbtdb_t *rbtdb, dns_rbtnode_t *rbtnode,
}
}
ntypes = 0;
for (topheader = rbtnode->data; topheader != NULL;
topheader = topheader->next)
{
++ntypes;
if (prio_type(topheader->type)) {
if (IS_CACHE(rbtdb) && ACTIVE(topheader, now)) {
++ntypes;
expireheader = topheader;
} else if (!IS_CACHE(rbtdb)) {
++ntypes;
}
if (prio_header(topheader)) {
prioheader = topheader;
}
if (topheader->type == newheader->type ||
@ -3088,17 +3084,14 @@ find_header:
/*
* No rdatasets of the given type exist at the node.
*/
INSIST(newheader->down == NULL);
if (rbtdb->maxtypepername > 0 &&
ntypes >= rbtdb->maxtypepername)
{
if (!IS_CACHE(rbtdb) && overmaxtype(rbtdb, ntypes)) {
dns_slabheader_destroy(&newheader);
return (DNS_R_TOOMANYRECORDS);
}
INSIST(newheader->down == NULL);
if (prio_type(newheader->type)) {
if (prio_header(newheader)) {
/* This is a priority type, prepend it */
newheader->next = rbtnode->data;
rbtnode->data = newheader;
@ -3111,6 +3104,30 @@ find_header:
newheader->next = rbtnode->data;
rbtnode->data = newheader;
}
if (IS_CACHE(rbtdb) && overmaxtype(rbtdb, ntypes)) {
if (expireheader == NULL) {
expireheader = newheader;
}
if (NEGATIVE(newheader) &&
!prio_header(newheader))
{
/*
* Add the new non-priority negative
* header to the database only
* temporarily.
*/
expireheader = newheader;
}
mark_ancient(expireheader);
/*
* FIXME: In theory, we should mark the RRSIG
* and the header at the same time, but there is
* no direct link between those two header, so
* we would have to check the whole list again.
*/
}
}
}

View file

@ -21,6 +21,8 @@
#include <dns/rbt.h>
#include <dns/types.h>
#include "db_p.h" /* for db_nodelock_t */
/*%
* Note that "impmagic" is not the first four bytes of the struct, so
* ISC_MAGIC_VALID cannot be used.