From 8e15d5eb3a000f1341e6bea0ddbc28d6dd2a0591 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Wed, 12 Jun 2013 11:31:30 +1000 Subject: [PATCH] 3593. [func] Update EDNS processing to better track remote server capabilities. [RT #30655] --- CHANGES | 3 + EDNS-9.10 | 31 ++ bin/named/main.c | 5 +- bin/named/named.docbook | 13 + bin/tests/bigtest/README | 15 + bin/tests/bigtest/buildzones.sh | 257 +++++++++++++ bin/tests/bigtest/rndc.key | 5 + bin/tests/bigtest/tests.sh | 67 ++++ bin/tests/bigtest/zones | 18 + bin/tests/system/cacheclean/tests.sh | 8 +- bin/tests/system/checknames/tests.sh | 4 +- bin/tests/system/dnssec/tests.sh | 21 +- bin/tests/system/forward/tests.sh | 28 +- bin/tests/system/lwresd/lwtest.c | 1 + bin/tests/system/org.isc.bind.system | 2 +- bin/tests/system/start.pl | 9 + .../system/staticstub/knowngood.dig.out.rec | 3 - bin/tests/system/staticstub/tests.sh | 2 +- bin/tests/system/stub/knowngood.dig.out.rec | 6 - bin/tests/system/stub/tests.sh | 2 +- lib/dns/adb.c | 287 +++++++++++++-- lib/dns/include/dns/adb.h | 87 +++++ lib/dns/resolver.c | 339 +++++++++++++----- 23 files changed, 1059 insertions(+), 154 deletions(-) create mode 100644 EDNS-9.10 create mode 100644 bin/tests/bigtest/README create mode 100644 bin/tests/bigtest/buildzones.sh create mode 100644 bin/tests/bigtest/rndc.key create mode 100644 bin/tests/bigtest/tests.sh create mode 100644 bin/tests/bigtest/zones diff --git a/CHANGES b/CHANGES index bd5de9e57d..4a4b8cfe34 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,6 @@ +3593. [func] Update EDNS processing to better track remote server + capabilities. [RT #30655] + 3592. [doc] Moved documentation of rndc command options to the rndc man page. [RT #33506] diff --git a/EDNS-9.10 b/EDNS-9.10 new file mode 100644 index 0000000000..a14ec50743 --- /dev/null +++ b/EDNS-9.10 @@ -0,0 +1,31 @@ + + EDNS in BIND 9.10 + +The EDNS code in BIND 9.10 records successful plain and EDNS query +counts as well at timeouts for plain DNS and EDNS queries at various +EDNS buffer sizes: 4096, 1432, 1232 and 512 for each server named +talks to. A EDNS timeout for a lower buffer size is also counted +against higher buffer sizes. These are held in 8 bit counters and +are shifted on overflow of any counter. This will result in false +positives due to transitory network problems to be removed from the +history. + +The buffer sizes of 1432 and 1232 are choosen to allow for a IPv4/IPv6 +encapsulated UDP message to be sent without fragmentation at Ethernet +and IPv6 network mimimum MTU sizes. + +Named also records the largest successful EDNS response size seen. + +When talking to a new server named will send a EDNS query advertising +a 512 byte UDP buffer. This is the most conservative EDNS message +that can be sent. If this results in a response with TC=1 being +sent a larger EDNS buffer size will be used rather than a immediate +fallback to TCP. + +If there are too many timeouts to EDNS queries without a successful EDNS +query and with successful plain DNS queries named will fallback to using +plain DNS when taking to a server. Named will periodically send a EDNS +query to see if the server now supports EDNS. + +When talking to a server using EDNS named will choose a EDNS buffer size +based on the history of EDNS timeouts at various advertised sizes. diff --git a/bin/named/main.c b/bin/named/main.c index 39efe3f92f..463703e2f7 100644 --- a/bin/named/main.c +++ b/bin/named/main.c @@ -419,7 +419,7 @@ parse_command_line(int argc, char *argv[]) { isc_commandline_errprint = ISC_FALSE; while ((ch = isc_commandline_parse(argc, argv, - "46c:C:d:E:fFgi:lm:n:N:p:P:" + "46c:C:d:D:E:fFgi:lm:n:N:p:P:" "sS:t:T:U:u:vVx:")) != -1) { switch (ch) { case '4': @@ -455,6 +455,9 @@ parse_command_line(int argc, char *argv[]) { ns_g_debuglevel = parse_int(isc_commandline_argument, "debug level"); break; + case 'D': + /* Descriptive comment for 'ps'. */ + break; case 'E': ns_g_engine = isc_commandline_argument; break; diff --git a/bin/named/named.docbook b/bin/named/named.docbook index 1f08e196e0..16242f6899 100644 --- a/bin/named/named.docbook +++ b/bin/named/named.docbook @@ -62,6 +62,7 @@ + @@ -149,6 +150,18 @@ + + -D string + + + Specifies a string that is used to identify a instance of + named in a process listing. The contents + of string are + not examined. + + + + -E engine-name diff --git a/bin/tests/bigtest/README b/bin/tests/bigtest/README new file mode 100644 index 0000000000..eed30a6d05 --- /dev/null +++ b/bin/tests/bigtest/README @@ -0,0 +1,15 @@ + + bash buildzones.sh < zones # creates setup, run, servers/* master/* + # named.conf + sudo sh setup # configure interfaces + sh run # setup + + ../named/named [-g] -c named.conf + + sh tests.sh < zones + + sudo sh teardown # teardown interfaces + +The test server can controlled with + + rndc -k rndc.key -s 127.127.0.0 -p 5300 diff --git a/bin/tests/bigtest/buildzones.sh b/bin/tests/bigtest/buildzones.sh new file mode 100644 index 0000000000..aabc3d877a --- /dev/null +++ b/bin/tests/bigtest/buildzones.sh @@ -0,0 +1,257 @@ +#!/bin/bash + +TOP=$( (cd ../../.. && pwd) ) + +addr=127.127.0.0 +ttl=300 +named=${TOP}/bin/named/named +keygen=${TOP}/bin/dnssec/dnssec-keygen +dsfromkey=${TOP}/bin/dnssec/dnssec-dsfromkey + +nextaddr() { + OLDIF="$IFS" + IFS="${IFS}." + set $1 + IFS="$OLDIFS" + _a=$1 _b=$2 _c=$3 _d=$4 + _d=$(($_d + 1)) + case $_d in + 256) _c=$(($_c + 1)); _d=0;; + esac + case $_c in + 256) _b=$(($_b + 1)); _c=0;; + esac + echo $_a.$_b.$_c.$_d +} + +parent() { + OLDIF="$IFS" + IFS="${IFS}." + set $1 + IFS="$OLDIFS" + shift + while [ $# -ne 0 ] + do + printf %s ${1} + shift + printf %s ${1:+.} + + done +} + +blackhole() { + echo 'options {' + echo ' port 5300;' + echo " listen-on { $1; };" + echo " query-source $1;" + echo " notify-source $1;" + echo " transfer-source $1;" + echo ' key-directory "keys";' + echo " recursion ${2:-no};" + echo ' pid-file "pids/'"${addr}"'.pid";' + echo ' blackhole { 127.127.0.0; };' + echo '};' +} + +refuse() { + echo 'options {' + echo ' port 5300;' + echo " listen-on { $1; };" + echo " query-source $1;" + echo " notify-source $1;" + echo " transfer-source $1;" + echo ' key-directory "keys";' + echo " recursion ${2:-no};" + echo ' pid-file "pids/'"${addr}"'.pid";' + echo ' allow-query { !127.127.0.0; any; };' + echo '};' +} + +options() { + echo 'options {' + echo ' port 5300;' + echo " listen-on { $1; };" + echo " query-source $1;" + echo " notify-source $1;" + echo " transfer-source $1;" + echo ' key-directory "keys";' + echo " recursion ${2:-no};" + echo ' pid-file "pids/'"${addr}"'.pid";' + echo '};' +} + +controls() { + echo 'include "rndc.key";' + echo "controls { inet $addr port 9953 allow { any; } keys { "rndc-key"; }; };" +} + +delay() { + _s=$1 + OLDIF="$IFS" + IFS="${IFS}/" + set ${2:-.} + IFS="$OLDIFS" + + case $1 in + .) _d=;; + *) _d=$1;; + esac + case $_s in + 1) echo -T delay=${_d:-100};; + 2) echo -T delay=${2:-50};; + 3) echo -T delay=${3:-150};; + 4) echo -T delay=${4:-250};; + 5) echo -T delay=${5:-125};; + 6) echo -T delay=${6:-25};; + 7) echo -T delay=${7:-75};; + 8) echo -T delay=${8:-125};; + 9) echo -T delay=${9:-10};; + 10) echo -T delay=${10:-40};; + 11) echo -T delay=${11:-80};; + 12) echo -T delay=${12:-90};; + *) echo -T delay=50;; + esac +} + +trusted-keys () { + awk '$3 == "DNSKEY" { + b = ""; for (i=7; i <= NF; i++) { b = b $i; }; + print "trusted-keys { \""$1"\"",$4,$5,$6,"\""b"\"; };" };' +} + +signed-zone () { + echo "zone "'"'"${1:-.}"'"'" {" + echo " type master;" + echo " file "'"'"master/${2}.db"'"'";" + echo " auto-dnssec maintain;" + echo " allow-update { any; };" + echo "};" +} + +unsigned-zone () { + echo "zone "'"'"${1:-.}"'"'" {" + echo " type master;" + echo " file "'"'"master/${2}.db"'"'";" + echo "};" +} + +slave-zone () { + echo "zone "'"'"${zone:-.}"'"'" {" + echo " type slave;" + echo " masters { ${master}; };" + echo "};" +} + +rm -rf servers master keys setup teardown run +mkdir -p servers +mkdir -p master +mkdir -p keys + +echo "ifconfig lo0 $addr netmask 0xffffffff alias" >> setup +echo "ifconfig lo0 $addr -alias" >> teardown +controls $addr > named.conf +options $addr yes >> named.conf +echo 'zone "." { type hint; file "master/hint.db"; };' >> named.conf + +while read zone servers nsfmt signed delay blackhole refuse flags +do + i=1 + case "${zone}" in + .) file=root zone=;; + *) file="$zone";; + esac + if [ "${zone}" != "" ] ; then + p=$(parent $zone) + case "${p}" in + "") p=root;; + esac + else + p=hint + fi + #echo "zone='${zone}' parent='${p}'" + addr=$(nextaddr $addr) + ns=$(printf "$nsfmt" ${i} "${zone}") + d=$(delay $i ${delay:-.}) + + echo "${zone}. ${ttl} soa ${ns}. hostmaster.${zone}${zone:+.} 1 3600 1200 604800 1200" >> master/${file}.db + echo "${zone}. ${ttl} ns ${ns}." >> master/${file}.db + echo "${ns}. ${ttl} a ${addr}" >> master/${file}.db + echo "${zone}. ${ttl} ns ${ns}." >> master/${p}.db + echo "${ns}. ${ttl} a ${addr}" >> master/${p}.db + if [ $signed = "S" ]; then + kskkey=`${keygen} -K keys -f KSK ${zone:-.}` + zskkey=`${keygen} -K keys ${zone:-.}` + if [ "${zone}" != "" ] ; then + ${dsfromkey} -T ${ttl} keys/${kskkey}.key >> master/${p}.db + else + trusted-keys < keys/${kskkey}.key >> named.conf + fi + fi + echo "ifconfig lo0 $addr netmask 0xffffffff alias" >> setup + echo "ifconfig lo0 $addr -alias" >> teardown + echo "${named} -D bigtest -c servers/${addr}.conf $d $flags" >> run + options ${addr} > servers/${addr}.conf + case ${signed} in + S) signed-zone ${zone:-.} ${file} >> servers/${addr}.conf;; + P) unsigned-zone ${zone:-.} ${file} >> servers/${addr}.conf;; + *) echo ${signed}; exit 1;; + esac + + # slave servers + while [ $i -lt $servers ] + do + master=$addr + i=$(($i + 1)) + ns=$(printf "$nsfmt" ${i} "${zone}") + d=$(delay $i ${delay:-.}) + addr=$(nextaddr $addr) + echo "${zone}. ${ttl} ns ${ns}." >> master/${file}.db + echo "${ns}. ${ttl} a ${addr}" >> master/${file}.db + echo "${zone}. ${ttl} ns ${ns}." >> master/${p}.db + echo "${ns}. ${ttl} a ${addr}" >> master/${p}.db + echo "ifconfig lo0 $addr netmask 0xffffffff alias" >> setup + echo "ifconfig lo0 $addr -alias" >> teardown + echo "${named} -D bigtest -c servers/${addr}.conf $d $flags" >> run + if [ $i = ${refuse:-.} ] + then + refuse $addr > servers/${addr}.conf + elif [ $i = ${blackhole:-.} ] + then + blackhole $addr > servers/${addr}.conf + else + options $addr > servers/${addr}.conf + fi + slave-zone ${zone:-.} ${master} >> servers/${addr}.conf + done + if [ "${zone}" != "" ] ; then + echo "www.${zone}. ${ttl} a 127.0.0.1" >> master/${file}.db + echo "www.${zone}. ${ttl} aaaa ::1" >> master/${file}.db + echo "${zone}. ${ttl} mx 10 mail.${zone}." >> master/${file}.db + echo "mail.${zone}. ${ttl} a 127.0.0.1" >> master/${file}.db + echo "mail.${zone}. ${ttl} aaaa ::1" >> master/${file}.db + echo "*.big.${zone}. ${ttl} txt (" >> master/${file}.db + i=0 + while [ $i -lt 150 ] + do + echo "1234567890" >> master/${file}.db + i=$(($i + 1)) + done + echo ")" >> master/${file}.db + echo "*.medium.${zone}. ${ttl} txt (" >> master/${file}.db + i=0 + while [ $i -lt 120 ] + do + echo "1234567890" >> master/${file}.db + i=$(($i + 1)) + done + echo ")" >> master/${file}.db + echo "*.medium.${zone}. ${ttl} txt (" >> master/${file}.db + i=0 + while [ $i -lt 120 ] + do + echo "1234567890" >> master/${file}.db + i=$(($i + 1)) + done + echo ")" >> master/${file}.db + fi +done diff --git a/bin/tests/bigtest/rndc.key b/bin/tests/bigtest/rndc.key new file mode 100644 index 0000000000..f279e14c1a --- /dev/null +++ b/bin/tests/bigtest/rndc.key @@ -0,0 +1,5 @@ +key "rndc-key" { + algorithm hmac-md5; + secret "xxxxxxxxxxxxxxxxxxxxHg=="; +}; + diff --git a/bin/tests/bigtest/tests.sh b/bin/tests/bigtest/tests.sh new file mode 100644 index 0000000000..4bdda3f982 --- /dev/null +++ b/bin/tests/bigtest/tests.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +TOP=$( (cd ../../.. && pwd) ) +dig=${TOP}/bin/dig/dig + +cmd="${dig} -p 5300 @127.127.0.0 txt" +inner() { + zone=$1 i=$2 to=$3 + x=$i + dout=dig$x.out + tout=time$x.out + while [ $i -lt $to ] + do + case $zone in + .) zone=;; + esac + + (time -p $cmd $i.${sub}$zone > $dout ) 2> $tout + s=`sed -n '/real/s/[^0-9]*\([0-9]*\)\..*/\1/p' $tout` + case $s in + 0);; + 1) t1=`expr ${t1:-0} + 1`;; + 2) t2=`expr ${t2:-0} + 1`;; + 3) t3=`expr ${t3:-0} + 1`;; + *) echo $i `grep real $tout`;; + esac + + grep "status: \(NXDOMAIN\|NOERROR\)" $dout > /dev/null || { + echo $cmd $i.${sub}$zone + cat $dout + } + i=`expr $i + 1` + done + if test ${t1:-0} -ne 0 -o ${t2:-0} -ne 0 -o ${t3:-0} -ne 0 + then + echo "$x timeouts: t1=${t1:-0} t2=${t2:-0} t3=${t3:-0}" + fi +} + +while read zone rest +do + for sub in "" medium. big. + do + case $zone in + .) echo doing ${sub:-.};; + *) echo doing $sub$zone;; + esac + ( inner $zone 1 100) & + ( inner $zone 101 200) & + ( inner $zone 201 300) & + ( inner $zone 301 400) & + ( inner $zone 401 500) & + ( inner $zone 501 600) & + ( inner $zone 601 700) & + ( inner $zone 701 800) & + ( inner $zone 801 900) & + ( inner $zone 901 1000) & + ( inner $zone 1001 1100) & + ( inner $zone 1101 1200) & + ( inner $zone 1201 1300) & + ( inner $zone 1301 1400) & + ( inner $zone 1401 1500) & + ( inner $zone 1501 1600) & + ( inner $zone 1601 1700) & + wait + done +done diff --git a/bin/tests/bigtest/zones b/bin/tests/bigtest/zones new file mode 100644 index 0000000000..0bdcdfe23f --- /dev/null +++ b/bin/tests/bigtest/zones @@ -0,0 +1,18 @@ +noedns-1.tld 1 ns%u.%s P . x x -T noedns +dropedns-1.tld 1 ns%u.%s P . x x -T dropedns +maxudp512-1.tld 1 ns%u.%s S . x x -T maxudp=512 +maxudp1460-1.tld 1 ns%u.%s S . x x -T maxudp=1460 +plain-1.tld 1 ns%u.%s S . x x +noedns-3.tld 3 ns%u.%s P . 2 x -T noedns +dropedns-3.tld 3 ns%u.%s P . 2 x -T dropedns +maxudp512-3.tld 3 ns%u.%s S . x x -T maxudp=512 +maxudp1460-3.tld 3 ns%u.%s S . x x -T maxudp=1460 +plain-3.tld 3 ns%u.%s S . x 3 +noedns-5.tld 5 ns%u.%s P . 3 x -T noedns +dropedns-5.tld 5 ns%u.%s P . x x -T dropedns +maxudp512-5.tld 5 ns%u.%s S . x x -T maxudp=512 +maxudp1460-5.tld 5 ns%u.%s S . x x -T maxudp=1460 +400ms-1.tld 5 ns%u.%s S 400/400/400/400/400 2 x +plain-5.tld 5 ns%u.%s S . x x +tld 12 ns%u.%s S . 5 8 +. 12 ns%u.root-servers.nil%s S . x x diff --git a/bin/tests/system/cacheclean/tests.sh b/bin/tests/system/cacheclean/tests.sh index 9c7ed97e93..2f711b3d5e 100644 --- a/bin/tests/system/cacheclean/tests.sh +++ b/bin/tests/system/cacheclean/tests.sh @@ -94,8 +94,8 @@ echo "I:reset and check that records are correctly cached initially" ret=0 load_cache dump_cache -nrecords=`grep flushtest.example ns2/named_dump.db | grep -v '^;' | wc -l` -[ $nrecords -eq 20 ] || ret=1 +nrecords=`grep flushtest.example ns2/named_dump.db | grep -v '^;' | grep -w '\(TXT\|ANY\)'| wc -l` +[ $nrecords -eq 17 ] || { ret=1; echo "I: found $nrecords records expected 17"; } if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` @@ -180,8 +180,8 @@ status=`expr $status + $ret` echo "I:check the number of cached records remaining" ret=0 dump_cache -nrecords=`grep flushtest.example ns2/named_dump.db | grep -v '^;' | wc -l` -[ $nrecords -eq 19 ] || ret=1 +nrecords=`grep flushtest.example ns2/named_dump.db | grep -v '^;' | grep -w '\(TXT\|ANY\)' | wc -l` +[ $nrecords -eq 17 ] || { ret=1; echo "I: found $nrecords records expected 17"; } if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` diff --git a/bin/tests/system/checknames/tests.sh b/bin/tests/system/checknames/tests.sh index 2a45159b7c..566078a7b9 100644 --- a/bin/tests/system/checknames/tests.sh +++ b/bin/tests/system/checknames/tests.sh @@ -53,8 +53,8 @@ n=`expr $n + 1` # Entry should exist echo "I: check that 'check-names response warn;' works ($n)" ret=0 -$DIG $DIGOPTS yy_yy.ignore.example. @10.53.0.1 a > dig.out.ns1.test$n || ret=1 -$DIG $DIGOPTS yy_yy.ignore.example. @10.53.0.2 a > dig.out.ns2.test$n || ret=1 +$DIG $DIGOPTS +noauth yy_yy.ignore.example. @10.53.0.1 a > dig.out.ns1.test$n || ret=1 +$DIG $DIGOPTS +noauth yy_yy.ignore.example. @10.53.0.2 a > dig.out.ns2.test$n || ret=1 $PERL ../digcomp.pl dig.out.ns1.test$n dig.out.ns2.test$n || ret=1 grep "check-names warning yy_yy.ignore.example/A/IN" ns2/named.run > /dev/null || ret=1 if [ $ret != 0 ]; then echo "I:failed"; fi diff --git a/bin/tests/system/dnssec/tests.sh b/bin/tests/system/dnssec/tests.sh index 57b9d67f48..839ff61666 100644 --- a/bin/tests/system/dnssec/tests.sh +++ b/bin/tests/system/dnssec/tests.sh @@ -76,6 +76,11 @@ israw1 () { return $? } +# strip NS and RRSIG NS from input +stripns () { + awk '($4 == "NS") || ($4 == "RRSIG" && $5 == "NS") { next} { print }' $1 +} + # Check the example. domain echo "I:checking that zone transfer worked ($n)" @@ -193,9 +198,13 @@ fi echo "I:checking positive wildcard validation NSEC ($n)" ret=0 -$DIG $DIGOPTS a.wild.example. @10.53.0.2 a > dig.out.ns2.test$n || ret=1 +$DIG $DIGOPTS a.wild.example. @10.53.0.3 a > dig.out.ns3.test$n || ret=1 $DIG $DIGOPTS a.wild.example. @10.53.0.4 a > dig.out.ns4.test$n || ret=1 -$PERL ../digcomp.pl dig.out.ns2.test$n dig.out.ns4.test$n || ret=1 +stripns dig.out.ns3.test$n > dig.out.ns3.stripped.test$n +stripns dig.out.ns4.test$n > dig.out.ns4.stripped.test$n +$PERL ../digcomp.pl dig.out.ns3.stripped.test$n dig.out.ns4.stripped.test$n || ret=1 +grep "\*\.wild\.example\..*RRSIG NSEC" dig.out.ns4.test$n > /dev/null || ret=1 +grep "\*\.wild\.example\..*NSEC z\.example" dig.out.ns4.test$n > /dev/null || ret=1 grep "flags:.*ad.*QUERY" dig.out.ns4.test$n > /dev/null || ret=1 grep "status: NOERROR" dig.out.ns4.test$n > /dev/null || ret=1 n=`expr $n + 1` @@ -235,7 +244,9 @@ echo "I:checking positive wildcard validation NSEC3 ($n)" ret=0 $DIG $DIGOPTS a.wild.nsec3.example. @10.53.0.3 a > dig.out.ns3.test$n || ret=1 $DIG $DIGOPTS a.wild.nsec3.example. @10.53.0.4 a > dig.out.ns4.test$n || ret=1 -$PERL ../digcomp.pl dig.out.ns3.test$n dig.out.ns4.test$n || ret=1 +stripns dig.out.ns3.test$n > dig.out.ns3.stripped.test$n +stripns dig.out.ns4.test$n > dig.out.ns4.stripped.test$n +$PERL ../digcomp.pl dig.out.ns3.stripped.test$n dig.out.ns4.stripped.test$n || ret=1 grep "flags:.*ad.*QUERY" dig.out.ns4.test$n > /dev/null || ret=1 grep "status: NOERROR" dig.out.ns4.test$n > /dev/null || ret=1 n=`expr $n + 1` @@ -259,7 +270,9 @@ $DIG $DIGOPTS a.wild.optout.example. \ @10.53.0.3 a > dig.out.ns3.test$n || ret=1 $DIG $DIGOPTS a.wild.optout.example. \ @10.53.0.4 a > dig.out.ns4.test$n || ret=1 -$PERL ../digcomp.pl dig.out.ns3.test$n dig.out.ns4.test$n || ret=1 +stripns dig.out.ns3.test$n > dig.out.ns3.stripped.test$n +stripns dig.out.ns4.test$n > dig.out.ns4.stripped.test$n +$PERL ../digcomp.pl dig.out.ns3.stripped.test$n dig.out.ns4.stripped.test$n || ret=1 grep "flags:.*ad.*QUERY" dig.out.ns4.test$n > /dev/null || ret=1 grep "status: NOERROR" dig.out.ns4.test$n > /dev/null || ret=1 n=`expr $n + 1` diff --git a/bin/tests/system/forward/tests.sh b/bin/tests/system/forward/tests.sh index e9f587eead..f4bef088d3 100644 --- a/bin/tests/system/forward/tests.sh +++ b/bin/tests/system/forward/tests.sh @@ -27,56 +27,56 @@ status=0 echo "I:checking that a forward zone overrides global forwarders" ret=0 -$DIG txt.example1. txt @$hidden -p 5300 > dig.out.hidden || ret=1 -$DIG txt.example1. txt @$f1 -p 5300 > dig.out.f1 || ret=1 +$DIG +noadd +noauth txt.example1. txt @$hidden -p 5300 > dig.out.hidden || ret=1 +$DIG +noadd +noauth txt.example1. txt @$f1 -p 5300 > dig.out.f1 || ret=1 $PERL ../digcomp.pl dig.out.hidden dig.out.f1 || ret=1 if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` echo "I:checking that a forward first zone no forwarders recurses" ret=0 -$DIG txt.example2. txt @$root -p 5300 > dig.out.root || ret=1 -$DIG txt.example2. txt @$f1 -p 5300 > dig.out.f1 || ret=1 +$DIG +noadd +noauth txt.example2. txt @$root -p 5300 > dig.out.root || ret=1 +$DIG +noadd +noauth txt.example2. txt @$f1 -p 5300 > dig.out.f1 || ret=1 $PERL ../digcomp.pl dig.out.root dig.out.f1 || ret=1 if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` echo "I:checking that a forward only zone no forwarders fails" ret=0 -$DIG txt.example2. txt @$root -p 5300 > dig.out.root || ret=1 -$DIG txt.example2. txt @$f1 -p 5300 > dig.out.f1 || ret=1 +$DIG +noadd +noauth txt.example2. txt @$root -p 5300 > dig.out.root || ret=1 +$DIG +noadd +noauth txt.example2. txt @$f1 -p 5300 > dig.out.f1 || ret=1 $PERL ../digcomp.pl dig.out.root dig.out.f1 || ret=1 if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` echo "I:checking that global forwarders work" ret=0 -$DIG txt.example4. txt @$hidden -p 5300 > dig.out.hidden || ret=1 -$DIG txt.example4. txt @$f1 -p 5300 > dig.out.f1 || ret=1 +$DIG +noadd +noauth txt.example4. txt @$hidden -p 5300 > dig.out.hidden || ret=1 +$DIG +noadd +noauth txt.example4. txt @$f1 -p 5300 > dig.out.f1 || ret=1 $PERL ../digcomp.pl dig.out.hidden dig.out.f1 || ret=1 if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` echo "I:checking that a forward zone works" ret=0 -$DIG txt.example1. txt @$hidden -p 5300 > dig.out.hidden || ret=1 -$DIG txt.example1. txt @$f2 -p 5300 > dig.out.f2 || ret=1 +$DIG +noadd +noauth txt.example1. txt @$hidden -p 5300 > dig.out.hidden || ret=1 +$DIG +noadd +noauth txt.example1. txt @$f2 -p 5300 > dig.out.f2 || ret=1 $PERL ../digcomp.pl dig.out.hidden dig.out.f2 || ret=1 if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` echo "I:checking that forwarding doesn't spontaneously happen" ret=0 -$DIG txt.example2. txt @$root -p 5300 > dig.out.root || ret=1 -$DIG txt.example2. txt @$f2 -p 5300 > dig.out.f2 || ret=1 +$DIG +noadd +noauth txt.example2. txt @$root -p 5300 > dig.out.root || ret=1 +$DIG +noadd +noauth txt.example2. txt @$f2 -p 5300 > dig.out.f2 || ret=1 $PERL ../digcomp.pl dig.out.root dig.out.f2 || ret=1 if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` echo "I:checking that a forward zone with no specified policy works" ret=0 -$DIG txt.example3. txt @$hidden -p 5300 > dig.out.hidden || ret=1 -$DIG txt.example3. txt @$f2 -p 5300 > dig.out.f2 || ret=1 +$DIG +noadd +noauth txt.example3. txt @$hidden -p 5300 > dig.out.hidden || ret=1 +$DIG +noadd +noauth txt.example3. txt @$f2 -p 5300 > dig.out.f2 || ret=1 $PERL ../digcomp.pl dig.out.hidden dig.out.f2 || ret=1 if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` diff --git a/bin/tests/system/lwresd/lwtest.c b/bin/tests/system/lwresd/lwtest.c index 0d415eab09..5f05c43712 100644 --- a/bin/tests/system/lwresd/lwtest.c +++ b/bin/tests/system/lwresd/lwtest.c @@ -764,6 +764,7 @@ main(void) { test_getrrsetbyname("a.example1.", 1, 1, 1, 0, 1); test_getrrsetbyname("e.example1.", 1, 1, 1, 1, 1); test_getrrsetbyname("e.example1.", 1, 255, 1, 1, 0); + test_getrrsetbyname("e.example1.", 1, 2, 1, 1, 1); test_getrrsetbyname("e.example1.", 1, 46, 2, 0, 1); test_getrrsetbyname("", 1, 1, 0, 0, 0); diff --git a/bin/tests/system/org.isc.bind.system b/bin/tests/system/org.isc.bind.system index 9d66b63295..47e6aec1ba 100644 --- a/bin/tests/system/org.isc.bind.system +++ b/bin/tests/system/org.isc.bind.system @@ -16,7 +16,7 @@ # $Id: org.isc.bind.system,v 1.1 2010/08/25 04:51:51 marka Exp $ -for ns in 1 2 3 4 5 6 7 +for ns in 1 2 3 4 5 6 7 8 do /sbin/ifconfig lo0 10.53.0.$ns alias /sbin/ifconfig lo0 inet6 fd92:7065:b8e:ffff::$ns alias diff --git a/bin/tests/system/start.pl b/bin/tests/system/start.pl index 7b9cf1d159..319b31f66d 100644 --- a/bin/tests/system/start.pl +++ b/bin/tests/system/start.pl @@ -155,12 +155,21 @@ sub start_server { close FH; $command .= "$options"; } else { + $command .= "-D $server "; $command .= "-m record,size,mctx "; $command .= "-T clienttest "; $command .= "-T nosoa " if (-e "$testdir/$server/named.nosoa"); $command .= "-T noaa " if (-e "$testdir/$server/named.noaa"); + $command .= "-T noedns " + if (-e "$testdir/$server/named.noedns"); + $command .= "-T dropedns " + if (-e "$testdir/$server/named.dropedns"); + $command .= "-T maxudp512 " + if (-e "$testdir/$server/named.maxudp512"); + $command .= "-T maxudp1460 " + if (-e "$testdir/$server/named.maxudp1460"); $command .= "-c named.conf -d 99 -g -U 4 "; } if ($restart) { diff --git a/bin/tests/system/staticstub/knowngood.dig.out.rec b/bin/tests/system/staticstub/knowngood.dig.out.rec index 15f84561a8..e854082c56 100644 --- a/bin/tests/system/staticstub/knowngood.dig.out.rec +++ b/bin/tests/system/staticstub/knowngood.dig.out.rec @@ -11,9 +11,6 @@ ;; ANSWER SECTION: data.example. 5M IN TXT "some" "test" "data" -;; AUTHORITY SECTION: -example. 5M IN NS ns4.example. - ;; Total query time: 8 msec ;; FROM: draco to SERVER: 10.53.0.3 ;; WHEN: Wed Jun 21 10:58:54 2000 diff --git a/bin/tests/system/staticstub/tests.sh b/bin/tests/system/staticstub/tests.sh index 0d05558740..33d4d95cba 100755 --- a/bin/tests/system/staticstub/tests.sh +++ b/bin/tests/system/staticstub/tests.sh @@ -80,7 +80,7 @@ status=`expr $status + $ret` n=`expr $n + 1` echo "I:look for static-stub zone data with recursion (should be found) ($n)" ret=0 -$DIG +tcp data.example. @10.53.0.2 txt -p 5300 > dig.out.ns2.test$n || ret=1 +$DIG +tcp +noauth data.example. @10.53.0.2 txt -p 5300 > dig.out.ns2.test$n || ret=1 $PERL ../digcomp.pl knowngood.dig.out.rec dig.out.ns2.test$n || ret=1 if [ $ret != 0 ]; then echo "I:failed"; fi status=`expr $status + $ret` diff --git a/bin/tests/system/stub/knowngood.dig.out.rec b/bin/tests/system/stub/knowngood.dig.out.rec index 9f2e4ee8e8..8ea19680d7 100644 --- a/bin/tests/system/stub/knowngood.dig.out.rec +++ b/bin/tests/system/stub/knowngood.dig.out.rec @@ -11,12 +11,6 @@ ;; ANSWER SECTION: data.child.example. 5M IN TXT "some" "test" "data" -;; AUTHORITY SECTION: -child.example. 5M IN NS ns2.child.example. - -;; ADDITIONAL SECTION: -ns2.child.example. 5M IN A 10.53.0.2 - ;; Total query time: 8 msec ;; FROM: draco to SERVER: 10.53.0.3 ;; WHEN: Wed Jun 21 10:58:54 2000 diff --git a/bin/tests/system/stub/tests.sh b/bin/tests/system/stub/tests.sh index 72d81a5ea8..c379aba42b 100644 --- a/bin/tests/system/stub/tests.sh +++ b/bin/tests/system/stub/tests.sh @@ -53,7 +53,7 @@ $PERL ../digcomp.pl knowngood.dig.out.norec dig.out.ns3 || ret=1 echo "I:look for stub zone data with recursion (should be found) (pass=$pass)" ret=0 -$DIG +tcp data.child.example. @10.53.0.3 txt -p 5300 > dig.out.ns3 || ret=1 +$DIG +tcp +noauth +noadd data.child.example. @10.53.0.3 txt -p 5300 > dig.out.ns3 || ret=1 $PERL ../digcomp.pl knowngood.dig.out.rec dig.out.ns3 || ret=1 [ $ret = 0 ] || { status=1; echo "I:failed"; } diff --git a/lib/dns/adb.c b/lib/dns/adb.c index c5c14e5de9..77e637324f 100644 --- a/lib/dns/adb.c +++ b/lib/dns/adb.c @@ -242,6 +242,18 @@ struct dns_adbentry { unsigned int flags; unsigned int srtt; + isc_uint16_t udpsize; + unsigned char plain; + unsigned char plainto; + unsigned char edns; + unsigned char to4096; /* Our max. */ + /* + * Allow for encapsulated IPv4/IPv6 UDP packet over ethernet. + * Ethernet 1500 - IP(20) - IP6(40) - UDP(8) = 1432. + */ + unsigned char to1432; /* Ethernet */ + unsigned char to1232; /* IPv6 nofrag */ + unsigned char to512; /* plain DNS */ isc_sockaddr_t sockaddr; isc_stdtime_t expires; @@ -255,7 +267,6 @@ struct dns_adbentry { ISC_LIST(dns_adblameinfo_t) lameinfo; ISC_LINK(dns_adbentry_t) plink; - }; /* @@ -952,12 +963,14 @@ import_rdataset(dns_adbname_t *adbname, dns_rdataset_t *rdataset, DP(NCACHE_LEVEL, "expire_v4 set to MIN(%u,%u) import_rdataset", adbname->expire_v4, now + rdataset->ttl); adbname->expire_v4 = ISC_MIN(adbname->expire_v4, - now + rdataset->ttl); + ISC_MIN(now + ADB_ENTRY_WINDOW, + now + rdataset->ttl)); } else { DP(NCACHE_LEVEL, "expire_v6 set to MIN(%u,%u) import_rdataset", adbname->expire_v6, now + rdataset->ttl); adbname->expire_v6 = ISC_MIN(adbname->expire_v6, - now + rdataset->ttl); + ISC_MIN(now + ADB_ENTRY_WINDOW, + now + rdataset->ttl)); } if (new_addresses_added) { @@ -1781,6 +1794,14 @@ new_adbentry(dns_adb_t *adb) { e->lock_bucket = DNS_ADB_INVALIDBUCKET; e->refcnt = 0; e->flags = 0; + e->udpsize = 0; + e->edns = 0; + e->plain = 0; + e->plainto = 0; + e->to4096 = 0; + e->to1432 = 0; + e->to1232 = 0; + e->to512 = 0; isc_random_get(&r); e->srtt = (r & 0x1f) + 1; e->expires = 0; @@ -2043,6 +2064,7 @@ find_entry_and_lock(dns_adb_t *adb, isc_sockaddr_t *addr, int *bucketp, entry_next = ISC_LIST_NEXT(entry, plink); (void)check_expire_entry(adb, &entry, now); if (entry != NULL && + (entry->expires == 0 || entry->expires > now) && isc_sockaddr_equal(addr, &entry->sockaddr)) { ISC_LIST_UNLINK(adb->entries[bucket], entry, plink); ISC_LIST_PREPEND(adb->entries[bucket], entry, plink); @@ -2815,6 +2837,7 @@ dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, unsigned int wanted_addresses; unsigned int wanted_fetches; unsigned int query_pending; + char namebuf[DNS_NAME_FORMATSIZE]; REQUIRE(DNS_ADB_VALID(adb)); if (task != NULL) { @@ -2875,6 +2898,11 @@ dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, REQUIRE(task != NULL); } + if (isc_log_wouldlog(dns_lctx, DEF_LEVEL)) + dns_name_format(name, namebuf, sizeof(namebuf)); + else + namebuf[0] = 0; + /* * Try to see if we know anything about this name at all. */ @@ -2932,8 +2960,8 @@ dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, * Yes, it is. */ DP(DEF_LEVEL, - "dns_adb_createfind: name %p is an alias (cached)", - adbname); + "dns_adb_createfind: name %s (%p) is an alias (cached)", + namebuf, adbname); alias = ISC_TRUE; goto post_copy; } @@ -2948,8 +2976,8 @@ dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, result = dbfind_name(adbname, now, dns_rdatatype_a); if (result == ISC_R_SUCCESS) { DP(DEF_LEVEL, - "dns_adb_createfind: found A for name %p in db", - adbname); + "dns_adb_createfind: found A for name %s (%p) in db", + namebuf, adbname); goto v6; } @@ -2958,8 +2986,8 @@ dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, */ if (result == DNS_R_ALIAS) { DP(DEF_LEVEL, - "dns_adb_createfind: name %p is an alias", - adbname); + "dns_adb_createfind: name %s (%p) is an alias", + namebuf, adbname); alias = ISC_TRUE; goto post_copy; } @@ -2988,8 +3016,8 @@ dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, result = dbfind_name(adbname, now, dns_rdatatype_aaaa); if (result == ISC_R_SUCCESS) { DP(DEF_LEVEL, - "dns_adb_createfind: found AAAA for name %p", - adbname); + "dns_adb_createfind: found AAAA for name %s (%p)", + namebuf, adbname); goto fetch; } @@ -2998,8 +3026,8 @@ dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, */ if (result == DNS_R_ALIAS) { DP(DEF_LEVEL, - "dns_adb_createfind: name %p is an alias", - adbname); + "dns_adb_createfind: name %s (%p) is an alias", + namebuf, adbname); alias = ISC_TRUE; goto post_copy; } @@ -3039,9 +3067,9 @@ dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, if (WANT_INET(wanted_fetches) && fetch_name(adbname, start_at_zone, dns_rdatatype_a) == ISC_R_SUCCESS) { - DP(DEF_LEVEL, - "dns_adb_createfind: started A fetch for name %p", - adbname); + DP(DEF_LEVEL, "dns_adb_createfind: " + "started A fetch for name %s (%p)", + namebuf, adbname); } /* @@ -3050,10 +3078,9 @@ dns_adb_createfind(dns_adb_t *adb, isc_task_t *task, isc_taskaction_t action, if (WANT_INET6(wanted_fetches) && fetch_name(adbname, start_at_zone, dns_rdatatype_aaaa) == ISC_R_SUCCESS) { - DP(DEF_LEVEL, - "dns_adb_createfind: " - "started AAAA fetch for name %p", - adbname); + DP(DEF_LEVEL, "dns_adb_createfind: " + "started AAAA fetch for name %s (%p)", + namebuf, adbname); } } @@ -3389,8 +3416,12 @@ dump_entry(FILE *f, dns_adbentry_t *entry, isc_boolean_t debug, if (debug) fprintf(f, ";\t%p: refcnt %u\n", entry, entry->refcnt); - fprintf(f, ";\t%s [srtt %u] [flags %08x]", - addrbuf, entry->srtt, entry->flags); + fprintf(f, ";\t%s [srtt %u] [flags %08x] [edns %u/%u/%u/%u/%u] " + "[plain %u/%u]", addrbuf, entry->srtt, entry->flags, + entry->edns, entry->to4096, entry->to1432, entry->to1232, + entry->to512, entry->plain, entry->plainto); + if (entry->udpsize != 0U) + fprintf(f, " [udpsize %u]", entry->udpsize); if (entry->expires != 0) fprintf(f, " [ttl %d]", entry->expires - now); fprintf(f, "\n"); @@ -3949,7 +3980,7 @@ dns_adb_adjustsrtt(dns_adb_t *adb, dns_adbaddrinfo_t *addr, unsigned int rtt, unsigned int factor) { int bucket; - unsigned int new_srtt; + isc_uint64_t new_srtt; isc_stdtime_t now; REQUIRE(DNS_ADB_VALID(adb)); @@ -3959,9 +3990,13 @@ dns_adb_adjustsrtt(dns_adb_t *adb, dns_adbaddrinfo_t *addr, bucket = addr->entry->lock_bucket; LOCK(&adb->entrylocks[bucket]); - if (factor == DNS_ADB_RTTADJAGE) - new_srtt = addr->entry->srtt * 98 / 100; - else + + if (factor == DNS_ADB_RTTADJAGE) { + new_srtt = addr->entry->srtt; + new_srtt <<= 9; + new_srtt -= addr->entry->srtt; + new_srtt >>= 9; + } else new_srtt = (addr->entry->srtt / 10 * factor) + (rtt / 10 * (10 - factor)); @@ -4004,6 +4039,206 @@ dns_adb_changeflags(dns_adb_t *adb, dns_adbaddrinfo_t *addr, UNLOCK(&adb->entrylocks[bucket]); } +#define EDNSTOS 3U +isc_boolean_t +dns_adb_noedns(dns_adb_t *adb, dns_adbaddrinfo_t *addr) { + int bucket; + isc_boolean_t noedns = ISC_FALSE; + + REQUIRE(DNS_ADB_VALID(adb)); + REQUIRE(DNS_ADBADDRINFO_VALID(addr)); + + bucket = addr->entry->lock_bucket; + LOCK(&adb->entrylocks[bucket]); + if (addr->entry->edns == 0U && + (addr->entry->plain > EDNSTOS || addr->entry->to4096 > EDNSTOS)) { + if (((addr->entry->plain + addr->entry->to4096) & 0x3f) != 0) { + noedns = ISC_TRUE; + } else { + /* + * Increment plain so we don't get stuck. + */ + addr->entry->plain++; + if (addr->entry->plain == 0xff) { + addr->entry->edns >>= 1; + addr->entry->to4096 >>= 1; + addr->entry->to1432 >>= 1; + addr->entry->to1232 >>= 1; + addr->entry->to512 >>= 1; + addr->entry->plain >>= 1; + addr->entry->plainto >>= 1; + } + } + } + UNLOCK(&adb->entrylocks[bucket]); + return (noedns); +} + +void +dns_adb_plainresponse(dns_adb_t *adb, dns_adbaddrinfo_t *addr) { + int bucket; + + REQUIRE(DNS_ADB_VALID(adb)); + REQUIRE(DNS_ADBADDRINFO_VALID(addr)); + + bucket = addr->entry->lock_bucket; + LOCK(&adb->entrylocks[bucket]); + addr->entry->plain++; + if (addr->entry->plain == 0xff) { + addr->entry->edns >>= 1; + addr->entry->to4096 >>= 1; + addr->entry->to1432 >>= 1; + addr->entry->to1232 >>= 1; + addr->entry->to512 >>= 1; + addr->entry->plain >>= 1; + addr->entry->plainto >>= 1; + } + UNLOCK(&adb->entrylocks[bucket]); +} + +void +dns_adb_timeout(dns_adb_t *adb, dns_adbaddrinfo_t *addr) { + int bucket; + + REQUIRE(DNS_ADB_VALID(adb)); + REQUIRE(DNS_ADBADDRINFO_VALID(addr)); + + bucket = addr->entry->lock_bucket; + LOCK(&adb->entrylocks[bucket]); + /* + * If we have not had a successful query then clear all + * edns timeout information. + */ + if (addr->entry->edns == 0 && addr->entry->plain == 0) { + addr->entry->to512 = 0; + addr->entry->to1232 = 0; + addr->entry->to1432 = 0; + addr->entry->to4096 = 0; + } else { + addr->entry->to512 >>= 1; + addr->entry->to1232 >>= 1; + addr->entry->to1432 >>= 1; + addr->entry->to4096 >>= 1; + } + addr->entry->plainto++; + if (addr->entry->plainto == 0xff) { + addr->entry->edns >>= 1; + addr->entry->plain >>= 1; + addr->entry->plainto >>= 1; + } + UNLOCK(&adb->entrylocks[bucket]); +} + +void +dns_adb_ednsto(dns_adb_t *adb, dns_adbaddrinfo_t *addr, unsigned int size) { + int bucket; + + REQUIRE(DNS_ADB_VALID(adb)); + REQUIRE(DNS_ADBADDRINFO_VALID(addr)); + + bucket = addr->entry->lock_bucket; + LOCK(&adb->entrylocks[bucket]); + + if (size <= 512U) { + if (addr->entry->to512 <= EDNSTOS) { + addr->entry->to512++; + addr->entry->to1232++; + addr->entry->to1432++; + addr->entry->to4096++; + } + } else if (size <= 1232U) { + if (addr->entry->to1232 <= EDNSTOS) { + addr->entry->to1232++; + addr->entry->to1432++; + addr->entry->to4096++; + } + } else if (size <= 1432U) { + if (addr->entry->to1432 <= EDNSTOS) { + addr->entry->to1432++; + addr->entry->to4096++; + } + } else { + if (addr->entry->to4096 <= EDNSTOS) + addr->entry->to4096++; + } + + if (addr->entry->to4096 == 0xff) { + addr->entry->edns >>= 1; + addr->entry->to4096 >>= 1; + addr->entry->to1432 >>= 1; + addr->entry->to1232 >>= 1; + addr->entry->to512 >>= 1; + addr->entry->plain >>= 1; + addr->entry->plainto >>= 1; + } + UNLOCK(&adb->entrylocks[bucket]); +} + +void +dns_adb_setudpsize(dns_adb_t *adb, dns_adbaddrinfo_t *addr, unsigned int size) { + int bucket; + + REQUIRE(DNS_ADB_VALID(adb)); + REQUIRE(DNS_ADBADDRINFO_VALID(addr)); + + bucket = addr->entry->lock_bucket; + LOCK(&adb->entrylocks[bucket]); + if (size < 512U) + size = 512U; + if (size > addr->entry->udpsize) + addr->entry->udpsize = size; + addr->entry->edns++; + if (addr->entry->edns == 0xff) { + addr->entry->edns >>= 1; + addr->entry->to4096 >>= 1; + addr->entry->to1432 >>= 1; + addr->entry->to1232 >>= 1; + addr->entry->to512 >>= 1; + addr->entry->plain >>= 1; + addr->entry->plainto >>= 1; + } + UNLOCK(&adb->entrylocks[bucket]); +} + +unsigned int +dns_adb_getudpsize(dns_adb_t *adb, dns_adbaddrinfo_t *addr) { + int bucket; + unsigned int size; + + REQUIRE(DNS_ADB_VALID(adb)); + REQUIRE(DNS_ADBADDRINFO_VALID(addr)); + + bucket = addr->entry->lock_bucket; + LOCK(&adb->entrylocks[bucket]); + size = addr->entry->udpsize; + UNLOCK(&adb->entrylocks[bucket]); + + return (size); +} + +unsigned int +dns_adb_probesize(dns_adb_t *adb, dns_adbaddrinfo_t *addr) { + int bucket; + unsigned int size; + + REQUIRE(DNS_ADB_VALID(adb)); + REQUIRE(DNS_ADBADDRINFO_VALID(addr)); + + bucket = addr->entry->lock_bucket; + LOCK(&adb->entrylocks[bucket]); + if (addr->entry->to1232 > EDNSTOS) + size = 512; + else if (addr->entry->to1432 > EDNSTOS) + size = 1232; + else if (addr->entry->to4096 > EDNSTOS) + size = 1432; + else + size = 4096; + UNLOCK(&adb->entrylocks[bucket]); + + return (size); +} + isc_result_t dns_adb_findaddrinfo(dns_adb_t *adb, isc_sockaddr_t *sa, dns_adbaddrinfo_t **addrp, isc_stdtime_t now) diff --git a/lib/dns/include/dns/adb.h b/lib/dns/include/dns/adb.h index 92a46d4ba2..27ad144311 100644 --- a/lib/dns/include/dns/adb.h +++ b/lib/dns/include/dns/adb.h @@ -567,6 +567,93 @@ dns_adb_changeflags(dns_adb_t *adb, dns_adbaddrinfo_t *addr, *\li addr be valid. */ +void +dns_adb_setudpsize(dns_adb_t *adb, dns_adbaddrinfo_t *addr, unsigned int size); +/*% + * Update seen UDP response size. The largest seen will be returned by + * dns_adb_getudpsize(). + * + * Requires: + * + *\li adb be valid. + * + *\li addr be valid. + */ + +unsigned int +dns_adb_getudpsize(dns_adb_t *adb, dns_adbaddrinfo_t *addr); +/*% + * Return the largest seen UDP response size. + * + * Requires: + * + *\li adb be valid. + * + *\li addr be valid. + */ + +unsigned int +dns_adb_probesize(dns_adb_t *adb, dns_adbaddrinfo_t *addr); +/*% + * Return suggested EDNS UDP size based on observed responses / failures. + * + * Requires: + * + *\li adb be valid. + * + *\li addr be valid. + */ + +void +dns_adb_plainresponse(dns_adb_t *adb, dns_adbaddrinfo_t *addr); +/*% + * Record a successful plain DNS response. + * + * Requires: + * + *\li adb be valid. + * + *\li addr be valid. + */ + +void +dns_adb_timeout(dns_adb_t *adb, dns_adbaddrinfo_t *addr); +/*% + * Record a plain DNS UDP query failed. + * + * Requires: + * + *\li adb be valid. + * + *\li addr be valid. + */ + +void +dns_adb_ednsto(dns_adb_t *adb, dns_adbaddrinfo_t *addr, unsigned int size); +/*% + * Record a failed EDNS UDP response and the advertised EDNS UDP buffer size + * used. + * + * Requires: + * + *\li adb be valid. + * + *\li addr be valid. + */ + +isc_boolean_t +dns_adb_noedns(dns_adb_t *adb, dns_adbaddrinfo_t *addr); +/*% + * Return whether EDNS should be disabled for this server. + * + * Requires: + * + *\li adb be valid. + * + *\li addr be valid. + */ + + isc_result_t dns_adb_findaddrinfo(dns_adb_t *adb, isc_sockaddr_t *sa, dns_adbaddrinfo_t **addrp, isc_stdtime_t now); diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index 58f9ef0e41..ec36115eb2 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -167,9 +167,16 @@ typedef struct query { unsigned int attributes; unsigned int sends; unsigned int connects; + unsigned int udpsize; unsigned char data[512]; } resquery_t; +struct tried { + isc_sockaddr_t addr; + unsigned int count; + ISC_LINK(struct tried) link; +}; + #define QUERY_MAGIC ISC_MAGIC('Q', '!', '!', '!') #define VALID_QUERY(query) ISC_MAGIC_VALID(query, QUERY_MAGIC) @@ -231,8 +238,8 @@ struct fetchctx { dns_forwarderlist_t forwarders; dns_fwdpolicy_t fwdpolicy; isc_sockaddrlist_t bad; - isc_sockaddrlist_t edns; - isc_sockaddrlist_t edns512; + ISC_LIST(struct tried) edns; + ISC_LIST(struct tried) edns512; isc_sockaddrlist_t bad_edns; dns_validator_t *validator; ISC_LIST(dns_validator_t) validators; @@ -452,6 +459,8 @@ struct dns_resolver { #define FCTX_ADDRINFO_MARK 0x0001 #define FCTX_ADDRINFO_FORWARDER 0x1000 #define FCTX_ADDRINFO_TRIED 0x2000 +#define FCTX_ADDRINFO_EDNSOK 0x4000 + #define UNMARKED(a) (((a)->flags & FCTX_ADDRINFO_MARK) \ == 0) #define ISFORWARDER(a) (((a)->flags & \ @@ -811,6 +820,10 @@ fctx_cancelquery(resquery_t **queryp, dns_dispatchevent_t **deventp, query->attributes |= RESQUERY_ATTR_CANCELED; + char addrbuf[ISC_SOCKADDR_FORMATSIZE]; + isc_sockaddr_format(&query->addrinfo->sockaddr, + addrbuf, sizeof(addrbuf)); + /* * Should we update the RTT? */ @@ -844,14 +857,45 @@ fctx_cancelquery(resquery_t **queryp, dns_dispatchevent_t **deventp, inc_stats(fctx->res, dns_resstatscounter_queryrtt5); } - } else { + } else { + isc_uint32_t value; + isc_uint32_t mask; /* * We don't have an RTT for this query. Maybe the * packet was lost, or maybe this server is very * slow. We don't know. Increase the RTT. */ INSIST(no_response); - rtt = query->addrinfo->srtt + 200000; + isc_random_get(&value); + if (query->addrinfo->srtt > 800000) + mask = 0x3fff; + else if (query->addrinfo->srtt > 400000) + mask = 0x7fff; + else if (query->addrinfo->srtt > 200000) + mask = 0xffff; + else if (query->addrinfo->srtt > 100000) + mask = 0x1ffff; + else if (query->addrinfo->srtt > 50000) + mask = 0x3ffff; + else if (query->addrinfo->srtt > 25000) + mask = 0x7ffff; + else + mask = 0xfffff; + if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0) + dns_adb_ednsto(fctx->adb, query->addrinfo, + query->udpsize); + else + dns_adb_timeout(fctx->adb, query->addrinfo); + + /* + * Don't adjust timeout on EDNS queries unless we have + * seen a EDNS response. + */ + if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0 && + (query->addrinfo->flags & FCTX_ADDRINFO_EDNSOK) == 0) { + mask >>= 2; + } + rtt = query->addrinfo->srtt + (value & mask); if (rtt > MAX_SINGLE_QUERY_TIMEOUT_US) rtt = MAX_SINGLE_QUERY_TIMEOUT_US; /* @@ -1618,62 +1662,70 @@ add_bad_edns(fetchctx_t *fctx, isc_sockaddr_t *address) { ISC_LIST_INITANDAPPEND(fctx->bad_edns, sa, link); } -static isc_boolean_t +static struct tried * triededns(fetchctx_t *fctx, isc_sockaddr_t *address) { - isc_sockaddr_t *sa; + struct tried *tried; - for (sa = ISC_LIST_HEAD(fctx->edns); - sa != NULL; - sa = ISC_LIST_NEXT(sa, link)) { - if (isc_sockaddr_equal(sa, address)) - return (ISC_TRUE); + for (tried = ISC_LIST_HEAD(fctx->edns); + tried != NULL; + tried = ISC_LIST_NEXT(tried, link)) { + if (isc_sockaddr_equal(&tried->addr, address)) + return (tried); } - return (ISC_FALSE); + return (NULL); } static void add_triededns(fetchctx_t *fctx, isc_sockaddr_t *address) { - isc_sockaddr_t *sa; + struct tried *tried; - if (triededns(fctx, address)) + tried = triededns(fctx, address); + if (tried != NULL) { + tried->count++; return; - - sa = isc_mem_get(fctx->mctx, sizeof(*sa)); - if (sa == NULL) - return; - - *sa = *address; - ISC_LIST_INITANDAPPEND(fctx->edns, sa, link); -} - -static isc_boolean_t -triededns512(fetchctx_t *fctx, isc_sockaddr_t *address) { - isc_sockaddr_t *sa; - - for (sa = ISC_LIST_HEAD(fctx->edns512); - sa != NULL; - sa = ISC_LIST_NEXT(sa, link)) { - if (isc_sockaddr_equal(sa, address)) - return (ISC_TRUE); } - return (ISC_FALSE); + tried = isc_mem_get(fctx->mctx, sizeof(*tried)); + if (tried == NULL) + return; + + tried->addr = *address; + tried->count = 1; + ISC_LIST_INITANDAPPEND(fctx->edns, tried, link); +} + +static struct tried * +triededns512(fetchctx_t *fctx, isc_sockaddr_t *address) { + struct tried *tried; + + for (tried = ISC_LIST_HEAD(fctx->edns512); + tried != NULL; + tried = ISC_LIST_NEXT(tried, link)) { + if (isc_sockaddr_equal(&tried->addr, address)) + return (tried); + } + + return (NULL); } static void add_triededns512(fetchctx_t *fctx, isc_sockaddr_t *address) { - isc_sockaddr_t *sa; + struct tried *tried; - if (triededns512(fctx, address)) + tried = triededns512(fctx, address); + if (tried != NULL) { + tried->count++; + return; + } + + tried = isc_mem_get(fctx->mctx, sizeof(*tried)); + if (tried == NULL) return; - sa = isc_mem_get(fctx->mctx, sizeof(*sa)); - if (sa == NULL) - return; - - *sa = *address; - ISC_LIST_INITANDAPPEND(fctx->edns512, sa, link); + tried->addr = *address; + tried->count = 1; + ISC_LIST_INITANDAPPEND(fctx->edns512, tried, link); } static isc_result_t @@ -1699,6 +1751,11 @@ resquery_send(resquery_t *query) { isc_boolean_t connecting = ISC_FALSE; dns_ednsopt_t ednsopts[EDNSOPTS]; unsigned ednsopt = 0; + isc_uint16_t hint = 0, udpsize = 0; /* No EDNS */ + + char addrbuf[ISC_SOCKADDR_FORMATSIZE]; + isc_sockaddr_format(&query->addrinfo->sockaddr, + addrbuf, sizeof(addrbuf)); fctx = query->fctx; QTRACE("send"); @@ -1766,11 +1823,12 @@ resquery_send(resquery_t *query) { /* * Set CD if the client says don't validate or the question is - * under a secure entry point. + * under a secure entry point and it is not a recursive query. */ if ((query->options & DNS_FETCHOPT_NOVALIDATE) != 0) { fctx->qmessage->flags |= DNS_MESSAGEFLAG_CD; - } else if (res->view->enablevalidation) { + } else if (res->view->enablevalidation && + (fctx->qmessage->flags & DNS_MESSAGEFLAG_RD) != 0) { result = dns_view_issecuredomain(res->view, &fctx->name, &secure_domain); if (result != ISC_R_SUCCESS) @@ -1827,35 +1885,37 @@ resquery_send(resquery_t *query) { if ((query->addrinfo->flags & DNS_FETCHOPT_NOEDNS0) != 0) query->options |= DNS_FETCHOPT_NOEDNS0; - /* - * Handle timeouts by reducing the UDP response size to 512 bytes - * then if that doesn't work disabling EDNS (includes DO) and CD. - * - * These timeout can be due to: - * * broken nameservers that don't respond to EDNS queries. - * * broken/misconfigured firewalls and NAT implementations - * that don't handle IP fragmentation. - * * broken/misconfigured firewalls that don't handle responses - * greater than 512 bytes. - * * broken/misconfigured firewalls that don't handle EDNS, DO - * or CD. - * * packet loss / link outage. - */ - if (fctx->timeout) { - if ((triededns512(fctx, &query->addrinfo->sockaddr) || - fctx->timeouts >= (MAX_EDNS0_TIMEOUTS * 2)) && - (query->options & DNS_FETCHOPT_NOEDNS0) == 0) { + /* See if response history indicates that EDNS is not supported. */ + if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0 && + dns_adb_noedns(fctx->adb, query->addrinfo)) + query->options |= DNS_FETCHOPT_NOEDNS0; + + + if (fctx->timeout && (query->options & DNS_FETCHOPT_NOEDNS0) == 0) { + isc_sockaddr_t *sockaddr = &query->addrinfo->sockaddr; + struct tried *tried; + + if (fctx->timeouts > (MAX_EDNS0_TIMEOUTS * 2) && + (query->addrinfo->flags & FCTX_ADDRINFO_EDNSOK) == 0) { query->options |= DNS_FETCHOPT_NOEDNS0; fctx->reason = "disabling EDNS"; - } else if ((triededns(fctx, &query->addrinfo->sockaddr) || - fctx->timeouts >= MAX_EDNS0_TIMEOUTS) && - (query->options & DNS_FETCHOPT_NOEDNS0) == 0) { - query->options |= DNS_FETCHOPT_EDNS512; - fctx->reason = "reducing the advertised EDNS UDP " - "packet size to 512 octets"; + } else if ((tried = triededns512(fctx, sockaddr)) != NULL && + tried->count >= 2U && + (query->addrinfo->flags & FCTX_ADDRINFO_EDNSOK) == 0) { + query->options |= DNS_FETCHOPT_NOEDNS0; + fctx->reason = "disabling EDNS"; + } else if ((tried = triededns(fctx, sockaddr)) != NULL) { + if (tried->count == 1U) { + hint = dns_adb_getudpsize(fctx->adb, + query->addrinfo); + } else if (tried->count >= 2U) { + query->options |= DNS_FETCHOPT_EDNS512; + fctx->reason = "reducing the advertised EDNS " + "UDP packet size to 512 octets"; + } } - fctx->timeout = ISC_FALSE; } + fctx->timeout = ISC_FALSE; /* * Use EDNS0, unless the caller doesn't want it, or we know that @@ -1864,19 +1924,46 @@ resquery_send(resquery_t *query) { if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0) { if ((query->addrinfo->flags & DNS_FETCHOPT_NOEDNS0) == 0) { unsigned int version = 0; /* Default version. */ - unsigned int flags; - isc_uint16_t udpsize = res->udpsize; + unsigned int flags = query->addrinfo->flags; isc_boolean_t reqnsid = res->view->requestnsid; - flags = query->addrinfo->flags; + if ((flags & FCTX_ADDRINFO_EDNSOK) != 0 && + (query->options & DNS_FETCHOPT_EDNS512) == 0) { + udpsize = dns_adb_probesize(fctx->adb, + query->addrinfo); + if (udpsize > res->udpsize) + udpsize = res->udpsize; + } + + if (peer != NULL) + (void)dns_peer_getudpsize(peer, &udpsize); + + if (udpsize == 0U && res->udpsize == 512U) + udpsize = 512; + + /* + * Was the size forced to 512 in the configuration? + */ + if (udpsize == 512U) + query->options |= DNS_FETCHOPT_EDNS512; + + /* + * We have talked to this server before. + */ + if (hint != 0U) + udpsize = hint; + + /* + * We know nothing about the peer's capabilities + * so start with minimal EDNS UDP size. + */ + if (udpsize == 0U) + udpsize = 512; + if ((flags & DNS_FETCHOPT_EDNSVERSIONSET) != 0) { version = flags & DNS_FETCHOPT_EDNSVERSIONMASK; version >>= DNS_FETCHOPT_EDNSVERSIONSHIFT; } - if ((query->options & DNS_FETCHOPT_EDNS512) != 0) - udpsize = 512; - else if (peer != NULL) - (void)dns_peer_getudpsize(peer, &udpsize); /* request NSID for current view or peer? */ if (peer != NULL) @@ -1899,6 +1986,7 @@ resquery_send(resquery_t *query) { * bit. */ query->options |= DNS_FETCHOPT_NOEDNS0; + udpsize = 0; } } else { /* @@ -1910,6 +1998,11 @@ resquery_send(resquery_t *query) { } } + /* + * Record the UDP EDNS size choosen. + */ + query->udpsize = udpsize; + /* * If we need EDNS0 to do this query and aren't using it, we lose. */ @@ -1918,10 +2011,10 @@ resquery_send(resquery_t *query) { goto cleanup_message; } - if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0) + if (udpsize > 512U) add_triededns(fctx, &query->addrinfo->sockaddr); - if ((query->options & DNS_FETCHOPT_EDNS512) != 0) + if (udpsize == 512U) add_triededns512(fctx, &query->addrinfo->sockaddr); /* @@ -2612,7 +2705,7 @@ fctx_getaddresses(fetchctx_t *fctx, isc_boolean_t badcache) { * Don't pound on remote servers. (Failsafe!) */ fctx->restarts++; - if (fctx->restarts > 10) { + if (fctx->restarts > 100) { FCTXTRACE("too many restarts"); return (DNS_R_SERVFAIL); } @@ -3142,6 +3235,7 @@ fctx_unlink(fetchctx_t *fctx) { static void fctx_destroy(fetchctx_t *fctx) { isc_sockaddr_t *sa, *next_sa; + struct tried *tried; REQUIRE(VALID_FCTX(fctx)); REQUIRE(fctx->state == fetchstate_done || @@ -3168,20 +3262,18 @@ fctx_destroy(fetchctx_t *fctx) { isc_mem_put(fctx->mctx, sa, sizeof(*sa)); } - for (sa = ISC_LIST_HEAD(fctx->edns); - sa != NULL; - sa = next_sa) { - next_sa = ISC_LIST_NEXT(sa, link); - ISC_LIST_UNLINK(fctx->edns, sa, link); - isc_mem_put(fctx->mctx, sa, sizeof(*sa)); + for (tried = ISC_LIST_HEAD(fctx->edns); + tried != NULL; + tried = ISC_LIST_HEAD(fctx->edns)) { + ISC_LIST_UNLINK(fctx->edns, tried, link); + isc_mem_put(fctx->mctx, tried, sizeof(*tried)); } - for (sa = ISC_LIST_HEAD(fctx->edns512); - sa != NULL; - sa = next_sa) { - next_sa = ISC_LIST_NEXT(sa, link); - ISC_LIST_UNLINK(fctx->edns512, sa, link); - isc_mem_put(fctx->mctx, sa, sizeof(*sa)); + for (tried = ISC_LIST_HEAD(fctx->edns512); + tried != NULL; + tried = ISC_LIST_HEAD(fctx->edns512)) { + ISC_LIST_UNLINK(fctx->edns512, tried, link); + isc_mem_put(fctx->mctx, tried, sizeof(*tried)); } for (sa = ISC_LIST_HEAD(fctx->bad_edns); @@ -6907,6 +6999,13 @@ resquery_response(isc_task_t *task, isc_event_t *event) { goto done; } + if ((options & DNS_FETCHOPT_TCP) == 0) { + if ((options & DNS_FETCHOPT_NOEDNS0) == 0) + dns_adb_setudpsize(fctx->adb, query->addrinfo, + isc_buffer_usedlength(&devent->buffer)); + else + dns_adb_plainresponse(fctx->adb, query->addrinfo); + } result = dns_message_parse(message, &devent->buffer, 0); if (result != ISC_R_SUCCESS) { switch (result) { @@ -7019,12 +7118,66 @@ resquery_response(isc_task_t *task, isc_event_t *event) { message->rcode == dns_rcode_refused || message->rcode == dns_rcode_yxdomain) && bad_edns(fctx, &query->addrinfo->sockaddr)) { - char addrbuf[ISC_SOCKADDR_FORMATSIZE]; +/* + * XXXMPA We need to drop/remove the logging here when we have more + * experience. + */ + char buf[4096], addrbuf[ISC_SOCKADDR_FORMATSIZE]; isc_sockaddr_format(&query->addrinfo->sockaddr, addrbuf, sizeof(addrbuf)); + snprintf(buf, sizeof(buf), "received packet from %s " + "(bad edns):\n", addrbuf); + dns_message_logpacket(message, buf, + DNS_LOGCATEGORY_RESOLVER, DNS_LOGMODULE_RESOLVER, + ISC_LOG_NOTICE, fctx->res->mctx); dns_adb_changeflags(fctx->adb, query->addrinfo, DNS_FETCHOPT_NOEDNS0, DNS_FETCHOPT_NOEDNS0); + } else if (opt == NULL && (message->flags & DNS_MESSAGEFLAG_TC) == 0 && + (message->rcode == dns_rcode_noerror || + message->rcode == dns_rcode_nxdomain) && + (query->options & DNS_FETCHOPT_NOEDNS0) == 0) { + /* + * Old versions of named incorrectly drop the OPT record + * when there is a signed, truncated response so check that + * TC is not set. + */ +/* + * XXXMPA We need to drop/remove the logging here when we have more + * experience. + */ + char buf[4096], addrbuf[ISC_SOCKADDR_FORMATSIZE]; + /* + * We didn't get a OPT record in response to a EDNS query. + * Record that the server is not talking EDNS. While this + * should be safe to do for any rcode we limit it to NOERROR + * and NXDOMAIN. + */ + isc_sockaddr_format(&query->addrinfo->sockaddr, addrbuf, + sizeof(addrbuf)); + snprintf(buf, sizeof(buf), "received packet from %s (no opt):\n", + addrbuf); + dns_message_logpacket(message, buf, + DNS_LOGCATEGORY_RESOLVER, DNS_LOGMODULE_RESOLVER, + ISC_LOG_NOTICE, fctx->res->mctx); + dns_adb_changeflags(fctx->adb, query->addrinfo, + DNS_FETCHOPT_NOEDNS0, + DNS_FETCHOPT_NOEDNS0); + } + + /* + * If we get a non error response to a EDNS query record the fact + * so we won't fallback to plain DNS in the future for this server. + */ + if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0 && + (query->addrinfo->flags & FCTX_ADDRINFO_EDNSOK) == 0 && + (message->rcode == dns_rcode_noerror || + message->rcode == dns_rcode_nxdomain || + message->rcode == dns_rcode_refused || + message->rcode == dns_rcode_yxdomain)) { + dns_adb_changeflags(fctx->adb, query->addrinfo, + FCTX_ADDRINFO_EDNSOK, + FCTX_ADDRINFO_EDNSOK); } /* @@ -7038,6 +7191,10 @@ resquery_response(isc_task_t *task, isc_event_t *event) { if ((options & DNS_FETCHOPT_TCP) != 0) { broken_server = DNS_R_TRUNCATEDTCP; keep_trying = ISC_TRUE; + } else if ((query->options & DNS_FETCHOPT_NOEDNS0) == 0 && + (query->options & DNS_FETCHOPT_EDNS512) == 0 && + !triededns(fctx, &query->addrinfo->sockaddr)) { + resend = ISC_TRUE; } else { options |= DNS_FETCHOPT_TCP; resend = ISC_TRUE;