]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
3593. [func] Update EDNS processing to better track remote server
authorMark Andrews <marka@isc.org>
Wed, 12 Jun 2013 01:31:30 +0000 (11:31 +1000)
committerMark Andrews <marka@isc.org>
Wed, 12 Jun 2013 01:31:30 +0000 (11:31 +1000)
                        capabilities. [RT #30655]

23 files changed:
CHANGES
EDNS-9.10 [new file with mode: 0644]
bin/named/main.c
bin/named/named.docbook
bin/tests/bigtest/README [new file with mode: 0644]
bin/tests/bigtest/buildzones.sh [new file with mode: 0644]
bin/tests/bigtest/rndc.key [new file with mode: 0644]
bin/tests/bigtest/tests.sh [new file with mode: 0644]
bin/tests/bigtest/zones [new file with mode: 0644]
bin/tests/system/cacheclean/tests.sh
bin/tests/system/checknames/tests.sh
bin/tests/system/dnssec/tests.sh
bin/tests/system/forward/tests.sh
bin/tests/system/lwresd/lwtest.c
bin/tests/system/org.isc.bind.system
bin/tests/system/start.pl
bin/tests/system/staticstub/knowngood.dig.out.rec
bin/tests/system/staticstub/tests.sh
bin/tests/system/stub/knowngood.dig.out.rec
bin/tests/system/stub/tests.sh
lib/dns/adb.c
lib/dns/include/dns/adb.h
lib/dns/resolver.c

diff --git a/CHANGES b/CHANGES
index bd5de9e57da9140b63b3ed32c7e45e1c5baabc06..4a4b8cfe3408b816aca9f7be72f45978fdd8396b 100644 (file)
--- 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 (file)
index 0000000..a14ec50
--- /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.
index 39efe3f92f5c0fdb1552bff94f21485280028954..463703e2f7fb19570c986d4d28ef7d95f3b760d4 100644 (file)
@@ -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;
index 1f08e196e040ff363aeb95ab55deb8ddd44f0716..16242f6899ee49030ac58afb9722eea711f119d3 100644 (file)
@@ -62,6 +62,7 @@
       <arg><option>-6</option></arg>
       <arg><option>-c <replaceable class="parameter">config-file</replaceable></option></arg>
       <arg><option>-d <replaceable class="parameter">debug-level</replaceable></option></arg>
+      <arg><option>-D <replaceable class="parameter">string</replaceable></option></arg>
       <arg><option>-E <replaceable class="parameter">engine-name</replaceable></option></arg>
       <arg><option>-f</option></arg>
       <arg><option>-g</option></arg>
         </listitem>
       </varlistentry>
 
+      <varlistentry>
+        <term>-D <replaceable class="parameter">string</replaceable></term>
+        <listitem>
+          <para>
+           Specifies a string that is used to identify a instance of
+           <command>named</command> in a process listing.  The contents
+           of <replaceable class="parameter">string</replaceable> are
+           not examined.
+          </para>
+        </listitem>
+      </varlistentry>
+
       <varlistentry>
         <term>-E <replaceable class="parameter">engine-name</replaceable></term>
         <listitem>
diff --git a/bin/tests/bigtest/README b/bin/tests/bigtest/README
new file mode 100644 (file)
index 0000000..eed30a6
--- /dev/null
@@ -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 (file)
index 0000000..aabc3d8
--- /dev/null
@@ -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 (file)
index 0000000..f279e14
--- /dev/null
@@ -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 (file)
index 0000000..4bdda3f
--- /dev/null
@@ -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 (file)
index 0000000..0bdcdfe
--- /dev/null
@@ -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
index 9c7ed97e938c1067205bea7e0269c5aa4ee9c4f2..2f711b3d5e4ad85c521eecc9d652186f9c1dc57c 100644 (file)
@@ -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`
 
index 2a45159b7c375eb7828f7ec3f1d6dd0cd4c60aeb..566078a7b90c5f5ca6e93bde465c165ddadefdf8 100644 (file)
@@ -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
index 57b9d67f487fe62e5a690c02449c448f97cac1d5..839ff61666f4cb43a43c1fcca0a4d4c62f98f2b9 100644 (file)
@@ -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`
index e9f587eeaddb0501f7f59171d194f134f863d0cf..f4bef088d3e2636d22fa07b5515551992d469498 100644 (file)
@@ -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`
index 0d415eab09c6e31b2d658166a73b7e99a8f9c8f8..5f05c43712221bc7b1f6e72a75720b33a26010bd 100644 (file)
@@ -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);
 
index 9d66b63295f733181b4b0b841af555e81861543a..47e6aec1ba891a359aed9000ac27e835ac516e1f 100644 (file)
@@ -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
index 7b9cf1d159e2a1e5ee0ca2bb3adfa98734f75627..319b31f66db0944317bd9c8f2d3146d65e52932f 100644 (file)
@@ -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) {
index 15f84561a82c65789d52b36607fb3549b709aac1..e854082c56544dabfb58399e3e50eb72ca4a643c 100644 (file)
@@ -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
index 0d0555874036c0a448f9f5272b3427c732edaf04..33d4d95cba3a5b2b76d55a559ece02b960bb5a51 100755 (executable)
@@ -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`
index 9f2e4ee8e807110006a89b0ccbff046f395fa54f..8ea19680d737602ae173ae7eb6c87a4ee147f46a 100644 (file)
 ;; 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
index 72d81a5ea8efdfc5c6159e97974a8a6abaf6640d..c379aba42bacf661e800cdd275f5e9d3ab4cdc75 100644 (file)
@@ -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"; }
 
index c5c14e5de97680dacb84bb293042423c8bfab3e0..77e637324fb1f11a12ab8ab0698ff771373e9013 100644 (file)
@@ -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)
index 92a46d4ba2e6006a7ea6cf2a6e05814c9817eeff..27ad144311f3e733b8a8bff7334395614f3503ab 100644 (file)
@@ -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);
index 58f9ef0e415743c24950415f6608bbb19106c4d9..ec36115eb2c0f47da8d16d1924747ec362938d0d 100644 (file)
@@ -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)
+       tried = isc_mem_get(fctx->mctx, sizeof(*tried));
+       if (tried == NULL)
                return;
 
-       *sa = *address;
-       ISC_LIST_INITANDAPPEND(fctx->edns, sa, link);
+       tried->addr = *address;
+       tried->count = 1;
+       ISC_LIST_INITANDAPPEND(fctx->edns, tried, link);
 }
 
-static isc_boolean_t
+static struct tried *
 triededns512(fetchctx_t *fctx, isc_sockaddr_t *address) {
-       isc_sockaddr_t *sa;
+       struct tried *tried;
 
-       for (sa = ISC_LIST_HEAD(fctx->edns512);
-            sa != NULL;
-            sa = ISC_LIST_NEXT(sa, link)) {
-               if (isc_sockaddr_equal(sa, address))
-                       return (ISC_TRUE);
+       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 (ISC_FALSE);
+       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;
+       }
 
-       sa = isc_mem_get(fctx->mctx, sizeof(*sa));
-       if (sa == NULL)
+       tried = isc_mem_get(fctx->mctx, sizeof(*tried));
+       if (tried == 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 ((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 ((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 = 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,14 +7118,68 @@ 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);
+       }
+
        /*
         * Deal with truncated responses by retrying using TCP.
         */
@@ -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;