mirror of
https://github.com/isc-projects/bind9.git
synced 2026-05-28 04:34:54 -04:00
[9.20] fix: usr: Clear serve-stale flags when following the CNAME chains
A stale answer could have been served in case of multiple upstream failures when following the CNAME chains. This has been fixed. Closes #5751 Backport of MR !11558 Merge branch 'backport-5751-clear-staleflags-in-CNAME-chains-9.20' into 'bind-9.20' See merge request isc-projects/bind9!11583
This commit is contained in:
commit
68fb231294
9 changed files with 568 additions and 5 deletions
|
|
@ -75,6 +75,15 @@ my $TARGET = "target.example 9 IN A $localaddr";
|
|||
my $SHORTCNAME = "shortttl.cname.example 1 IN CNAME longttl.target.example";
|
||||
my $LONGTARGET = "longttl.target.example 600 IN A $localaddr";
|
||||
|
||||
#
|
||||
# YWH records
|
||||
#
|
||||
my $ywhSOA = "source.stale 300 IN SOA . . 0 0 0 0 300";
|
||||
my $ywhNS = "source.stale 300 IN NS ns.source.stale";
|
||||
my $ywhA = "ns.source.stale 300 IN A $localaddr";
|
||||
my $ywhCNAME = "alias.source.stale 2 IN CNAME www.target.stale";
|
||||
my $ywhCNAMENX = "aliasnx.source.stale 2 IN CNAME nonexist.target.stale";
|
||||
|
||||
sub reply_handler {
|
||||
my ($qname, $qclass, $qtype) = @_;
|
||||
my ($rcode, @ans, @auth, @add);
|
||||
|
|
@ -306,6 +315,34 @@ sub reply_handler {
|
|||
push @auth, $rr;
|
||||
}
|
||||
$rcode = "NOERROR";
|
||||
} elsif ($qname eq "source.stale") {
|
||||
if ($qtype eq "SOA") {
|
||||
my $rr = new Net::DNS::RR($ywhSOA);
|
||||
push @ans, $rr;
|
||||
} elsif ($qtype eq "NS") {
|
||||
my $rr = new Net::DNS::RR($ywhNS);
|
||||
push @ans, $rr;
|
||||
$rr = new Net::DNS::RR($ywhA);
|
||||
push @add, $rr;
|
||||
}
|
||||
$rcode = "NOERROR";
|
||||
} elsif ($qname eq "ns.source.stale") {
|
||||
if ($qtype eq "A") {
|
||||
my $rr = new Net::DNS::RR($ywhA);
|
||||
push @ans, $rr;
|
||||
} else {
|
||||
my $rr = new Net::DNS::RR($ywhSOA);
|
||||
push @auth, $rr;
|
||||
}
|
||||
$rcode = "NOERROR";
|
||||
} elsif ($qname eq "alias.source.stale") {
|
||||
my $rr = new Net::DNS::RR($ywhCNAME);
|
||||
push @ans, $rr;
|
||||
$rcode = "NOERROR";
|
||||
} elsif ($qname eq "aliasnx.source.stale") {
|
||||
my $rr = new Net::DNS::RR($ywhCNAMENX);
|
||||
push @ans, $rr;
|
||||
$rcode = "NOERROR";
|
||||
} else {
|
||||
my $rr = new Net::DNS::RR($SOA);
|
||||
push @auth, $rr;
|
||||
|
|
|
|||
164
bin/tests/system/serve-stale/ans8/ans.pl
Normal file
164
bin/tests/system/serve-stale/ans8/ans.pl
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
#!/usr/bin/env perl
|
||||
|
||||
# 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.
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use IO::File;
|
||||
use IO::Socket;
|
||||
use Getopt::Long;
|
||||
use Net::DNS;
|
||||
use Time::HiRes qw(usleep nanosleep);
|
||||
|
||||
my $pidf = new IO::File "ans.pid", "w" or die "cannot open pid file: $!";
|
||||
print $pidf "$$\n" or die "cannot write pid file: $!";
|
||||
$pidf->close or die "cannot close pid file: $!";
|
||||
sub rmpid { unlink "ans.pid"; exit 1; };
|
||||
|
||||
$SIG{INT} = \&rmpid;
|
||||
$SIG{TERM} = \&rmpid;
|
||||
|
||||
my $localaddr = "10.53.0.8";
|
||||
|
||||
my $localport = int($ENV{'PORT'});
|
||||
if (!$localport) { $localport = 5300; }
|
||||
|
||||
my $udpsock = IO::Socket::INET->new(LocalAddr => "$localaddr",
|
||||
LocalPort => $localport, Proto => "udp", Reuse => 1) or die "$!";
|
||||
|
||||
#
|
||||
# YWH records
|
||||
#
|
||||
my $ywhSOA = "target.stale 300 IN SOA . . 0 0 0 0 300";
|
||||
my $ywhNS = "target.stale 300 IN NS ns.target.stale";
|
||||
my $ywhA = "ns.target.stale 300 IN A $localaddr";
|
||||
my $ywhWWW = "www.target.stale 2 IN A 10.0.0.1";
|
||||
|
||||
sub reply_handler {
|
||||
my ($qname, $qclass, $qtype) = @_;
|
||||
my ($rcode, @ans, @auth, @add);
|
||||
|
||||
print ("request: $qname/$qtype\n");
|
||||
STDOUT->flush();
|
||||
|
||||
# Control what response we send.
|
||||
if ($qname eq "update" ) {
|
||||
if ($qtype eq "TXT") {
|
||||
$ywhWWW = "www.target.stale 2 IN A 10.0.0.2";
|
||||
my $rr = new Net::DNS::RR("$qname 0 $qclass TXT \"update\"");
|
||||
push @ans, $rr;
|
||||
}
|
||||
$rcode = "NOERROR";
|
||||
return ($rcode, \@ans, \@auth, \@add, { aa => 1 });
|
||||
} elsif ($qname eq "restore" ) {
|
||||
if ($qtype eq "TXT") {
|
||||
$ywhWWW = "www.target.stale 2 IN A 10.0.0.1";
|
||||
my $rr = new Net::DNS::RR("$qname 0 $qclass TXT \"restore\"");
|
||||
push @ans, $rr;
|
||||
}
|
||||
$rcode = "NOERROR";
|
||||
return ($rcode, \@ans, \@auth, \@add, { aa => 1 });
|
||||
}
|
||||
|
||||
if ($qname eq "target.stale") {
|
||||
if ($qtype eq "SOA") {
|
||||
my $rr = new Net::DNS::RR($ywhSOA);
|
||||
push @ans, $rr;
|
||||
} elsif ($qtype eq "NS") {
|
||||
my $rr = new Net::DNS::RR($ywhNS);
|
||||
push @ans, $rr;
|
||||
$rr = new Net::DNS::RR($ywhA);
|
||||
push @add, $rr;
|
||||
}
|
||||
$rcode = "NOERROR";
|
||||
} elsif ($qname eq "ns.target.stale") {
|
||||
if ($qtype eq "A") {
|
||||
my $rr = new Net::DNS::RR($ywhA);
|
||||
push @ans, $rr;
|
||||
} else {
|
||||
my $rr = new Net::DNS::RR($ywhSOA);
|
||||
push @auth, $rr;
|
||||
}
|
||||
$rcode = "NOERROR";
|
||||
} elsif ($qname eq "www.target.stale") {
|
||||
if ($qtype eq "A") {
|
||||
my $rr = new Net::DNS::RR($ywhWWW);
|
||||
push @ans, $rr;
|
||||
} else {
|
||||
my $rr = new Net::DNS::RR($ywhSOA);
|
||||
push @auth, $rr;
|
||||
}
|
||||
$rcode = "NOERROR";
|
||||
} else {
|
||||
my $rr = new Net::DNS::RR($ywhSOA);
|
||||
push @auth, $rr;
|
||||
$rcode = "NXDOMAIN";
|
||||
}
|
||||
|
||||
# mark the answer as authoritative (by setting the 'aa' flag)
|
||||
return ($rcode, \@ans, \@auth, \@add, { aa => 1 });
|
||||
}
|
||||
|
||||
GetOptions(
|
||||
'port=i' => \$localport,
|
||||
);
|
||||
|
||||
my $rin;
|
||||
my $rout;
|
||||
|
||||
for (;;) {
|
||||
$rin = '';
|
||||
vec($rin, fileno($udpsock), 1) = 1;
|
||||
|
||||
select($rout = $rin, undef, undef, undef);
|
||||
|
||||
if (vec($rout, fileno($udpsock), 1)) {
|
||||
my ($buf, $request, $err);
|
||||
$udpsock->recv($buf, 512);
|
||||
|
||||
if ($Net::DNS::VERSION > 0.68) {
|
||||
$request = new Net::DNS::Packet(\$buf, 0);
|
||||
$@ and die $@;
|
||||
} else {
|
||||
my $err;
|
||||
($request, $err) = new Net::DNS::Packet(\$buf, 0);
|
||||
$err and die $err;
|
||||
}
|
||||
|
||||
my @questions = $request->question;
|
||||
my $qname = $questions[0]->qname;
|
||||
my $qclass = $questions[0]->qclass;
|
||||
my $qtype = $questions[0]->qtype;
|
||||
my $id = $request->header->id;
|
||||
|
||||
my ($rcode, $ans, $auth, $add, $headermask) = reply_handler($qname, $qclass, $qtype);
|
||||
|
||||
if (!defined($rcode)) {
|
||||
print " Silently ignoring query\n";
|
||||
next;
|
||||
}
|
||||
|
||||
my $reply = Net::DNS::Packet->new();
|
||||
$reply->header->qr(1);
|
||||
$reply->header->aa(1) if $headermask->{'aa'};
|
||||
$reply->header->id($id);
|
||||
$reply->header->rcode($rcode);
|
||||
$reply->push("question", @questions);
|
||||
$reply->push("answer", @$ans) if $ans;
|
||||
$reply->push("authority", @$auth) if $auth;
|
||||
$reply->push("additional", @$add) if $add;
|
||||
|
||||
my $num_chars = $udpsock->send($reply->data);
|
||||
print " Sent $num_chars bytes via UDP\n";
|
||||
}
|
||||
}
|
||||
|
|
@ -9,9 +9,12 @@
|
|||
; See the COPYRIGHT file distributed with this work for additional
|
||||
; information regarding copyright ownership.
|
||||
|
||||
stale. IN SOA ns.stale. matthijs.isc.org. 1 0 0 0 0
|
||||
stale. IN NS ns.stale.
|
||||
ns.stale. IN A 10.53.0.6
|
||||
stale. IN SOA ns.stale. matthijs.isc.org. 1 0 0 0 0
|
||||
stale. IN NS ns.stale.
|
||||
ns.stale. IN A 10.53.0.6
|
||||
|
||||
serve.stale. IN NS ns.serve.stale.
|
||||
ns.serve.stale. IN A 10.53.0.6
|
||||
serve.stale. IN NS ns.serve.stale.
|
||||
ns.serve.stale. IN A 10.53.0.6
|
||||
|
||||
target.stale. IN NS ns.target.stale.
|
||||
ns.target.stale. IN A 10.53.0.7
|
||||
|
|
|
|||
62
bin/tests/system/serve-stale/ns7/named.conf.j2
Normal file
62
bin/tests/system/serve-stale/ns7/named.conf.j2
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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.7 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
|
||||
};
|
||||
|
||||
options {
|
||||
query-source address 10.53.0.7;
|
||||
notify-source 10.53.0.7;
|
||||
transfer-source 10.53.0.7;
|
||||
port @PORT@;
|
||||
pid-file "named.pid";
|
||||
listen-on { 10.53.0.7; };
|
||||
listen-on-v6 { none; };
|
||||
recursion yes;
|
||||
dnssec-validation no;
|
||||
qname-minimization off;
|
||||
|
||||
stale-answer-enable yes;
|
||||
stale-cache-enable yes;
|
||||
max-stale-ttl 3600;
|
||||
|
||||
stale-answer-client-timeout off;
|
||||
stale-refresh-time 30;
|
||||
|
||||
max-cache-ttl 300;
|
||||
max-ncache-ttl 300;
|
||||
};
|
||||
|
||||
zone "." {
|
||||
type hint;
|
||||
file "root.db";
|
||||
};
|
||||
|
||||
// Authoritative zone: nonexist.target.stale -> NXDOMAIN
|
||||
zone "target.stale" {
|
||||
type primary;
|
||||
file "target.stale.db";
|
||||
};
|
||||
|
||||
// Forward source.stale queries to ans2
|
||||
zone "source.stale" {
|
||||
type forward;
|
||||
forward only;
|
||||
forwarders { 10.53.0.2 port @PORT@; };
|
||||
};
|
||||
63
bin/tests/system/serve-stale/ns7/named1.conf.j2
Normal file
63
bin/tests/system/serve-stale/ns7/named1.conf.j2
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* 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.7 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
|
||||
};
|
||||
|
||||
options {
|
||||
query-source address 10.53.0.7;
|
||||
notify-source 10.53.0.7;
|
||||
transfer-source 10.53.0.7;
|
||||
port @PORT@;
|
||||
pid-file "named.pid";
|
||||
listen-on { 10.53.0.7; };
|
||||
listen-on-v6 { none; };
|
||||
recursion yes;
|
||||
dnssec-validation no;
|
||||
qname-minimization off;
|
||||
|
||||
stale-answer-enable yes;
|
||||
stale-cache-enable yes;
|
||||
max-stale-ttl 3600;
|
||||
|
||||
stale-answer-client-timeout off;
|
||||
stale-refresh-time 30;
|
||||
|
||||
max-cache-ttl 300;
|
||||
max-ncache-ttl 300;
|
||||
};
|
||||
|
||||
zone "." {
|
||||
type hint;
|
||||
file "root.db";
|
||||
};
|
||||
|
||||
// Forward source.stale queries to ans2
|
||||
zone "source.stale" {
|
||||
type forward;
|
||||
forward only;
|
||||
forwarders { 10.53.0.2 port @PORT@; };
|
||||
};
|
||||
|
||||
// Forward target.stale queries to ans8
|
||||
zone "target.stale" {
|
||||
type forward;
|
||||
forward only;
|
||||
forwarders { 10.53.0.8 port @PORT@; };
|
||||
};
|
||||
1
bin/tests/system/serve-stale/ns7/root.db
Symbolic link
1
bin/tests/system/serve-stale/ns7/root.db
Symbolic link
|
|
@ -0,0 +1 @@
|
|||
../ns1/root.db
|
||||
18
bin/tests/system/serve-stale/ns7/target.stale.db
Normal file
18
bin/tests/system/serve-stale/ns7/target.stale.db
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
; 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.
|
||||
|
||||
target.stale. IN SOA ns.target.stale. ywh. 1 0 0 0 0
|
||||
target.stale. IN NS ns.target.stale.
|
||||
ns.target.stale. IN A 10.53.0.6
|
||||
|
||||
; NOTE: "nonexist.target.stale." is NOT defined here.
|
||||
; Queries for it will return authoritative NXDOMAIN.
|
||||
; This is the CNAME target from alias.source.stale.
|
||||
|
|
@ -24,6 +24,212 @@ stale_answer_ttl=$(sed -ne 's,^[[:space:]]*stale-answer-ttl \([[:digit:]]*\).*,\
|
|||
status=0
|
||||
n=0
|
||||
|
||||
#
|
||||
# YWH-PGM40640-56:
|
||||
# Stale/Wrong DNS Data Served via CNAME Flag Leak.
|
||||
#
|
||||
echo_i "test server with serve-stale options set"
|
||||
|
||||
#
|
||||
# Variant 1: local authoritative zone
|
||||
#
|
||||
|
||||
# Initial query — populates cache, gets correct NXDOMAIN
|
||||
n=$((n + 1))
|
||||
echo_i "prime cache aliasnx.source.stale A ($n)"
|
||||
ret=0
|
||||
$DIG -p ${PORT} @10.53.0.7 aliasnx.source.stale A >dig.out.test$n || ret=1
|
||||
grep "status: NXDOMAIN" dig.out.test$n >/dev/null || ret=1
|
||||
grep "ANSWER: 1," dig.out.test$n >/dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
# Wait for CNAME TTL to expire
|
||||
sleep 3
|
||||
# Kill auth server — source.test becomes unreachable
|
||||
n=$((n + 1))
|
||||
echo_i "disable responses from authoritative server ($n)"
|
||||
ret=0
|
||||
$DIG -p ${PORT} @10.53.0.2 txt disable >dig.out.test$n || ret=1
|
||||
grep "ANSWER: 1," dig.out.test$n >/dev/null || ret=1
|
||||
grep "TXT.\"0\"" dig.out.test$n >/dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
# Query via stale CNAME — triggers the bug
|
||||
n=$((n + 1))
|
||||
echo_i "check stale aliasnx.source.stale A ($n)"
|
||||
ret=0
|
||||
$DIG -p ${PORT} @10.53.0.7 aliasnx.source.stale A >dig.out.test$n || ret=1
|
||||
grep "status: NXDOMAIN" dig.out.test$n >/dev/null || ret=1
|
||||
grep "ANSWER: 1," dig.out.test$n >/dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
# Restore auth server
|
||||
n=$((n + 1))
|
||||
echo_i "enable responses from authoritative server ($n)"
|
||||
ret=0
|
||||
$DIG -p ${PORT} @10.53.0.2 txt enable >dig.out.test$n || ret=1
|
||||
grep "ANSWER: 1," dig.out.test$n >/dev/null || ret=1
|
||||
grep "TXT.\"1\"" dig.out.test$n >/dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
#
|
||||
# Variant 2: stale/wrong data served
|
||||
#
|
||||
n=$((n + 1))
|
||||
echo_i "updating ns7/named.conf ($n)"
|
||||
ret=0
|
||||
cp ns7/named1.conf ns7/named.conf
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
n=$((n + 1))
|
||||
echo_i "running 'rndc reload' ($n)"
|
||||
ret=0
|
||||
rndc_reload ns7 10.53.0.7
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
# Initial query — caches both CNAME and A record
|
||||
n=$((n + 1))
|
||||
echo_i "prime cache alias.source.stale A ($n)"
|
||||
ret=0
|
||||
$DIG -p ${PORT} @10.53.0.7 alias.source.stale A >dig.out.test$n || ret=1
|
||||
grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
|
||||
grep "ANSWER: 2," dig.out.test$n >/dev/null || ret=1
|
||||
grep "alias.source.stale.*2.*IN.*CNAME.*www.target.stale." dig.out.test$n >/dev/null || ret=1
|
||||
grep "www.target.stale.*2.*IN.*A.*10.0.0.1" dig.out.test$n >/dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
# Wait for both TTLs to expire
|
||||
sleep 3
|
||||
# Kill source.test auth (CNAME becomes stale)
|
||||
n=$((n + 1))
|
||||
echo_i "disable responses from authoritative server ($n)"
|
||||
ret=0
|
||||
$DIG -p ${PORT} @10.53.0.2 txt disable >dig.out.test$n || ret=1
|
||||
grep "ANSWER: 1," dig.out.test$n >/dev/null || ret=1
|
||||
grep "TXT.\"0\"" dig.out.test$n >/dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
# Kill target auth, restart with NEW IP (10.0.0.2)
|
||||
n=$((n + 1))
|
||||
echo_i "update target authoritative server ($n)"
|
||||
ret=0
|
||||
$DIG -p ${PORT} @10.53.0.8 txt update >dig.out.test$n || ret=1
|
||||
grep "ANSWER: 1," dig.out.test$n >/dev/null || ret=1
|
||||
grep "TXT.\"update\"" dig.out.test$n >/dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
# Query via stale CNAME — triggers the bug
|
||||
n=$((n + 1))
|
||||
echo_i "check stale alias.source.stale A ($n)"
|
||||
ret=0
|
||||
$DIG -p ${PORT} @10.53.0.7 alias.source.stale A >dig.out.test$n || ret=1
|
||||
grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
|
||||
grep "ANSWER: 2," dig.out.test$n >/dev/null || ret=1
|
||||
grep "alias.source.stale.*30.*IN.*CNAME.*www.target.stale." dig.out.test$n >/dev/null || ret=1
|
||||
grep "www.target.stale.*2.*IN.*A.*10.0.0.2" dig.out.test$n >/dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
# Control: direct query for same name (no stale CNAME involved)
|
||||
n=$((n + 1))
|
||||
echo_i "check target www.target.stale A ($n)"
|
||||
ret=0
|
||||
$DIG -p ${PORT} @10.53.0.7 www.target.stale A >dig.out.test$n || ret=1
|
||||
grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
|
||||
grep "ANSWER: 1," dig.out.test$n >/dev/null || ret=1
|
||||
grep "www.target.stale.*IN.*A.*10.0.0.2" dig.out.test$n >/dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
# Restore auth servers
|
||||
n=$((n + 1))
|
||||
echo_i "enable responses from authoritative server ($n)"
|
||||
ret=0
|
||||
$DIG -p ${PORT} @10.53.0.2 txt enable >dig.out.test$n || ret=1
|
||||
grep "ANSWER: 1," dig.out.test$n >/dev/null || ret=1
|
||||
grep "TXT.\"1\"" dig.out.test$n >/dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
n=$((n + 1))
|
||||
echo_i "update target authoritative server ($n)"
|
||||
ret=0
|
||||
$DIG -p ${PORT} @10.53.0.8 txt restore >dig.out.test$n || ret=1
|
||||
grep "ANSWER: 1," dig.out.test$n >/dev/null || ret=1
|
||||
grep "TXT.\"restore\"" dig.out.test$n >/dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
#
|
||||
# Variant 3: recursion blocked, servfail
|
||||
#
|
||||
|
||||
# Flush stale data
|
||||
n=$((n + 1))
|
||||
echo_i "flush stale data ($n)"
|
||||
ret=0
|
||||
$RNDCCMD 10.53.0.7 flushtree stale >/dev/null 2>&1 || ret=1
|
||||
sleep 1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
# Initial query — NXDOMAIN via CNAME chain through BOTH forwarders
|
||||
n=$((n + 1))
|
||||
echo_i "prime cache aliasnx.source.stale A ($n)"
|
||||
ret=0
|
||||
$DIG -p ${PORT} @10.53.0.7 aliasnx.source.stale A >dig.out.test$n || ret=1
|
||||
grep "status: NXDOMAIN" dig.out.test$n >/dev/null || ret=1
|
||||
grep "ANSWER: 1," dig.out.test$n >/dev/null || ret=1
|
||||
grep "aliasnx.source.stale.*2.*IN.*CNAME.*nonexist.target.stale." dig.out.test$n >/dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
# Wait for CNAME TTL to expire
|
||||
sleep 3
|
||||
# Kill source.test auth ONLY (target.test auth stays alive!)
|
||||
n=$((n + 1))
|
||||
echo_i "disable responses from authoritative server ($n)"
|
||||
ret=0
|
||||
$DIG -p ${PORT} @10.53.0.2 txt disable >dig.out.test$n || ret=1
|
||||
grep "ANSWER: 1," dig.out.test$n >/dev/null || ret=1
|
||||
grep "TXT.\"0\"" dig.out.test$n >/dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
# Flush target's negative cache entry (simulates cache eviction/pressure)
|
||||
n=$((n + 1))
|
||||
echo_i "flush name nonexist.target.stale ($n)"
|
||||
ret=0
|
||||
$RNDCCMD 10.53.0.7 flushname nonexist.target.stale >/dev/null 2>&1 || ret=1
|
||||
sleep 1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
# Verify target auth is STILL ALIVE and returns correct NXDOMAIN
|
||||
n=$((n + 1))
|
||||
echo_i "verify nonexist.target.stale A ($n)"
|
||||
ret=0
|
||||
$DIG -p ${PORT} @10.53.0.8 nonexist.target.stale A >dig.out.test$n || ret=1
|
||||
grep "status: NXDOMAIN" dig.out.test$n >/dev/null || ret=1
|
||||
grep "ANSWER: 0," dig.out.test$n >/dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
# Query via stale CNAME — triggers the bug
|
||||
n=$((n + 1))
|
||||
echo_i "check stale aliasnx.source.stale A ($n)"
|
||||
ret=0
|
||||
$DIG -p ${PORT} @10.53.0.7 aliasnx.source.stale A >dig.out.test$n || ret=1
|
||||
grep "status: NXDOMAIN" dig.out.test$n >/dev/null || ret=1
|
||||
grep "ANSWER: 1," dig.out.test$n >/dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
grep "aliasnx.source.stale.*30.*IN.*CNAME.*nonexist.target.stale." dig.out.test$n >/dev/null || ret=1
|
||||
# Restore auth server
|
||||
n=$((n + 1))
|
||||
echo_i "enable responses from authoritative server ($n)"
|
||||
ret=0
|
||||
$DIG -p ${PORT} @10.53.0.2 txt enable >dig.out.test$n || ret=1
|
||||
grep "ANSWER: 1," dig.out.test$n >/dev/null || ret=1
|
||||
grep "TXT.\"1\"" dig.out.test$n >/dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status + ret))
|
||||
|
||||
#
|
||||
# First test server with serve-stale options set.
|
||||
#
|
||||
|
|
|
|||
|
|
@ -5780,6 +5780,8 @@ root_key_sentinel_detect(query_ctx_t *qctx) {
|
|||
isc_result_t
|
||||
ns__query_start(query_ctx_t *qctx) {
|
||||
isc_result_t result = ISC_R_UNSET;
|
||||
ns_client_t *client = qctx->client;
|
||||
|
||||
CCTRACE(ISC_LOG_DEBUG(3), "ns__query_start");
|
||||
qctx->want_restart = false;
|
||||
qctx->authoritative = false;
|
||||
|
|
@ -5788,6 +5790,13 @@ ns__query_start(query_ctx_t *qctx) {
|
|||
qctx->need_wildcardproof = false;
|
||||
qctx->rpz = false;
|
||||
|
||||
/*
|
||||
* Clean existing stale options in case ns__query_start was restarted
|
||||
* due to the CNAME/DNAME chains.
|
||||
*/
|
||||
client->query.dboptions &= ~(DNS_DBFIND_STALETIMEOUT |
|
||||
DNS_DBFIND_STALEOK);
|
||||
|
||||
CALL_HOOK(NS_QUERY_START_BEGIN, qctx);
|
||||
|
||||
/*
|
||||
|
|
|
|||
Loading…
Reference in a new issue