2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-30 22:15:20 +00:00

[9.20] fix: usr: Fix a serve-stale issue with a delegated zone

When ``stale-answer-client-timeout 0`` option was enabled, it could be ignored
when resolving a zone which is a delegation of an authoritative zone belonging
to the resolver. This has been fixed.

Closes #5275

Backport of MR !10381

Merge branch 'backport-5275-stale-answer-client-timeout-0-and-delegation-fix-9.20' into 'bind-9.20'

See merge request isc-projects/bind9!10420
This commit is contained in:
Arаm Sаrgsyаn
2025-04-23 13:41:46 +00:00
5 changed files with 148 additions and 7 deletions

View File

@@ -44,11 +44,14 @@ my $udpsock = IO::Socket::INET->new(LocalAddr => "$localaddr",
LocalPort => $localport, Proto => "udp", Reuse => 1) or die "$!";
#
# Delegation
# Delegations
#
my $SOA = "example 300 IN SOA . . 0 0 0 0 300";
my $NS = "example 300 IN NS ns.example";
my $A = "ns.example 300 IN A $localaddr";
my $ssSOA = "delegated.serve.stale 300 IN SOA . . 0 0 0 0 300";
my $ssNS = "delegated.serve.stale 300 IN NS ns.delegated.serve.stale";
my $ssA = "ns.delegated.serve.stale 300 IN A $localaddr";
#
# Slow delegation
@@ -66,6 +69,7 @@ my $TXT = "data.example 2 IN TXT \"A text record with a 2 second ttl\"";
my $LONGTXT = "longttl.example 600 IN TXT \"A text record with a 600 second ttl\"";
my $CAA = "othertype.example 2 IN CAA 0 issue \"ca1.example.net\"";
my $negSOA = "example 2 IN SOA . . 0 0 0 0 300";
my $ssnegSOA = "delegated.serve.stale 2 IN SOA . . 0 0 0 0 300";
my $CNAME = "cname.example 7 IN CNAME target.example";
my $TARGET = "target.example 9 IN A $localaddr";
my $SHORTCNAME = "shortttl.cname.example 1 IN CNAME longttl.target.example";
@@ -223,6 +227,38 @@ sub reply_handler {
push @auth, $rr;
}
$rcode = "NOERROR";
} elsif ($qname eq "ns.delegated.serve.stale" ) {
if ($qtype eq "A") {
my $rr = new Net::DNS::RR($ssA);
push @ans, $rr;
} else {
my $rr = new Net::DNS::RR($ssSOA);
push @auth, $rr;
}
$rcode = "NOERROR";
} elsif ($qname eq "delegated.serve.stale") {
if ($qtype eq "NS") {
my $rr = new Net::DNS::RR($ssNS);
push @auth, $rr;
$rr = new Net::DNS::RR($ssA);
push @add, $rr;
} elsif ($qtype eq "SOA") {
my $rr = new Net::DNS::RR($ssSOA);
push @ans, $rr;
} else {
my $rr = new Net::DNS::RR($ssSOA);
push @auth, $rr;
}
$rcode = "NOERROR";
} elsif ($qname eq "www.delegated.serve.stale") {
if ($qtype eq "A") {
my $rr = new Net::DNS::RR("www.delegated.serve.stale 2 IN A 10.53.0.99");
push @ans, $rr;
} else {
my $rr = new Net::DNS::RR($ssnegSOA);
push @auth, $rr;
}
$rcode = "NOERROR";
} elsif ($qname eq "ns.slow" ) {
if ($qtype eq "A") {
my $rr = new Net::DNS::RR($slowA);

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
key rndc_key {
secret "1234abcd8765";
algorithm @DEFAULT_HMAC@;
};
controls {
inet 10.53.0.3 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
};
options {
query-source address 10.53.0.3;
notify-source 10.53.0.3;
transfer-source 10.53.0.3;
port @PORT@;
pid-file "named.pid";
listen-on { 10.53.0.3; };
listen-on-v6 { none; };
recursion yes;
dnssec-validation no;
stale-answer-enable yes;
stale-cache-enable yes;
stale-answer-ttl 3;
stale-answer-client-timeout 0;
};
zone "." {
type secondary;
primaries { 10.53.0.1; };
file "root.bk";
};
zone "serve.stale" IN {
type primary;
notify no;
file "serve.stale.db";
};

View File

@@ -16,3 +16,6 @@ ns.serve.stale. IN A 10.53.0.6
$ORIGIN serve.stale.
test IN NS nss1.example.nxd.
test IN NS nss2.example.nxd.
delegated IN NS ns2.delegated.serve.stale.
ns2.delegated IN A 10.53.0.2

View File

@@ -2506,5 +2506,33 @@ grep "2001:aaaa" dig.out.2.test$n >/dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
n=$((n + 1))
echo_i "check serve-stale (stale-answer-client-timeout 0) with a delegation ($n)"
ret=0
# configure ns3 with stale-answer-client-timeout 0 and a delegated zone
copy_setports ns3/named9.conf.in ns3/named.conf
rndc_reload ns3 10.53.0.3
# flush cache, enable ans2 responses, make sure serve-stale is on
$RNDCCMD 10.53.0.3 flush >rndc.out.test$n.1 2>&1 || ret=1
$DIG -p ${PORT} @10.53.0.2 txt enable >/dev/null || ret=1
$RNDCCMD 10.53.0.3 serve-stale on >rndc.out.test$n.2 2>&1 || ret=1
# prime the cache with the A response
$DIG -p ${PORT} @10.53.0.3 www.delegated.serve.stale >dig.out.1.test$n || ret=1
grep -F "status: NOERROR" dig.out.1.test$n >/dev/null || ret=1
grep -F "10.53.0.99" dig.out.1.test$n >/dev/null || ret=1
# disable responses from the auth server
$DIG -p ${PORT} @10.53.0.2 txt disable >/dev/null || ret=1
# wait two seconds for the previous answer to become stale
sleep 2
# resend the query; we should immediately get a stale answer
$DIG -p ${PORT} @10.53.0.3 www.delegated.serve.stale >dig.out.2.test$n || ret=1
grep -F "status: NOERROR" dig.out.2.test$n >/dev/null || ret=1
grep -F "EDE: 3 (Stale Answer): (stale data prioritized over lookup)" dig.out.2.test$n >/dev/null || ret=1
grep -F "10.53.0.99" dig.out.2.test$n >/dev/null || ret=1
# re-enable responses
$DIG -p ${PORT} @10.53.0.2 txt enable >/dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status + ret))
echo_i "exit status: $status"
[ $status -eq 0 ] || exit 1

View File

@@ -6206,11 +6206,18 @@ query_lookup(query_ctx_t *qctx) {
}
} else if (stale_timeout) {
if (qctx->options.stalefirst) {
if (!stale_found && !answer_found) {
/*
* We have nothing useful in cache to return
* immediately.
*/
/*
* If 'qctx->zdb' is set, this was a cache lookup after
* an authoritative lookup returned a delegation (in
* order to find a better answer). But we still can
* return without getting any usable answer here, as
* query_notfound() should handle it from here.
* Otherwise, if nothing useful was found in cache then
* recursively call query_lookup() again without the
* 'stalefirst' option set.
*/
if (!stale_found && !answer_found && qctx->zdb == NULL)
{
qctx_clean(qctx);
qctx_freedata(qctx);
dns_db_attach(qctx->client->view->cachedb,
@@ -8936,7 +8943,25 @@ query_zone_delegation(query_ctx_t *qctx) {
dns_db_attach(qctx->view->cachedb, &qctx->db);
qctx->is_zone = false;
return query_lookup(qctx);
/*
* Since 'qctx->is_zone' is now false, we should reconsider
* setting the 'stalefirst' option, which is usually set in
* the beginning in ns__query_start().
*/
if (qctx->view->staleanswerclienttimeout == 0 &&
dns_view_staleanswerenabled(qctx->view))
{
qctx->options.stalefirst = true;
}
result = query_lookup(qctx);
/*
* After fetch completes, this option is not expected to be set.
*/
qctx->options.stalefirst = false;
return result;
}
return query_prepare_delegation_response(qctx);