diff --git a/.reuse/dep5 b/.reuse/dep5 index 38b1937635..d90b537d9e 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -81,7 +81,10 @@ Files: **/*.after* bin/tests/system/unknown/large.out bin/tests/system/xfer/ans5/badkeydata bin/tests/system/xfer/ans5/badmessageid + bin/tests/system/xfer/ans5/ednsformerr + bin/tests/system/xfer/ans5/ednsnotimp bin/tests/system/xfer/ans5/goodaxfr + bin/tests/system/xfer/ans5/ixfrnotimp bin/tests/system/xfer/ans5/partial bin/tests/system/xfer/ans5/soamismatch bin/tests/system/xfer/ans5/unknownkey diff --git a/CHANGES b/CHANGES index 2efb524d7b..ba46357e7d 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ +6270. [bug] Handle an assertion when the primary server returned + NOTIMP to IXFR or FORMERR to EDNS to SOA/IXFR/AXFR + request when transfering a zone. [GL #4372] + 6269. [maint] B.ROOT-SERVERS.NET addresses are now 170.247.170.2 and 2801:1b8:10::b. [GL #4101] diff --git a/bin/tests/system/ans.pl b/bin/tests/system/ans.pl index bbae89ef57..bfd36bc0f1 100644 --- a/bin/tests/system/ans.pl +++ b/bin/tests/system/ans.pl @@ -65,6 +65,21 @@ # pattern, only this data will be signed. Currently, this is only # done for TCP. # +# /pattern NOTIMP / +# /pattern NOTIMP/ +# +# Return a NOTIMP response +# +# /pattern EDNS=NOTIMP / +# /pattern EDNS=NOTIMP/ +# +# Return a NOTIMP response to an EDNS request +# +# /pattern EDNS=FORMERR / +# /pattern EDNS=FORMERR/ +# +# Return a FORMERR response to an EDNS request +# # /pattern bad-id / # /pattern bad-id/ # @@ -345,6 +360,11 @@ sub handleTCP { my $qtype = $questions[0]->qtype; my $qclass = $questions[0]->qclass; my $id = $request->header->id; + my @additional = $request->additional; + my $has_opt = 0; + foreach (@additional) { + $has_opt = 1 if (ref($_) eq 'Net::DNS::RR::OPT'); + } my $opaque; @@ -376,14 +396,50 @@ sub handleTCP { if ("$qname $qtype" =~ /$dbtype/) { $count_these++; my $a; - foreach $a (@{$r->{answer}}) { - $packet->push("answer", $a); + my $done = 0; + + while (defined($key_name) && + ($key_name eq "NOTIMP" || $key_name eq "EDNS=NOTIMP" || + $key_name eq "EDNS=FORMERR" || $key_name eq "bad-id")) { + + if (defined($key_name) && $key_name eq "NOTIMP") { + $packet->header->rcode('NOTIMP') if (!$done); + $key_name = $key_data; + ($key_data, $tname) = split(/ /,$tname); + $done = 1; + } + + if (defined($key_name) && $key_name eq "EDNS=NOTIMP") { + if ($has_opt) { + $packet->header->rcode('NOTIMP') if (!$done); + $done = 1; + } + $key_name = $key_data; + ($key_data, $tname) = split(/ /,$tname); + } + + if (defined($key_name) && $key_name eq "EDNS=FORMERR") { + if ($has_opt) { + $packet->header->rcode('FORMERR') if (!$done); + $done = 1; + } + $key_name = $key_data; + ($key_data, $tname) = split(/ /,$tname); + } + + if (defined($key_name) && $key_name eq "bad-id") { + $packet->header->id(($id+50)%0xffff); + $key_name = $key_data; + ($key_data, $tname) = split(/ /,$tname); + } } - if (defined($key_name) && $key_name eq "bad-id") { - $packet->header->id(($id+50)%0xffff); - $key_name = $key_data; - ($key_data, $tname) = split(/ /,$tname) + + if (!$done) { + foreach $a (@{$r->{answer}}) { + $packet->push("answer", $a); + } } + if (defined($key_name) && defined($key_data)) { my $tsig; # sign the packet @@ -453,6 +509,7 @@ sub handleTCP { } #$packet->print; push(@results,$packet->data); + last if ($done); if ($tname eq "") { $tname = $qname; } diff --git a/bin/tests/system/xfer/ans5/ednsformerr b/bin/tests/system/xfer/ans5/ednsformerr new file mode 100644 index 0000000000..6ea77bef78 --- /dev/null +++ b/bin/tests/system/xfer/ans5/ednsformerr @@ -0,0 +1,10 @@ +/SOA tsig_key LSAnCU+Z/ +nil. 300 SOA ns.nil. root.nil. 1 300 300 604800 300 +/AXFR EDNS=FORMERR tsig_key LSAnCU+Z/ +nil. 300 SOA ns.nil. root.nil. 1 300 300 604800 300 +/AXFR EDNS=FORMERR tsig_key LSAnCU+Z/ +nil. 300 NS ns.nil. +nil. 300 TXT "EDNS FORMERR" +a.nil. 60 A 10.0.0.61 +/AXFR EDNS=FORMERR tsig_key LSAnCU+Z/ +nil. 300 SOA ns.nil. root.nil. 1 300 300 604800 300 diff --git a/bin/tests/system/xfer/ans5/ednsnotimp b/bin/tests/system/xfer/ans5/ednsnotimp new file mode 100644 index 0000000000..a1df16b406 --- /dev/null +++ b/bin/tests/system/xfer/ans5/ednsnotimp @@ -0,0 +1,12 @@ +/SOA tsig_key LSAnCU+Z/ +nil. 300 SOA ns.nil. root.nil. 1 300 300 604800 300 +/AXFR EDNS=NOTIMP tsig_key LSAnCU+Z/ +nil. 300 SOA ns.nil. root.nil. 1 300 300 604800 300 +/AXFR tsig_key LSAnCU+Z/ +nil. 300 SOA ns.nil. root.nil. 1 300 300 604800 300 +/AXFR tsig_key LSAnCU+Z/ +nil. 300 NS ns.nil. +nil. 300 TXT "EDNS NOTIMP" +a.nil. 60 A 10.0.0.61 +/AXFR tsig_key LSAnCU+Z/ +nil. 300 SOA ns.nil. root.nil. 1 300 300 604800 300 diff --git a/bin/tests/system/xfer/ans5/ixfrnotimp b/bin/tests/system/xfer/ans5/ixfrnotimp new file mode 100644 index 0000000000..a947a6346f --- /dev/null +++ b/bin/tests/system/xfer/ans5/ixfrnotimp @@ -0,0 +1,11 @@ +/SOA tsig_key LSAnCU+Z/ +nil. 300 SOA ns.nil. root.nil. 2 300 300 604800 300 +/IXFR NOTIMP tsig_key LSAnCU+Z/ +/AXFR tsig_key LSAnCU+Z/ +nil. 300 SOA ns.nil. root.nil. 2 300 300 604800 300 +/AXFR tsig_key LSAnCU+Z/ +nil. 300 NS ns.nil. +nil. 300 TXT "IXFR NOTIMP" +a.nil. 60 A 10.0.0.61 +/AXFR tsig_key LSAnCU+Z/ +nil. 300 SOA ns.nil. root.nil. 2 300 300 604800 300 diff --git a/bin/tests/system/xfer/tests.sh b/bin/tests/system/xfer/tests.sh index 5acc1c5d23..8a691b613f 100755 --- a/bin/tests/system/xfer/tests.sh +++ b/bin/tests/system/xfer/tests.sh @@ -307,6 +307,25 @@ $DIGCMD nil. TXT | grep 'initial AXFR' >/dev/null || { status=$((status+1)) } +n=$((n+1)) +echo_i "handle IXFR NOTIMP ($n)" + +sendcmd < ans5/ixfrnotimp + +$RNDCCMD 10.53.0.4 refresh nil | sed 's/^/ns4 /' | cat_i + +sleep 2 + +nextpart ns4/named.run | grep "zone nil/IN: requesting IXFR from 10.53.0.5" > /dev/null || { + echo_i "failed: expected status was not logged" + status=$((status+1)) +} + +$DIGCMD nil. TXT | grep 'IXFR NOTIMP' >/dev/null || { + echo_i "failed" + status=$((status+1)) +} + n=$((n+1)) echo_i "unsigned transfer ($n)" @@ -463,6 +482,38 @@ $DIGCMD nil. TXT | grep 'SOA mismatch AXFR' >/dev/null && { status=$((status+1)) } +n=$((n+1)) +echo_i "handle EDNS NOTIMP ($n)" + +$RNDCCMD 10.53.0.4 null testing EDNS NOTIMP | sed 's/^/ns4 /' | cat_i + +sendcmd < ans5/ednsnotimp + +$RNDCCMD 10.53.0.4 retransfer nil | sed 's/^/ns4 /' | cat_i + +sleep 2 + +nextpart ns4/named.run | grep "Transfer status: NOTIMP" > /dev/null || { + echo_i "failed: expected status was not logged" + status=$((status+1)) +} + +n=$((n+1)) +echo_i "handle EDNS FORMERR ($n)" + +$RNDCCMD 10.53.0.4 null testing EDNS FORMERR | sed 's/^/ns4 /' | cat_i + +sendcmd < ans5/ednsformerr + +$RNDCCMD 10.53.0.4 retransfer nil | sed 's/^/ns4 /' | cat_i + +sleep 10 + +$DIGCMD nil. TXT | grep 'EDNS FORMERR' >/dev/null || { + echo_i "failed" + status=$((status+1)) +} + n=$((n+1)) echo_i "check that we ask for and got a EDNS EXPIRE response when transfering from a secondary ($n)" tmp=0 diff --git a/lib/dns/xfrin.c b/lib/dns/xfrin.c index d389f7dc44..ef91a36c16 100644 --- a/lib/dns/xfrin.c +++ b/lib/dns/xfrin.c @@ -1205,8 +1205,9 @@ xfrin_start(dns_xfrin_t *xfr) { * do this because other connections could be using a different * certificate, so we just create a new dispatch every time. */ - if (xfr->transport == NULL || - dns_transport_get_type(xfr->transport) == DNS_TRANSPORT_TCP) + if (xfr->disp == NULL && + (xfr->transport == NULL || + dns_transport_get_type(xfr->transport) == DNS_TRANSPORT_TCP)) { dns_dispatchmgr_t *dispmgr = dns_view_getdispatchmgr(xfr->view); if (dispmgr == NULL) { @@ -1223,7 +1224,7 @@ xfrin_start(dns_xfrin_t *xfr) { isc_sockaddr_format(&xfr->primaryaddr, peer, sizeof(peer)); xfrin_log(xfr, ISC_LOG_DEBUG(1), "attached to TCP connection to %s", peer); - } else { + } else if (xfr->disp == NULL) { dns_dispatchmgr_t *dispmgr = dns_view_getdispatchmgr(xfr->view); if (dispmgr == NULL) { result = ISC_R_SHUTTINGDOWN; @@ -1236,6 +1237,11 @@ xfrin_start(dns_xfrin_t *xfr) { CHECK(result); } + /* If this is a retry, we need to cancel the previous dispentry */ + if (xfr->dispentry != NULL) { + dns_dispatch_done(&xfr->dispentry); + } + LIBDNS_XFRIN_START(xfr, xfr->info); /* @@ -1940,7 +1946,7 @@ xfrin_recv_done(isc_result_t result, isc_region_t *region, void *arg) { case XFRST_GOTSOA: xfr->reqtype = dns_rdatatype_axfr; atomic_store(&xfr->state, XFRST_ZONEXFRREQUEST); - CHECK(xfrin_send_request(xfr)); + CHECK(xfrin_start(xfr)); break; case XFRST_AXFR_END: case XFRST_IXFR_END: