From a99adb9efa16f4e03a35dee724ae5128dfa2a218 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Wed, 18 Oct 2023 18:13:52 +1100 Subject: [PATCH 1/4] Test NOTIMP being returned to an IXFR request in xfrin The server is expected to retry the transfer using SOA and if the returned serial is greater than the current serial AXFR. Check the log that IXFR is request. --- .reuse/dep5 | 1 + bin/tests/system/ans.pl | 15 ++++++++++++++- bin/tests/system/xfer/ans5/ixfrnotimp | 11 +++++++++++ bin/tests/system/xfer/tests.sh | 19 +++++++++++++++++++ 4 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 bin/tests/system/xfer/ans5/ixfrnotimp diff --git a/.reuse/dep5 b/.reuse/dep5 index 38b1937635..4cf9906f81 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -82,6 +82,7 @@ Files: **/*.after* bin/tests/system/xfer/ans5/badkeydata bin/tests/system/xfer/ans5/badmessageid 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/bin/tests/system/ans.pl b/bin/tests/system/ans.pl index bbae89ef57..946d2ae01f 100644 --- a/bin/tests/system/ans.pl +++ b/bin/tests/system/ans.pl @@ -65,6 +65,11 @@ # pattern, only this data will be signed. Currently, this is only # done for TCP. # +# /pattern NOTIMP / +# /pattern NOTIMP/ +# +# Return a NOTIMP response +# # /pattern bad-id / # /pattern bad-id/ # @@ -376,13 +381,20 @@ sub handleTCP { if ("$qname $qtype" =~ /$dbtype/) { $count_these++; my $a; + my $done = 0; foreach $a (@{$r->{answer}}) { $packet->push("answer", $a); } + if (defined($key_name) && $key_name eq "NOTIMP") { + $packet->header->rcode('NOTIMP'); + $key_name = $key_data; + ($key_data, $tname) = split(/ /,$tname); + $done = 1; + } if (defined($key_name) && $key_name eq "bad-id") { $packet->header->id(($id+50)%0xffff); $key_name = $key_data; - ($key_data, $tname) = split(/ /,$tname) + ($key_data, $tname) = split(/ /,$tname); } if (defined($key_name) && defined($key_data)) { my $tsig; @@ -453,6 +465,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/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..f6984fd423 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)" From aacea440c372a3fc230dc096a7c5d21cb3ac8f1b Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Wed, 18 Oct 2023 13:07:24 -0700 Subject: [PATCH 2/4] handle pre-existing disp/dispentry when retrying when xfrin_start() is called to retry a transfer, close the existing dispatch entry and reuse the existing dispatch. --- lib/dns/xfrin.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) 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: From 84fd3e38085ab589d791ab22ca4ef2138150e4c4 Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Thu, 19 Oct 2023 14:38:59 +1100 Subject: [PATCH 3/4] Test xfrin's handing of EDNS failure scenarios We test EDNS requests returning FORMERR where named is expected to retry without EDNS. We test EDNS requests returning NOTIMP where named is expected to fail the transfer as the remote end is not protocol compliant. --- .reuse/dep5 | 2 + bin/tests/system/ans.pl | 68 +++++++++++++++++++++----- bin/tests/system/xfer/ans5/ednsformerr | 10 ++++ bin/tests/system/xfer/ans5/ednsnotimp | 12 +++++ bin/tests/system/xfer/tests.sh | 32 ++++++++++++ 5 files changed, 112 insertions(+), 12 deletions(-) create mode 100644 bin/tests/system/xfer/ans5/ednsformerr create mode 100644 bin/tests/system/xfer/ans5/ednsnotimp diff --git a/.reuse/dep5 b/.reuse/dep5 index 4cf9906f81..d90b537d9e 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -81,6 +81,8 @@ 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 diff --git a/bin/tests/system/ans.pl b/bin/tests/system/ans.pl index 946d2ae01f..bfd36bc0f1 100644 --- a/bin/tests/system/ans.pl +++ b/bin/tests/system/ans.pl @@ -70,6 +70,16 @@ # # 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/ # @@ -350,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; @@ -382,20 +397,49 @@ sub handleTCP { $count_these++; my $a; my $done = 0; - foreach $a (@{$r->{answer}}) { - $packet->push("answer", $a); + + 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 "NOTIMP") { - $packet->header->rcode('NOTIMP'); - $key_name = $key_data; - ($key_data, $tname) = split(/ /,$tname); - $done = 1; - } - 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 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/tests.sh b/bin/tests/system/xfer/tests.sh index f6984fd423..8a691b613f 100755 --- a/bin/tests/system/xfer/tests.sh +++ b/bin/tests/system/xfer/tests.sh @@ -482,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 From c00c1e9a72765a9403eb38d95f36a400097cfbda Mon Sep 17 00:00:00 2001 From: Mark Andrews Date: Thu, 19 Oct 2023 14:52:15 +1100 Subject: [PATCH 4/4] Add CHANGES note for [GL #4372] --- CHANGES | 4 ++++ 1 file changed, 4 insertions(+) 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]