mirror of
https://github.com/isc-projects/bind9.git
synced 2026-07-15 17:50:51 -04:00
Merge branch '2247-add-serve-stale-option-to-set-client-timeout' into 'main'
Resolve "Add serve-stale option to set client timeout" Closes #2247 See merge request isc-projects/bind9!4514
This commit is contained in:
commit
e96bf1f3dc
33 changed files with 1511 additions and 149 deletions
5
CHANGES
5
CHANGES
|
|
@ -1,3 +1,8 @@
|
|||
5566. [func] Add "stale-answer-client-timeout" option, which
|
||||
is the amount of time a recursive resolver waits before
|
||||
attempting to answer the query using stale data from cache.
|
||||
[GL #2247]
|
||||
|
||||
5565. [func] The SONAMEs for BIND 9 libraries now include the current
|
||||
BIND 9 version number, in an effort to tightly couple
|
||||
internal libraries with a specific release. [GL #2387]
|
||||
|
|
|
|||
|
|
@ -194,9 +194,10 @@ options {\n\
|
|||
servfail-ttl 1;\n\
|
||||
# sortlist <none>\n\
|
||||
stale-answer-enable false;\n\
|
||||
stale-refresh-time 30; /* 30 seconds */\n\
|
||||
stale-answer-client-timeout 1800; /* in milliseconds */\n\
|
||||
stale-answer-ttl 30; /* 30 seconds */\n\
|
||||
stale-cache-enable false;\n\
|
||||
stale-refresh-time 30; /* 30 seconds */\n\
|
||||
synth-from-dnssec no;\n\
|
||||
# topology <none>\n\
|
||||
transfer-format many-answers;\n\
|
||||
|
|
|
|||
|
|
@ -403,6 +403,7 @@ OPTIONS
|
|||
sig-validity-interval integer [ integer ];
|
||||
sortlist { address_match_element; ... };
|
||||
stacksize ( default | unlimited | sizeval );
|
||||
stale-answer-client-timeout ( disabled | off | integer );
|
||||
stale-answer-enable boolean;
|
||||
stale-answer-ttl duration;
|
||||
stale-cache-enable boolean;
|
||||
|
|
@ -806,6 +807,7 @@ VIEW
|
|||
sig-signing-type integer;
|
||||
sig-validity-interval integer [ integer ];
|
||||
sortlist { address_match_element; ... };
|
||||
stale-answer-client-timeout ( disabled | off | integer );
|
||||
stale-answer-enable boolean;
|
||||
stale-answer-ttl duration;
|
||||
stale-cache-enable boolean;
|
||||
|
|
|
|||
|
|
@ -4485,6 +4485,23 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config,
|
|||
view->staleanswersok = dns_stale_answer_conf;
|
||||
}
|
||||
|
||||
obj = NULL;
|
||||
result = named_config_get(maps, "stale-answer-client-timeout", &obj);
|
||||
INSIST(result == ISC_R_SUCCESS);
|
||||
if (cfg_obj_isstring(obj)) {
|
||||
/*
|
||||
* The only string values available for this option
|
||||
* are "disabled" and "off".
|
||||
* We use (uint32_t) -1 to represent disabled since
|
||||
* a value of zero means that stale data can be used
|
||||
* to promptly answer the query, while an attempt to
|
||||
* refresh the RRset will still be made in background.
|
||||
*/
|
||||
view->staleanswerclienttimeout = (uint32_t)-1;
|
||||
} else {
|
||||
view->staleanswerclienttimeout = cfg_obj_asuint32(obj);
|
||||
}
|
||||
|
||||
obj = NULL;
|
||||
result = named_config_get(maps, "stale-refresh-time", &obj);
|
||||
INSIST(result == ISC_R_SUCCESS);
|
||||
|
|
@ -4774,6 +4791,27 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config,
|
|||
query_timeout = cfg_obj_asuint32(obj);
|
||||
dns_resolver_settimeout(view->resolver, query_timeout);
|
||||
|
||||
/*
|
||||
* Adjust stale-answer-client-timeout upper bound
|
||||
* to be resolver-query-timeout - 1s.
|
||||
* This assignment is safe as dns_resolver_settimeout()
|
||||
* ensures that resolver->querytimeout value will be in the
|
||||
* [MINIMUM_QUERY_TIMEOUT, MAXIMUM_QUERY_TIMEOUT] range and
|
||||
* MINIMUM_QUERY_TIMEOUT is > 1000 (in ms).
|
||||
*/
|
||||
if (view->staleanswerclienttimeout != (uint32_t)-1 &&
|
||||
view->staleanswerclienttimeout >
|
||||
(dns_resolver_gettimeout(view->resolver) - 1000))
|
||||
{
|
||||
view->staleanswerclienttimeout =
|
||||
dns_resolver_gettimeout(view->resolver) - 1000;
|
||||
isc_log_write(
|
||||
named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
|
||||
NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
|
||||
"stale-answer-client-timeout adjusted to %" PRIu32,
|
||||
view->staleanswerclienttimeout);
|
||||
}
|
||||
|
||||
/* Specify whether to use 0-TTL for negative response for SOA query */
|
||||
dns_resolver_setzeronosoattl(view->resolver, zero_no_soattl);
|
||||
|
||||
|
|
|
|||
|
|
@ -15,3 +15,4 @@ rm -f */named.run */named.memstats
|
|||
rm -f ns*/managed-keys.bind*
|
||||
rm -f ns*/named_dump*
|
||||
rm -f ns*/named.stats*
|
||||
rm -f ns*/named.run.prev
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ options {
|
|||
max-stale-ttl 3600;
|
||||
stale-answer-ttl 4;
|
||||
stale-answer-enable yes;
|
||||
stale-answer-client-timeout disabled;
|
||||
stale-cache-enable yes;
|
||||
stale-refresh-time 30;
|
||||
servfail-ttl 0;
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ options {
|
|||
max-stale-ttl 20;
|
||||
stale-answer-ttl 3;
|
||||
stale-answer-enable yes;
|
||||
stale-answer-client-timeout disabled;
|
||||
stale-cache-enable yes;
|
||||
servfail-ttl 0;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ options {
|
|||
max-stale-ttl 20;
|
||||
stale-answer-ttl 3;
|
||||
stale-answer-enable yes;
|
||||
stale-answer-client-timeout disabled;
|
||||
stale-cache-enable yes;
|
||||
stale-refresh-time 0;
|
||||
servfail-ttl 0;
|
||||
|
|
|
|||
48
bin/tests/system/serve-stale/ns3/named2.conf.in
Normal file
48
bin/tests/system/serve-stale/ns3/named2.conf.in
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
*
|
||||
* 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 http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See the COPYRIGHT file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Test default stale-answer-client-timeout value
|
||||
*/
|
||||
|
||||
key rndc_key {
|
||||
secret "1234abcd8765";
|
||||
algorithm hmac-sha256;
|
||||
};
|
||||
|
||||
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; };
|
||||
dnssec-validation no;
|
||||
recursion yes;
|
||||
stale-answer-enable yes;
|
||||
stale-cache-enable yes;
|
||||
stale-answer-ttl 3;
|
||||
stale-refresh-time 0;
|
||||
max-stale-ttl 3600;
|
||||
resolver-query-timeout 10;
|
||||
};
|
||||
|
||||
zone "." {
|
||||
type secondary;
|
||||
primaries { 10.53.0.1; };
|
||||
file "root.bk";
|
||||
};
|
||||
48
bin/tests/system/serve-stale/ns3/named3.conf.in
Normal file
48
bin/tests/system/serve-stale/ns3/named3.conf.in
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
*
|
||||
* 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 http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See the COPYRIGHT file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Test disable of stale-answer-client-timeout.
|
||||
*/
|
||||
|
||||
key rndc_key {
|
||||
secret "1234abcd8765";
|
||||
algorithm hmac-sha256;
|
||||
};
|
||||
|
||||
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; };
|
||||
dnssec-validation no;
|
||||
recursion yes;
|
||||
stale-answer-enable yes;
|
||||
stale-cache-enable yes;
|
||||
stale-answer-ttl 3;
|
||||
stale-answer-client-timeout off;
|
||||
stale-refresh-time 0;
|
||||
max-stale-ttl 3600;
|
||||
resolver-query-timeout 10;
|
||||
};
|
||||
|
||||
zone "." {
|
||||
type hint;
|
||||
file "root.db";
|
||||
};
|
||||
48
bin/tests/system/serve-stale/ns3/named4.conf.in
Normal file
48
bin/tests/system/serve-stale/ns3/named4.conf.in
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
*
|
||||
* 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 http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See the COPYRIGHT file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Test stale-answer-client-timeout 0.
|
||||
*/
|
||||
|
||||
key rndc_key {
|
||||
secret "1234abcd8765";
|
||||
algorithm hmac-sha256;
|
||||
};
|
||||
|
||||
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; };
|
||||
dnssec-validation no;
|
||||
recursion yes;
|
||||
stale-answer-enable yes;
|
||||
stale-cache-enable yes;
|
||||
stale-answer-ttl 3;
|
||||
stale-answer-client-timeout 0;
|
||||
stale-refresh-time 0;
|
||||
resolver-query-timeout 10;
|
||||
max-stale-ttl 3600;
|
||||
};
|
||||
|
||||
zone "." {
|
||||
type hint;
|
||||
file "root.db";
|
||||
};
|
||||
48
bin/tests/system/serve-stale/ns3/named5.conf.in
Normal file
48
bin/tests/system/serve-stale/ns3/named5.conf.in
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
*
|
||||
* 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 http://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See the COPYRIGHT file distributed with this work for additional
|
||||
* information regarding copyright ownership.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Test stale-answer-client-timeout 0.
|
||||
*/
|
||||
|
||||
key rndc_key {
|
||||
secret "1234abcd8765";
|
||||
algorithm hmac-sha256;
|
||||
};
|
||||
|
||||
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; };
|
||||
dnssec-validation no;
|
||||
recursion yes;
|
||||
stale-answer-enable yes;
|
||||
stale-cache-enable yes;
|
||||
stale-answer-ttl 3;
|
||||
stale-answer-client-timeout 0;
|
||||
stale-refresh-time 4;
|
||||
resolver-query-timeout 10;
|
||||
max-stale-ttl 3600;
|
||||
};
|
||||
|
||||
zone "." {
|
||||
type hint;
|
||||
file "root.db";
|
||||
};
|
||||
11
bin/tests/system/serve-stale/ns3/root.db
Normal file
11
bin/tests/system/serve-stale/ns3/root.db
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
; Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
||||
;
|
||||
; 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 http://mozilla.org/MPL/2.0/.
|
||||
;
|
||||
; See the COPYRIGHT file distributed with this work for additional
|
||||
; information regarding copyright ownership.
|
||||
|
||||
. 300 NS ns.nil.
|
||||
ns.nil. 300 A 10.53.0.1
|
||||
|
|
@ -19,7 +19,6 @@ stale_answer_ttl=$(sed -ne 's,^[[:space:]]*stale-answer-ttl \([[:digit:]]*\).*,\
|
|||
|
||||
status=0
|
||||
n=0
|
||||
|
||||
#
|
||||
# First test server with serve-stale options set.
|
||||
#
|
||||
|
|
@ -188,7 +187,7 @@ if [ $ret != 0 ]; then echo_i "failed"; fi
|
|||
# 2. Disable responses from authoritative server.
|
||||
# 3. Sleep for TTL duration so rrset TTL expires (2 sec)
|
||||
# 4. Query data.example
|
||||
# 5. Check if response come from stale rrset (3 sec TTL)
|
||||
# 5. Check if response come from stale rrset (4 sec TTL)
|
||||
# 6. Enable responses from authoritative server.
|
||||
# 7. Query data.example
|
||||
# 8. Check if response come from stale rrset, since the query
|
||||
|
|
@ -548,8 +547,8 @@ grep "ANSWER: 0," dig.out.test$n > /dev/null || ret=1
|
|||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status+ret))
|
||||
|
||||
# keep track of time so we can access these rrset later,
|
||||
# when we expect them to become ancient.
|
||||
# Keep track of time so we can access these RRset later, when we expect them
|
||||
# to become ancient.
|
||||
t1=`$PERL -e 'print time()'`
|
||||
|
||||
n=$((n+1))
|
||||
|
|
@ -644,16 +643,16 @@ grep "1 #NXDOMAIN" ns1/named.stats.$n.cachedb > /dev/null || ret=1
|
|||
status=$((status+ret))
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
|
||||
# retrieve max-stale-ttl value,
|
||||
# Retrieve max-stale-ttl value.
|
||||
interval_to_ancient=`grep 'max-stale-ttl' ns1/named2.conf.in | awk '{ print $2 }' | tr -d ';'`
|
||||
# we add 2 seconds to it since this is the ttl value of the records being tested.
|
||||
# We add 2 seconds to it since this is the ttl value of the records being
|
||||
# tested.
|
||||
interval_to_ancient=$((interval_to_ancient + 2))
|
||||
t2=`$PERL -e 'print time()'`
|
||||
elapsed=$((t2 - t1))
|
||||
|
||||
# if elapsed time so far is less than max-stale-ttl + 2 seconds,
|
||||
# then we sleep enough to ensure that we'll ask for ancient rrsets
|
||||
# in the next queries.
|
||||
# If elapsed time so far is less than max-stale-ttl + 2 seconds, then we sleep
|
||||
# enough to ensure that we'll ask for ancient RRsets in the next queries.
|
||||
if [ $elapsed -lt $interval_to_ancient ]; then
|
||||
sleep $((interval_to_ancient - elapsed))
|
||||
fi
|
||||
|
|
@ -885,7 +884,7 @@ $DIG -p ${PORT} @10.53.0.1 data.example TXT > dig.out.test$((n+1))
|
|||
|
||||
# Step 8.
|
||||
n=$((n+1))
|
||||
echo_i "check stale data.example comes from authoritative (stale-refresh-time disabled) ($n)"
|
||||
echo_i "check data.example comes from authoritative (stale-refresh-time disabled) ($n)"
|
||||
ret=0
|
||||
grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
|
||||
grep "ANSWER: 1," dig.out.test$n > /dev/null || ret=1
|
||||
|
|
@ -1040,8 +1039,8 @@ $RNDCCMD 10.53.0.3 stats > /dev/null 2>&1
|
|||
[ -f ns3/named.stats ] || ret=1
|
||||
cp ns3/named.stats ns3/named.stats.$n
|
||||
# Check first 10 lines of Cache DB statistics. After last queries, we expect
|
||||
# one active TXT RRset, one stale TXT, one stale nxrrset TXT, and one
|
||||
# stale NXDOMAIN.
|
||||
# one active TXT RRset, one stale TXT, one stale nxrrset TXT, and one stale
|
||||
# NXDOMAIN.
|
||||
grep -A 10 "++ Cache DB RRsets ++" ns3/named.stats.$n > ns3/named.stats.$n.cachedb || ret=1
|
||||
grep "1 TXT" ns3/named.stats.$n.cachedb > /dev/null || ret=1
|
||||
grep "1 #TXT" ns3/named.stats.$n.cachedb > /dev/null || ret=1
|
||||
|
|
@ -1260,8 +1259,8 @@ $RNDCCMD 10.53.0.4 stats > /dev/null 2>&1
|
|||
[ -f ns4/named.stats ] || ret=1
|
||||
cp ns4/named.stats ns4/named.stats.$n
|
||||
# Check first 10 lines of Cache DB statistics. After last queries, we expect
|
||||
# one active TXT RRset, one stale TXT, one stale nxrrset TXT, and one
|
||||
# stale NXDOMAIN.
|
||||
# one active TXT RRset, one stale TXT, one stale nxrrset TXT, and one stale
|
||||
# NXDOMAIN.
|
||||
grep -A 10 "++ Cache DB RRsets ++" ns4/named.stats.$n > ns4/named.stats.$n.cachedb || ret=1
|
||||
grep "1 TXT" ns4/named.stats.$n.cachedb > /dev/null || ret=1
|
||||
grep "1 #TXT" ns4/named.stats.$n.cachedb > /dev/null || ret=1
|
||||
|
|
@ -1282,9 +1281,9 @@ status=$((status+ret))
|
|||
echo_i "stop ns4"
|
||||
$PERL ../stop.pl --use-rndc --port ${CONTROLPORT} serve-stale ns4
|
||||
|
||||
# Load the cache as if it was five minutes (RBTDB_VIRTUAL) older.
|
||||
# Since max-stale-ttl defaults to a week, we need to adjust the date by
|
||||
# one week and five minutes.
|
||||
# Load the cache as if it was five minutes (RBTDB_VIRTUAL) older. Since
|
||||
# max-stale-ttl defaults to a week, we need to adjust the date by one week and
|
||||
# five minutes.
|
||||
LASTWEEK=`TZ=UTC perl -e 'my $now = time();
|
||||
my $oneWeekAgo = $now - 604800;
|
||||
my $fiveMinutesAgo = $oneWeekAgo - 300;
|
||||
|
|
@ -1553,5 +1552,463 @@ grep -F "#NXDOMAIN" ns5/named.stats.$n.cachedb > /dev/null && ret=1
|
|||
status=$((status+ret))
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
|
||||
########################################################
|
||||
# Test for stale-answer-client-timeout (default 1.8s). #
|
||||
########################################################
|
||||
echo_i "test stale-answer-client-timeout (default 1.8)"
|
||||
|
||||
n=$((n+1))
|
||||
echo_i "updating ns3/named.conf ($n)"
|
||||
ret=0
|
||||
copy_setports ns3/named2.conf.in ns3/named.conf
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status+ret))
|
||||
|
||||
echo_i "restart ns3"
|
||||
$PERL ../stop.pl --use-rndc --port ${CONTROLPORT} serve-stale ns3
|
||||
start_server --noclean --restart --port ${PORT} serve-stale ns3
|
||||
|
||||
n=$((n+1))
|
||||
echo_i "check 'rndc serve-stale status' ($n)"
|
||||
ret=0
|
||||
$RNDCCMD 10.53.0.3 serve-stale status > rndc.out.test$n 2>&1 || ret=1
|
||||
grep '_default: on (stale-answer-ttl=3 max-stale-ttl=3600 stale-refresh-time=0)' rndc.out.test$n > /dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status+ret))
|
||||
|
||||
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
|
||||
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 "prime cache data.example (stale-answer-client-timeout)"
|
||||
ret=0
|
||||
$DIG -p ${PORT} @10.53.0.3 data.example TXT > dig.out.test$n
|
||||
grep "status: NOERROR" 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))
|
||||
|
||||
n=$((n+1))
|
||||
echo_i "prime cache nodata.example (stale-answer-client-timeout)"
|
||||
ret=0
|
||||
$DIG -p ${PORT} @10.53.0.3 nodata.example TXT > dig.out.test$n
|
||||
grep "status: NOERROR" 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))
|
||||
|
||||
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
|
||||
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))
|
||||
|
||||
# Allow RRset to become stale.
|
||||
sleep 2
|
||||
|
||||
# We configured a long value of 30 seconds for resolver-query-timeout.
|
||||
# That should give us enough time to receive an stale answer from cache
|
||||
# after stale-answer-client-timeout timer of 1.8 sec triggers.
|
||||
n=$((n+1))
|
||||
echo_i "check stale data.example comes from cache (default stale-answer-client-timeout) ($n)"
|
||||
nextpart ns3/named.run > /dev/null
|
||||
t1=`$PERL -e 'print time()'`
|
||||
$DIG -p ${PORT} +tries=1 +timeout=10 @10.53.0.3 data.example TXT > dig.out.test$n
|
||||
t2=`$PERL -e 'print time()'`
|
||||
wait_for_log 5 "data.example client timeout, stale answer used" ns3/named.run || ret=1
|
||||
ret=0
|
||||
grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
|
||||
grep "ANSWER: 1," dig.out.test$n > /dev/null || ret=1
|
||||
grep "data\.example\..*3.*IN.*TXT.*A text record with a 2 second ttl" dig.out.test$n > /dev/null || ret=1
|
||||
# Default stale-answer-client-timeout is 1.8s, we allow some extra time
|
||||
# just in case other tests are taking too much cpu.
|
||||
[ $((t2 - t1)) -le 10 ] || { echo_i "query took $((t2 - t1))s to resolve."; ret=1; }
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status+ret))
|
||||
|
||||
echo_i "sending queries for tests $((n+1))-$((n+2))..."
|
||||
$DIG -p ${PORT} +tries=1 +timeout=3 @10.53.0.3 nodata.example TXT > dig.out.test$((n+1)) &
|
||||
$DIG -p ${PORT} +tries=1 +timeout=30 @10.53.0.3 nodata.example TXT > dig.out.test$((n+2))
|
||||
wait
|
||||
|
||||
# Since nodata.example is cached as NXRRSET and marked as stale at this point,
|
||||
# BIND must not return this RRset when stale-answer-client-timeout triggers,
|
||||
# instead, it must attempt to refresh the RRset. Since the authoritative
|
||||
# server is disabled and we are using resolver-query-timeout value of 10
|
||||
# seconds, we expect this query with a timeout of 3 seconds to time out.
|
||||
n=$((n+1))
|
||||
echo_i "check query for nodata.example times out (default stale-answer-client-timeout) ($n)"
|
||||
grep "connection timed out" dig.out.test$n > /dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status+ret))
|
||||
|
||||
# For this query we expect BIND to return stale NXRRSET data for
|
||||
# nodata.example after resolver-query-timeout expires.
|
||||
n=$((n+1))
|
||||
echo_i "check stale nodata.example comes from cache after resolver-query-timeout expires (default stale-answer-client-timeout) ($n)"
|
||||
grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
|
||||
grep "ANSWER: 0," dig.out.test$n > /dev/null || ret=1
|
||||
grep "example\..*3.*IN.*SOA" dig.out.test$n > /dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status+ret))
|
||||
|
||||
#############################################
|
||||
# Test for stale-answer-client-timeout off. #
|
||||
#############################################
|
||||
echo_i "test stale-answer-client-timeout (off)"
|
||||
|
||||
n=$((n+1))
|
||||
echo_i "updating ns3/named.conf ($n)"
|
||||
ret=0
|
||||
copy_setports ns3/named3.conf.in ns3/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 ns3 10.53.0.3
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status+ret))
|
||||
|
||||
# Send a query, auth server is disabled, we will enable it after a while in
|
||||
# order to receive an answer before resolver-query-timeout expires. Since
|
||||
# stale-answer-client-timeout is disabled we must receive an answer from
|
||||
# authoritative server.
|
||||
echo_i "sending query for test $((n+2))"
|
||||
$DIG -p ${PORT} @10.53.0.3 data.example TXT > dig.out.test$((n+2)) &
|
||||
sleep 3
|
||||
|
||||
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
|
||||
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))
|
||||
|
||||
# Wait until dig is done.
|
||||
wait
|
||||
|
||||
n=$((n+1))
|
||||
echo_i "check data.example comes from authoritative server (stale-answer-client-timeout off) ($n)"
|
||||
grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
|
||||
grep "ANSWER: 1," dig.out.test$n > /dev/null || ret=1
|
||||
grep "data\.example\..*[12].*IN.*TXT.*A text record with a 2 second ttl" dig.out.test$n > /dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status+ret))
|
||||
|
||||
#############################################
|
||||
# Test for stale-answer-client-timeout 0. #
|
||||
#############################################
|
||||
echo_i "test stale-answer-client-timeout (0)"
|
||||
|
||||
n=$((n+1))
|
||||
echo_i "updating ns3/named.conf ($n)"
|
||||
ret=0
|
||||
copy_setports ns3/named4.conf.in ns3/named.conf
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status+ret))
|
||||
|
||||
echo_i "restart ns3"
|
||||
$PERL ../stop.pl --use-rndc --port ${CONTROLPORT} serve-stale ns3
|
||||
start_server --noclean --restart --port ${PORT} serve-stale ns3
|
||||
|
||||
n=$((n+1))
|
||||
echo_i "prime cache data.example (stale-answer-client-timeout 0)"
|
||||
ret=0
|
||||
$DIG -p ${PORT} @10.53.0.3 data.example TXT > dig.out.test$n
|
||||
grep "status: NOERROR" 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))
|
||||
|
||||
n=$((n+1))
|
||||
echo_i "prime cache nodata.example (stale-answer-client-timeout 0)"
|
||||
ret=0
|
||||
$DIG -p ${PORT} @10.53.0.3 nodata.example TXT > dig.out.test$n
|
||||
grep "status: NOERROR" 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))
|
||||
|
||||
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
|
||||
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))
|
||||
|
||||
# Allow RRset to become stale.
|
||||
sleep 2
|
||||
|
||||
n=$((n+1))
|
||||
ret=0
|
||||
echo_i "check stale data.example comes from cache (stale-answer-client-timeout 0) ($n)"
|
||||
nextpart ns3/named.run > /dev/null
|
||||
$DIG -p ${PORT} @10.53.0.3 data.example TXT > dig.out.test$n
|
||||
wait_for_log 5 "data.example stale answer used, an attempt to refresh the RRset" ns3/named.run || 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 "data\.example\..*3.*IN.*TXT.*A text record with a 2 second ttl" dig.out.test$n > /dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status+ret))
|
||||
|
||||
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
|
||||
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))
|
||||
|
||||
wait_for_rrset_refresh() {
|
||||
nextpart ns3/named.run | grep 'data.example.*2.*TXT.*"A text record with a 2 second ttl"' > /dev/null && return 0
|
||||
return 1
|
||||
}
|
||||
|
||||
# This test ensures that after we get stale data due to
|
||||
# stale-answer-client-timeout 0, enabling the authoritative server will allow
|
||||
# the RRset to be updated.
|
||||
n=$((n+1))
|
||||
ret=0
|
||||
echo_i "check stale data.example was refreshed (stale-answer-client-timeout 0) ($n)"
|
||||
retry_quiet 10 wait_for_rrset_refresh || ret=1
|
||||
$DIG -p ${PORT} @10.53.0.3 data.example TXT > dig.out.test$n
|
||||
grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
|
||||
grep "ANSWER: 1," dig.out.test$n > /dev/null || ret=1
|
||||
grep "data\.example\..*[12].*IN.*TXT.*A text record with a 2 second ttl" dig.out.test$n > /dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status+ret))
|
||||
|
||||
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
|
||||
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))
|
||||
|
||||
echo_i "sending queries for tests $((n+1))-$((n+2))..."
|
||||
$DIG -p ${PORT} +tries=1 +timeout=3 @10.53.0.3 nodata.example TXT > dig.out.test$((n+1)) &
|
||||
$DIG -p ${PORT} +tries=1 +timeout=30 @10.53.0.3 nodata.example TXT > dig.out.test$((n+2))
|
||||
wait
|
||||
|
||||
# Since nodata.example is cached as NXRRSET and marked as stale at this point,
|
||||
# BIND must not prompty return this RRset due to
|
||||
# stale-answer-client-timeout == 0, instead, it must attempt to refresh the
|
||||
# RRset. Since the authoritative server is disabled and we are using
|
||||
# resolver-query-timeout value of 10 seconds, we expect this query with a
|
||||
# timeout of 3 seconds to time out.
|
||||
n=$((n+1))
|
||||
echo_i "check query for nodata.example times out (stale-answer-client-timeout 0) ($n)"
|
||||
grep "connection timed out" dig.out.test$n > /dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status+ret))
|
||||
|
||||
# For this query we expect BIND to return stale NXRRSET data for
|
||||
# nodata.example after resolver-query-timeout expires.
|
||||
n=$((n+1))
|
||||
echo_i "check stale nodata.example comes from cache after resolver-query-timeout expires (stale-answer-client-timeout 0) ($n)"
|
||||
grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
|
||||
grep "ANSWER: 0," dig.out.test$n > /dev/null || ret=1
|
||||
grep "example\..*3.*IN.*SOA" dig.out.test$n > /dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status+ret))
|
||||
|
||||
####################################################################
|
||||
# Test for stale-answer-client-timeout 0 and stale-refresh-time 4. #
|
||||
####################################################################
|
||||
echo_i "test stale-answer-client-timeout (0) and stale-refresh-time (4)"
|
||||
|
||||
n=$((n+1))
|
||||
echo_i "updating ns3/named.conf ($n)"
|
||||
ret=0
|
||||
copy_setports ns3/named5.conf.in ns3/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 ns3 10.53.0.3
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status+ret))
|
||||
|
||||
n=$((n+1))
|
||||
echo_i "flush cache, enable responses from authoritative server ($n)"
|
||||
ret=0
|
||||
$RNDCCMD 10.53.0.3 flushtree example > rndc.out.test$n.1 2>&1 || ret=1
|
||||
$DIG -p ${PORT} @10.53.0.2 txt enable > dig.out.test$n
|
||||
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 "prime cache data.example (stale-answer-client-timeout 0, stale-refresh-time 4) ($n)"
|
||||
ret=0
|
||||
$DIG -p ${PORT} @10.53.0.3 data.example TXT > dig.out.test$n
|
||||
grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
|
||||
grep "ANSWER: 1," dig.out.test$n > /dev/null || ret=1
|
||||
grep "data\.example\..*2.*IN.*TXT.*A text record with a 2 second ttl" dig.out.test$n > /dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status+ret))
|
||||
|
||||
# Allow RRset to become stale.
|
||||
sleep 2
|
||||
|
||||
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
|
||||
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))
|
||||
|
||||
n=$((n+1))
|
||||
ret=0
|
||||
echo_i "check stale data.example comes from cache (stale-answer-client-timeout 0 stale-refresh-time 4) ($n)"
|
||||
nextpart ns3/named.run > /dev/null
|
||||
$DIG -p ${PORT} @10.53.0.3 data.example TXT > dig.out.test$n
|
||||
wait_for_log 5 "data.example stale answer used, an attempt to refresh the RRset" ns3/named.run || 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 "data\.example\..*3.*IN.*TXT.*A text record with a 2 second ttl" dig.out.test$n > /dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status+ret))
|
||||
|
||||
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
|
||||
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))
|
||||
|
||||
# This test ensures that after we get stale data due to
|
||||
# stale-answer-client-timeout 0, enabling the authoritative server will allow
|
||||
# the RRset to be updated.
|
||||
n=$((n+1))
|
||||
ret=0
|
||||
echo_i "check stale data.example was refreshed (stale-answer-client-timeout 0 stale-refresh-time 4) ($n)"
|
||||
retry_quiet 10 wait_for_rrset_refresh || ret=1
|
||||
$DIG -p ${PORT} @10.53.0.3 data.example TXT > dig.out.test$n
|
||||
grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
|
||||
grep "ANSWER: 1," dig.out.test$n > /dev/null || ret=1
|
||||
grep "data\.example\..*[12].*IN.*TXT.*A text record with a 2 second ttl" dig.out.test$n > /dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status+ret))
|
||||
|
||||
# Allow RRset to become stale.
|
||||
sleep 2
|
||||
|
||||
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
|
||||
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))
|
||||
|
||||
n=$((n+1))
|
||||
ret=0
|
||||
echo_i "check stale data.example comes from cache (stale-answer-client-timeout 0 stale-refresh-time 4) ($n)"
|
||||
nextpart ns3/named.run > /dev/null
|
||||
$DIG -p ${PORT} @10.53.0.3 data.example TXT > dig.out.test$n
|
||||
wait_for_log 5 "data.example stale answer used, an attempt to refresh the RRset" ns3/named.run || 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 "data\.example\..*3.*IN.*TXT.*A text record with a 2 second ttl" dig.out.test$n > /dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status+ret))
|
||||
|
||||
# Allow stale-refresh-time to be activated.
|
||||
n=$((n+1))
|
||||
ret=0
|
||||
echo_i "wait until resolver query times out, activating stale-refresh-time"
|
||||
wait_for_log 15 "data.example resolver failure, stale answer used" ns3/named.run || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status+ret))
|
||||
|
||||
n=$((n+1))
|
||||
ret=0
|
||||
echo_i "check stale data.example comes from cache within stale-refresh-time (stale-answer-client-timeout 0 stale-refresh-time 4) ($n)"
|
||||
nextpart ns3/named.run > /dev/null
|
||||
$DIG -p ${PORT} @10.53.0.3 data.example TXT > dig.out.test$n
|
||||
wait_for_log 5 "data.example query within stale refresh time" ns3/named.run || 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 "data\.example\..*3.*IN.*TXT.*A text record with a 2 second ttl" dig.out.test$n > /dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status+ret))
|
||||
|
||||
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
|
||||
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))
|
||||
|
||||
# We give BIND some time to ensure that after we enable authoritative server,
|
||||
# this RRset is still not refreshed because it was hit during
|
||||
# stale-refresh-time window.
|
||||
sleep 1
|
||||
|
||||
n=$((n+1))
|
||||
ret=0
|
||||
echo_i "check stale data.example was not refreshed (stale-answer-client-timeout 0 stale-refresh-time 4) ($n)"
|
||||
nextpart ns3/named.run > /dev/null
|
||||
$DIG -p ${PORT} @10.53.0.3 data.example TXT > dig.out.test$n
|
||||
wait_for_log 5 "data.example query within stale refresh time" ns3/named.run || 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 "data\.example\..*3.*IN.*TXT.*A text record with a 2 second ttl" dig.out.test$n > /dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status+ret))
|
||||
|
||||
# After the refresh-time-window, the RRset will be refreshed.
|
||||
sleep 4
|
||||
|
||||
n=$((n+1))
|
||||
ret=0
|
||||
echo_i "check stale data.example comes from cache (stale-answer-client-timeout 0 stale-refresh-time 4) ($n)"
|
||||
$DIG -p ${PORT} @10.53.0.3 data.example TXT > dig.out.test$n
|
||||
wait_for_log 5 "data.example stale answer used, an attempt to refresh the RRset" ns3/named.run || 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 "data\.example\..*3.*IN.*TXT.*A text record with a 2 second ttl" dig.out.test$n > /dev/null || ret=1
|
||||
if [ $ret != 0 ]; then echo_i "failed"; fi
|
||||
status=$((status+ret))
|
||||
|
||||
n=$((n+1))
|
||||
ret=0
|
||||
echo_i "check stale data.example was refreshed (stale-answer-client-timeout 0 stale-refresh-time 4) ($n)"
|
||||
$DIG -p ${PORT} @10.53.0.3 data.example TXT > dig.out.test$n
|
||||
grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
|
||||
grep "ANSWER: 1," dig.out.test$n > /dev/null || ret=1
|
||||
grep "data\.example\..*[12].*IN.*TXT.*A text record with a 2 second ttl" dig.out.test$n > /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
|
||||
|
|
|
|||
|
|
@ -1832,6 +1832,20 @@ Boolean Options
|
|||
Information about stale answers is logged under the ``serve-stale``
|
||||
log category.
|
||||
|
||||
``stale-answer-client-timeout``
|
||||
This option defines the amount of time ``named`` waits before attempting to
|
||||
answer the query with a stale RRset from cache. If a stale answer is found,
|
||||
``named`` continues the ongoing fetches, attempting to refresh the RRset in
|
||||
cache until the ``resolver-query-timeout`` interval is reached.
|
||||
|
||||
The default value is ``1800`` (in milliseconds) and the maximum value is
|
||||
bounded to ``resolver-query-timeout`` minus one second. A value of ``0``
|
||||
immediately returns a cached RRset if available, and still attempts a refresh
|
||||
of the data in cache.
|
||||
|
||||
The option can be disabled by setting the value to ``off`` or ``disabled``.
|
||||
It also has no effect if ``stale-answer-enable`` is disabled.
|
||||
|
||||
``stale-cache-enable``
|
||||
If ``yes``, enable the retaining of "stale" cached answers. Default ``no``.
|
||||
|
||||
|
|
|
|||
|
|
@ -466,6 +466,7 @@ options {
|
|||
sig\-validity\-interval integer [ integer ];
|
||||
sortlist { address_match_element; ... };
|
||||
stacksize ( default | unlimited | sizeval );
|
||||
stale\-answer\-client\-timeout ( disabled | off | integer );
|
||||
stale\-answer\-enable boolean;
|
||||
stale\-answer\-ttl duration;
|
||||
stale\-cache\-enable boolean;
|
||||
|
|
@ -901,6 +902,7 @@ view string [ class ] {
|
|||
sig\-signing\-type integer;
|
||||
sig\-validity\-interval integer [ integer ];
|
||||
sortlist { address_match_element; ... };
|
||||
stale\-answer\-client\-timeout ( disabled | off | integer );
|
||||
stale\-answer\-enable boolean;
|
||||
stale\-answer\-ttl duration;
|
||||
stale\-cache\-enable boolean;
|
||||
|
|
|
|||
|
|
@ -333,6 +333,7 @@ options {
|
|||
sig-validity-interval <integer> [ <integer> ];
|
||||
sortlist { <address_match_element>; ... };
|
||||
stacksize ( default | unlimited | <sizeval> );
|
||||
stale-answer-client-timeout ( disabled | off | <integer> );
|
||||
stale-answer-enable <boolean>;
|
||||
stale-answer-ttl <duration>;
|
||||
stale-cache-enable <boolean>;
|
||||
|
|
@ -696,6 +697,7 @@ view <string> [ <class> ] {
|
|||
sig-signing-type <integer>;
|
||||
sig-validity-interval <integer> [ <integer> ];
|
||||
sortlist { <address_match_element>; ... };
|
||||
stale-answer-client-timeout ( disabled | off | <integer> );
|
||||
stale-answer-enable <boolean>;
|
||||
stale-answer-ttl <duration>;
|
||||
stale-cache-enable <boolean>;
|
||||
|
|
|
|||
|
|
@ -331,6 +331,7 @@ options {
|
|||
sig-validity-interval <integer> [ <integer> ];
|
||||
sortlist { <address_match_element>; ... };
|
||||
stacksize ( default | unlimited | <sizeval> );
|
||||
stale-answer-client-timeout ( disabled | off | <integer> );
|
||||
stale-answer-enable <boolean>;
|
||||
stale-answer-ttl <duration>;
|
||||
stale-cache-enable <boolean>;
|
||||
|
|
@ -692,6 +693,7 @@ view <string> [ <class> ] {
|
|||
sig-signing-type <integer>;
|
||||
sig-validity-interval <integer> [ <integer> ];
|
||||
sortlist { <address_match_element>; ... };
|
||||
stale-answer-client-timeout ( disabled | off | <integer> );
|
||||
stale-answer-enable <boolean>;
|
||||
stale-answer-ttl <duration>;
|
||||
stale-cache-enable <boolean>;
|
||||
|
|
|
|||
|
|
@ -259,6 +259,7 @@
|
|||
sig-validity-interval <integer> [ <integer> ];
|
||||
sortlist { <address_match_element>; ... };
|
||||
stacksize ( default | unlimited | <sizeval> );
|
||||
stale-answer-client-timeout ( disabled | off | <integer> );
|
||||
stale-answer-enable <boolean>;
|
||||
stale-answer-ttl <duration>;
|
||||
stale-cache-enable <boolean>;
|
||||
|
|
|
|||
|
|
@ -26,6 +26,23 @@ New Features
|
|||
|
||||
- None.
|
||||
|
||||
- A new option, ```stale-answer-client-timeout``, has been added to
|
||||
improve ``named``'s behavior with respect to serving stale data. The option
|
||||
defines the amount of time ``named`` waits before attempting
|
||||
to answer the query with a stale RRset from cache. If a stale answer
|
||||
is found, ``named`` continues the ongoing fetches, attempting to
|
||||
refresh the RRset in cache until the ``resolver-query-timeout`` interval is
|
||||
reached.
|
||||
|
||||
The default value is ``1800`` (in milliseconds) and the maximum value is
|
||||
bounded to ``resolver-query-timeout`` minus one second. A value of
|
||||
``0`` immediately returns a cached RRset if available, and still
|
||||
attempts a refresh of the data in cache.
|
||||
|
||||
The option can be disabled by setting the value to ``off`` or
|
||||
``disabled``. It also has no effect if ``stale-answer-enable`` is
|
||||
disabled.
|
||||
|
||||
Removed Features
|
||||
~~~~~~~~~~~~~~~~
|
||||
|
||||
|
|
|
|||
|
|
@ -239,8 +239,35 @@ struct dns_dbonupdatelistener {
|
|||
#define DNS_DBFIND_FORCENSEC3 0x0080
|
||||
#define DNS_DBFIND_ADDITIONALOK 0x0100
|
||||
#define DNS_DBFIND_NOZONECUT 0x0200
|
||||
#define DNS_DBFIND_STALEOK 0x0400
|
||||
|
||||
/*
|
||||
* DNS_DBFIND_STALEOK: This flag is set when BIND fails to refresh a
|
||||
* RRset due to timeout (resolver-query-timeout), its intent is to
|
||||
* try to look for stale data in cache as a fallback, but only if
|
||||
* stale answers are enabled in configuration.
|
||||
*
|
||||
* This flag is also used to activate stale-refresh-time window, since it
|
||||
* is the only way the database knows that a resolution has failed.
|
||||
*/
|
||||
#define DNS_DBFIND_STALEOK 0x0400
|
||||
|
||||
/*
|
||||
* DNS_DBFIND_STALEENABLED: This flag is used as a hint to the database
|
||||
* that it may use stale data. It is always set during query lookup if
|
||||
* stale answers are enabled, but only effectively used during
|
||||
* stale-refresh-time window. Also during this window, the resolver will
|
||||
* not try to resolve the query, in other words no attempt to refresh the
|
||||
* data in cache is made when the stale-refresh-time window is active.
|
||||
*/
|
||||
#define DNS_DBFIND_STALEENABLED 0x0800
|
||||
|
||||
/*
|
||||
* DNS_DBFIND_STALEONLY: This new introduced flag is used when we want
|
||||
* stale data from the database, but not due to a failure in resolution,
|
||||
* it also doesn't require stale-refresh-time window timer to be active.
|
||||
* As long as there is a stale RRset available, it should be returned.
|
||||
*/
|
||||
#define DNS_DBFIND_STALEONLY 0x1000
|
||||
/*@}*/
|
||||
|
||||
/*@{*/
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@
|
|||
#define DNS_EVENT_CATZDELZONE (ISC_EVENTCLASS_DNS + 56)
|
||||
#define DNS_EVENT_RPZUPDATED (ISC_EVENTCLASS_DNS + 57)
|
||||
#define DNS_EVENT_STARTUPDATE (ISC_EVENTCLASS_DNS + 58)
|
||||
#define DNS_EVENT_TRYSTALE (ISC_EVENTCLASS_DNS + 59)
|
||||
|
||||
#define DNS_EVENT_FIRSTEVENT (ISC_EVENTCLASS_DNS + 0)
|
||||
#define DNS_EVENT_LASTEVENT (ISC_EVENTCLASS_DNS + 65535)
|
||||
|
|
|
|||
|
|
@ -189,6 +189,7 @@ struct dns_rdataset {
|
|||
#define DNS_RDATASETATTR_CYCLIC 0x00800000 /*%< Cyclic ordering. */
|
||||
#define DNS_RDATASETATTR_STALE 0x01000000
|
||||
#define DNS_RDATASETATTR_ANCIENT 0x02000000
|
||||
#define DNS_RDATASETATTR_STALE_WINDOW 0x04000000
|
||||
|
||||
/*%
|
||||
* _OMITDNSSEC:
|
||||
|
|
|
|||
|
|
@ -129,9 +129,10 @@ typedef enum { dns_quotatype_zone = 0, dns_quotatype_server } dns_quotatype_t;
|
|||
* if possible. */
|
||||
|
||||
/* Reserved in use by adb.c 0x00400000 */
|
||||
#define DNS_FETCHOPT_EDNSVERSIONSET 0x00800000
|
||||
#define DNS_FETCHOPT_EDNSVERSIONMASK 0xff000000
|
||||
#define DNS_FETCHOPT_EDNSVERSIONSHIFT 24
|
||||
#define DNS_FETCHOPT_EDNSVERSIONSET 0x00800000
|
||||
#define DNS_FETCHOPT_EDNSVERSIONMASK 0xff000000
|
||||
#define DNS_FETCHOPT_EDNSVERSIONSHIFT 24
|
||||
#define DNS_FETCHOPT_TRYSTALE_ONTIMEOUT 0x01000000
|
||||
|
||||
/*
|
||||
* Upper bounds of class of query RTT (ms). Corresponds to
|
||||
|
|
|
|||
|
|
@ -173,6 +173,7 @@ struct dns_view {
|
|||
dns_stale_answer_t staleanswersok; /* rndc setting */
|
||||
bool staleanswersenable; /* named.conf setting
|
||||
* */
|
||||
uint32_t staleanswerclienttimeout;
|
||||
uint16_t nocookieudp;
|
||||
uint16_t padding;
|
||||
dns_acl_t * pad_acl;
|
||||
|
|
@ -1343,6 +1344,15 @@ dns_view_setviewrevert(dns_view_t *view);
|
|||
*\li 'view' to be valid.
|
||||
*/
|
||||
|
||||
bool
|
||||
dns_view_staleanswerenabled(dns_view_t *view);
|
||||
/*%<
|
||||
* Check if stale answers are enabled for this view.
|
||||
*
|
||||
* Requires:
|
||||
*\li 'view' to be valid.
|
||||
*/
|
||||
|
||||
ISC_LANG_ENDDECLS
|
||||
|
||||
#endif /* DNS_VIEW_H */
|
||||
|
|
|
|||
|
|
@ -273,7 +273,8 @@ typedef ISC_LIST(dns_rbtnode_t) rbtnodelist_t;
|
|||
#define RDATASET_ATTR_ZEROTTL 0x0800
|
||||
#define RDATASET_ATTR_CASEFULLYLOWER 0x1000
|
||||
/*%< Ancient - awaiting cleanup. */
|
||||
#define RDATASET_ATTR_ANCIENT 0x2000
|
||||
#define RDATASET_ATTR_ANCIENT 0x2000
|
||||
#define RDATASET_ATTR_STALE_WINDOW 0x4000
|
||||
|
||||
/*
|
||||
* XXX
|
||||
|
|
@ -303,6 +304,9 @@ typedef ISC_LIST(dns_rbtnode_t) rbtnodelist_t;
|
|||
#define STALE(header) \
|
||||
((atomic_load_acquire(&(header)->attributes) & RDATASET_ATTR_STALE) != \
|
||||
0)
|
||||
#define STALE_WINDOW(header) \
|
||||
((atomic_load_acquire(&(header)->attributes) & \
|
||||
RDATASET_ATTR_STALE_WINDOW) != 0)
|
||||
#define RESIGN(header) \
|
||||
((atomic_load_acquire(&(header)->attributes) & \
|
||||
RDATASET_ATTR_RESIGN) != 0)
|
||||
|
|
@ -3148,6 +3152,9 @@ bind_rdataset(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, rdatasetheader_t *header,
|
|||
rdataset->attributes |= DNS_RDATASETATTR_PREFETCH;
|
||||
}
|
||||
if (STALE(header)) {
|
||||
if (STALE_WINDOW(header)) {
|
||||
rdataset->attributes |= DNS_RDATASETATTR_STALE_WINDOW;
|
||||
}
|
||||
rdataset->attributes |= DNS_RDATASETATTR_STALE;
|
||||
rdataset->stale_ttl =
|
||||
(rbtdb->serve_stale_ttl + header->rdh_ttl) - now;
|
||||
|
|
@ -4551,6 +4558,8 @@ check_stale_header(dns_rbtnode_t *node, rdatasetheader_t *header,
|
|||
* skip this record. We skip the records with ZEROTTL
|
||||
* (these records should not be cached anyway).
|
||||
*/
|
||||
|
||||
RDATASET_ATTR_CLR(header, RDATASET_ATTR_STALE_WINDOW);
|
||||
if (!ZEROTTL(header) && KEEPSTALE(search->rbtdb) &&
|
||||
stale > search->now) {
|
||||
mark_header_stale(search->rbtdb, header);
|
||||
|
|
@ -4574,6 +4583,15 @@ check_stale_header(dns_rbtnode_t *node, rdatasetheader_t *header,
|
|||
* then don't skip this stale entry but use it
|
||||
* instead.
|
||||
*/
|
||||
RDATASET_ATTR_SET(header,
|
||||
RDATASET_ATTR_STALE_WINDOW);
|
||||
return (false);
|
||||
} else if ((search->options & DNS_DBFIND_STALEONLY) !=
|
||||
0) {
|
||||
/*
|
||||
* We want stale RRset only, so we don't skip
|
||||
* it.
|
||||
*/
|
||||
return (false);
|
||||
}
|
||||
return ((search->options & DNS_DBFIND_STALEOK) == 0);
|
||||
|
|
|
|||
|
|
@ -307,7 +307,9 @@ struct fetchctx {
|
|||
dns_rdataset_t nameservers;
|
||||
atomic_uint_fast32_t attributes;
|
||||
isc_timer_t *timer;
|
||||
isc_timer_t *timer_try_stale;
|
||||
isc_time_t expires;
|
||||
isc_time_t expires_try_stale;
|
||||
isc_interval_t interval;
|
||||
dns_message_t *qmessage;
|
||||
ISC_LIST(resquery_t) queries;
|
||||
|
|
@ -1135,6 +1137,16 @@ fctx_starttimer(fetchctx_t *fctx) {
|
|||
NULL, true));
|
||||
}
|
||||
|
||||
static inline isc_result_t
|
||||
fctx_starttimer_trystale(fetchctx_t *fctx) {
|
||||
/*
|
||||
* Start the stale-answer-client-timeout timer for fctx.
|
||||
*/
|
||||
|
||||
return (isc_timer_reset(fctx->timer_try_stale, isc_timertype_once,
|
||||
&fctx->expires_try_stale, NULL, true));
|
||||
}
|
||||
|
||||
static inline void
|
||||
fctx_stoptimer(fetchctx_t *fctx) {
|
||||
isc_result_t result;
|
||||
|
|
@ -1153,6 +1165,22 @@ fctx_stoptimer(fetchctx_t *fctx) {
|
|||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
fctx_stoptimer_trystale(fetchctx_t *fctx) {
|
||||
isc_result_t result;
|
||||
|
||||
if (fctx->timer_try_stale != NULL) {
|
||||
result = isc_timer_reset(fctx->timer_try_stale,
|
||||
isc_timertype_inactive, NULL, NULL,
|
||||
true);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
UNEXPECTED_ERROR(__FILE__, __LINE__,
|
||||
"isc_timer_reset(): %s",
|
||||
isc_result_totext(result));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline isc_result_t
|
||||
fctx_startidletimer(fetchctx_t *fctx, isc_interval_t *interval) {
|
||||
/*
|
||||
|
|
@ -1537,6 +1565,7 @@ fctx_stopqueries(fetchctx_t *fctx, bool no_response, bool age_untried) {
|
|||
FCTXTRACE("stopqueries");
|
||||
fctx_cancelqueries(fctx, no_response, age_untried);
|
||||
fctx_stoptimer(fctx);
|
||||
fctx_stoptimer_trystale(fctx);
|
||||
}
|
||||
|
||||
static inline void
|
||||
|
|
@ -1697,6 +1726,16 @@ fctx_sendevents(fetchctx_t *fctx, isc_result_t result, int line) {
|
|||
event = next_event) {
|
||||
next_event = ISC_LIST_NEXT(event, ev_link);
|
||||
ISC_LIST_UNLINK(fctx->events, event, ev_link);
|
||||
if (event->ev_type == DNS_EVENT_TRYSTALE) {
|
||||
/*
|
||||
* Not applicable to TRY STALE events, this function is
|
||||
* called when the fetch has either completed or timed
|
||||
* out due to resolver-query-timeout being reached.
|
||||
*/
|
||||
isc_task_detach((isc_task_t **)&event->ev_sender);
|
||||
isc_event_free((isc_event_t **)&event);
|
||||
continue;
|
||||
}
|
||||
task = event->ev_sender;
|
||||
event->ev_sender = fctx;
|
||||
event->vresult = fctx->vresult;
|
||||
|
|
@ -4182,7 +4221,13 @@ fctx_try(fetchctx_t *fctx, bool retrying, bool badcache) {
|
|||
*/
|
||||
if (fctx->minimized && !fctx->forwarding) {
|
||||
unsigned int options = fctx->options;
|
||||
options &= ~DNS_FETCHOPT_QMINIMIZE;
|
||||
/*
|
||||
* Also clear DNS_FETCHOPT_TRYSTALE_ONTIMEOUT here, otherwise
|
||||
* every query minimization step will activate the try-stale
|
||||
* timer again.
|
||||
*/
|
||||
options &= ~(DNS_FETCHOPT_QMINIMIZE |
|
||||
DNS_FETCHOPT_TRYSTALE_ONTIMEOUT);
|
||||
|
||||
/*
|
||||
* Is another QNAME minimization fetch still running?
|
||||
|
|
@ -4222,6 +4267,7 @@ fctx_try(fetchctx_t *fctx, bool retrying, bool badcache) {
|
|||
fctx_increference(fctx);
|
||||
task = res->buckets[bucketnum].task;
|
||||
fctx_stoptimer(fctx);
|
||||
fctx_stoptimer_trystale(fctx);
|
||||
result = dns_resolver_createfetch(
|
||||
fctx->res, &fctx->qminname, fctx->qmintype,
|
||||
&fctx->domain, &fctx->nameservers, NULL, NULL, 0,
|
||||
|
|
@ -4247,6 +4293,7 @@ fctx_try(fetchctx_t *fctx, bool retrying, bool badcache) {
|
|||
}
|
||||
|
||||
fctx_increference(fctx);
|
||||
|
||||
result = fctx_query(fctx, addrinfo, fctx->options);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
fctx_done(fctx, result, __LINE__);
|
||||
|
|
@ -4504,6 +4551,9 @@ fctx_destroy(fetchctx_t *fctx) {
|
|||
isc_counter_detach(&fctx->qc);
|
||||
fcount_decr(fctx);
|
||||
isc_timer_detach(&fctx->timer);
|
||||
if (fctx->timer_try_stale != NULL) {
|
||||
isc_timer_detach(&fctx->timer_try_stale);
|
||||
}
|
||||
dns_message_detach(&fctx->qmessage);
|
||||
if (dns_name_countlabels(&fctx->domain) > 0) {
|
||||
dns_name_free(&fctx->domain, fctx->mctx);
|
||||
|
|
@ -4579,6 +4629,57 @@ fctx_timeout(isc_task_t *task, isc_event_t *event) {
|
|||
isc_event_free(&event);
|
||||
}
|
||||
|
||||
/*
|
||||
* Fetch event handlers called if stale answers are enabled
|
||||
* (stale-answer-enabled) and the fetch took more than
|
||||
* stale-answer-client-timeout to complete.
|
||||
*/
|
||||
static void
|
||||
fctx_timeout_try_stale(isc_task_t *task, isc_event_t *event) {
|
||||
fetchctx_t *fctx = event->ev_arg;
|
||||
dns_fetchevent_t *dns_event, *next_event;
|
||||
isc_task_t *sender_task;
|
||||
unsigned int count = 0;
|
||||
|
||||
REQUIRE(VALID_FCTX(fctx));
|
||||
|
||||
UNUSED(task);
|
||||
|
||||
FCTXTRACE("timeout_try_stale");
|
||||
|
||||
if (event->ev_type != ISC_TIMEREVENT_LIFE) {
|
||||
return;
|
||||
}
|
||||
|
||||
LOCK(&fctx->res->buckets[fctx->bucketnum].lock);
|
||||
|
||||
/*
|
||||
* Trigger events of type DNS_EVENT_TRYSTALE.
|
||||
*/
|
||||
for (dns_event = ISC_LIST_HEAD(fctx->events); dns_event != NULL;
|
||||
dns_event = next_event)
|
||||
{
|
||||
next_event = ISC_LIST_NEXT(dns_event, ev_link);
|
||||
|
||||
if (dns_event->ev_type != DNS_EVENT_TRYSTALE) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ISC_LIST_UNLINK(fctx->events, dns_event, ev_link);
|
||||
sender_task = dns_event->ev_sender;
|
||||
dns_event->ev_sender = fctx;
|
||||
dns_event->vresult = ISC_R_TIMEDOUT;
|
||||
dns_event->result = ISC_R_TIMEDOUT;
|
||||
|
||||
isc_task_sendanddetach(&sender_task, ISC_EVENT_PTR(&dns_event));
|
||||
count++;
|
||||
}
|
||||
|
||||
UNLOCK(&fctx->res->buckets[fctx->bucketnum].lock);
|
||||
|
||||
isc_event_free(&event);
|
||||
}
|
||||
|
||||
static void
|
||||
fctx_shutdown(fetchctx_t *fctx) {
|
||||
isc_event_t *cevent;
|
||||
|
|
@ -4760,6 +4861,9 @@ fctx_start(isc_task_t *task, isc_event_t *event) {
|
|||
* All is well. Start working on the fetch.
|
||||
*/
|
||||
result = fctx_starttimer(fctx);
|
||||
if (result == ISC_R_SUCCESS && fctx->timer_try_stale != NULL) {
|
||||
result = fctx_starttimer_trystale(fctx);
|
||||
}
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
fctx_done(fctx, result, __LINE__);
|
||||
} else {
|
||||
|
|
@ -4826,6 +4930,34 @@ fctx_join(fetchctx_t *fctx, isc_task_t *task, const isc_sockaddr_t *client,
|
|||
return (ISC_R_SUCCESS);
|
||||
}
|
||||
|
||||
static inline void
|
||||
fctx_add_event(fetchctx_t *fctx, isc_task_t *task, const isc_sockaddr_t *client,
|
||||
dns_messageid_t id, isc_taskaction_t action, void *arg,
|
||||
dns_fetch_t *fetch, isc_eventtype_t event_type) {
|
||||
isc_task_t *tclone;
|
||||
dns_fetchevent_t *event;
|
||||
/*
|
||||
* We store the task we're going to send this event to in the
|
||||
* sender field. We'll make the fetch the sender when we actually
|
||||
* send the event.
|
||||
*/
|
||||
tclone = NULL;
|
||||
isc_task_attach(task, &tclone);
|
||||
event = (dns_fetchevent_t *)isc_event_allocate(fctx->res->mctx, tclone,
|
||||
event_type, action, arg,
|
||||
sizeof(*event));
|
||||
event->result = DNS_R_SERVFAIL;
|
||||
event->qtype = fctx->type;
|
||||
event->db = NULL;
|
||||
event->node = NULL;
|
||||
event->rdataset = NULL;
|
||||
event->sigrdataset = NULL;
|
||||
event->fetch = fetch;
|
||||
event->client = client;
|
||||
event->id = id;
|
||||
ISC_LIST_APPEND(fctx->events, event, ev_link);
|
||||
}
|
||||
|
||||
static inline void
|
||||
log_ns_ttl(fetchctx_t *fctx, const char *where) {
|
||||
char namebuf[DNS_NAME_FORMATSIZE];
|
||||
|
|
@ -4850,9 +4982,11 @@ fctx_create(dns_resolver_t *res, const dns_name_t *name, dns_rdatatype_t type,
|
|||
isc_result_t iresult;
|
||||
isc_interval_t interval;
|
||||
unsigned int findoptions = 0;
|
||||
char buf[DNS_NAME_FORMATSIZE + DNS_RDATATYPE_FORMATSIZE];
|
||||
char buf[DNS_NAME_FORMATSIZE + DNS_RDATATYPE_FORMATSIZE + 1];
|
||||
char typebuf[DNS_RDATATYPE_FORMATSIZE];
|
||||
isc_mem_t *mctx;
|
||||
size_t p;
|
||||
bool try_stale;
|
||||
|
||||
/*
|
||||
* Caller must be holding the lock for bucket number 'bucketnum'.
|
||||
|
|
@ -4879,8 +5013,8 @@ fctx_create(dns_resolver_t *res, const dns_name_t *name, dns_rdatatype_t type,
|
|||
*/
|
||||
dns_name_format(name, buf, sizeof(buf));
|
||||
dns_rdatatype_format(type, typebuf, sizeof(typebuf));
|
||||
strlcat(buf, "/", sizeof(buf));
|
||||
strlcat(buf, typebuf, sizeof(buf));
|
||||
p = strlcat(buf, "/", sizeof(buf));
|
||||
strlcat(buf + p, typebuf, sizeof(buf));
|
||||
fctx->info = isc_mem_strdup(mctx, buf);
|
||||
|
||||
FCTXTRACE("create");
|
||||
|
|
@ -5077,6 +5211,29 @@ fctx_create(dns_resolver_t *res, const dns_name_t *name, dns_rdatatype_t type,
|
|||
goto cleanup_qmessage;
|
||||
}
|
||||
|
||||
try_stale = ((options & DNS_FETCHOPT_TRYSTALE_ONTIMEOUT) != 0);
|
||||
if (try_stale) {
|
||||
INSIST(res->view->staleanswerclienttimeout <=
|
||||
(res->query_timeout - 1000));
|
||||
/*
|
||||
* Compute an expiration time after which stale data will
|
||||
* attempted to be served, if stale answers are enabled and
|
||||
* target RRset is available in cache.
|
||||
*/
|
||||
isc_interval_set(
|
||||
&interval, res->view->staleanswerclienttimeout / 1000,
|
||||
res->view->staleanswerclienttimeout % 1000 * 1000000);
|
||||
iresult = isc_time_nowplusinterval(&fctx->expires_try_stale,
|
||||
&interval);
|
||||
if (iresult != ISC_R_SUCCESS) {
|
||||
UNEXPECTED_ERROR(__FILE__, __LINE__,
|
||||
"isc_time_nowplusinterval: %s",
|
||||
isc_result_totext(iresult));
|
||||
result = ISC_R_UNEXPECTED;
|
||||
goto cleanup_qmessage;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Default retry interval initialization. We set the interval now
|
||||
* mostly so it won't be uninitialized. It will be set to the
|
||||
|
|
@ -5085,10 +5242,11 @@ fctx_create(dns_resolver_t *res, const dns_name_t *name, dns_rdatatype_t type,
|
|||
isc_interval_set(&fctx->interval, 2, 0);
|
||||
|
||||
/*
|
||||
* Create an inactive timer. It will be made active when the fetch
|
||||
* is actually started.
|
||||
* Create an inactive timer for resolver-query-timeout. It
|
||||
* will be made active when the fetch is actually started.
|
||||
*/
|
||||
fctx->timer = NULL;
|
||||
|
||||
iresult = isc_timer_create(res->timermgr, isc_timertype_inactive, NULL,
|
||||
NULL, res->buckets[bucketnum].task,
|
||||
fctx_timeout, fctx, &fctx->timer);
|
||||
|
|
@ -5099,6 +5257,26 @@ fctx_create(dns_resolver_t *res, const dns_name_t *name, dns_rdatatype_t type,
|
|||
goto cleanup_qmessage;
|
||||
}
|
||||
|
||||
/*
|
||||
* If stale answers are enabled, then create an inactive timer
|
||||
* for stale-answer-client-timeout. It will be made active when
|
||||
* the fetch is actually started.
|
||||
*/
|
||||
fctx->timer_try_stale = NULL;
|
||||
if (try_stale) {
|
||||
iresult = isc_timer_create(
|
||||
res->timermgr, isc_timertype_inactive, NULL, NULL,
|
||||
res->buckets[bucketnum].task, fctx_timeout_try_stale,
|
||||
fctx, &fctx->timer_try_stale);
|
||||
if (iresult != ISC_R_SUCCESS) {
|
||||
UNEXPECTED_ERROR(__FILE__, __LINE__,
|
||||
"isc_timer_create: %s",
|
||||
isc_result_totext(iresult));
|
||||
result = ISC_R_UNEXPECTED;
|
||||
goto cleanup_qmessage;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Attach to the view's cache and adb.
|
||||
*/
|
||||
|
|
@ -5143,6 +5321,7 @@ cleanup_mctx:
|
|||
dns_adb_detach(&fctx->adb);
|
||||
dns_db_detach(&fctx->cache);
|
||||
isc_timer_detach(&fctx->timer);
|
||||
isc_timer_detach(&fctx->timer_try_stale);
|
||||
|
||||
cleanup_qmessage:
|
||||
dns_message_detach(&fctx->qmessage);
|
||||
|
|
@ -5356,6 +5535,23 @@ clone_results(fetchctx_t *fctx) {
|
|||
for (event = ISC_LIST_NEXT(hevent, ev_link); event != NULL;
|
||||
event = ISC_LIST_NEXT(event, ev_link))
|
||||
{
|
||||
if (event->ev_type == DNS_EVENT_TRYSTALE) {
|
||||
/*
|
||||
* We don't need to clone resulting data to this
|
||||
* type of event, as its associated callback is only
|
||||
* called when stale-answer-client-timeout triggers,
|
||||
* and the logic in there doesn't expect any result
|
||||
* as input, as it will itself lookup for stale data
|
||||
* in cache to use as result, if any is available.
|
||||
*
|
||||
* Also, if we reached this point, then the whole fetch
|
||||
* context is done, it will cancel timers, process
|
||||
* associated callbacks of type DNS_EVENT_FETCHDONE, and
|
||||
* silently remove/free events of type
|
||||
* DNS_EVENT_TRYSTALE.
|
||||
*/
|
||||
continue;
|
||||
}
|
||||
name = dns_fixedname_name(&event->foundname);
|
||||
dns_name_copynf(hname, name);
|
||||
event->result = hevent->result;
|
||||
|
|
@ -6125,6 +6321,7 @@ cache_name(fetchctx_t *fctx, dns_name_t *name, dns_message_t *message,
|
|||
(!need_validation)) {
|
||||
have_answer = true;
|
||||
event = ISC_LIST_HEAD(fctx->events);
|
||||
|
||||
if (event != NULL) {
|
||||
adbp = &event->db;
|
||||
aname = dns_fixedname_name(&event->foundname);
|
||||
|
|
@ -10814,6 +11011,14 @@ dns_resolver_createfetch(dns_resolver_t *res, const dns_name_t *name,
|
|||
|
||||
result = fctx_join(fctx, task, client, id, action, arg, rdataset,
|
||||
sigrdataset, fetch);
|
||||
|
||||
if (result == ISC_R_SUCCESS &&
|
||||
((options & DNS_FETCHOPT_TRYSTALE_ONTIMEOUT) != 0))
|
||||
{
|
||||
fctx_add_event(fctx, task, client, id, action, arg, fetch,
|
||||
DNS_EVENT_TRYSTALE);
|
||||
}
|
||||
|
||||
if (new_fctx) {
|
||||
if (result == ISC_R_SUCCESS) {
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -2518,3 +2518,25 @@ dns_view_setviewrevert(dns_view_t *view) {
|
|||
dns_zt_setviewrevert(zonetable);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
dns_view_staleanswerenabled(dns_view_t *view) {
|
||||
uint32_t stale_ttl = 0;
|
||||
bool result = false;
|
||||
|
||||
REQUIRE(DNS_VIEW_VALID(view));
|
||||
|
||||
if (dns_db_getservestalettl(view->cachedb, &stale_ttl) != ISC_R_SUCCESS)
|
||||
{
|
||||
return (false);
|
||||
}
|
||||
if (stale_ttl > 0) {
|
||||
if (view->staleanswersok == dns_stale_answer_yes) {
|
||||
result = true;
|
||||
} else if (view->staleanswersok == dns_stale_answer_conf) {
|
||||
result = view->staleanswersenable;
|
||||
}
|
||||
}
|
||||
|
||||
return (result);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1147,6 +1147,7 @@ dns_view_setrootdelonly
|
|||
dns_view_setviewcommit
|
||||
dns_view_setviewrevert
|
||||
dns_view_simplefind
|
||||
dns_view_staleanswerenabled
|
||||
dns_view_thaw
|
||||
dns_view_untrust
|
||||
dns_view_weakattach
|
||||
|
|
|
|||
|
|
@ -1899,6 +1899,28 @@ static cfg_type_t cfg_type_dns64 = { "dns64", cfg_parse_netprefix_map,
|
|||
cfg_print_map, cfg_doc_map,
|
||||
&cfg_rep_map, dns64_clausesets };
|
||||
|
||||
static const char *staleanswerclienttimeout_enums[] = { "disabled", "off",
|
||||
NULL };
|
||||
static isc_result_t
|
||||
parse_staleanswerclienttimeout(cfg_parser_t *pctx, const cfg_type_t *type,
|
||||
cfg_obj_t **ret) {
|
||||
return (cfg_parse_enum_or_other(pctx, type, &cfg_type_uint32, ret));
|
||||
}
|
||||
|
||||
static void
|
||||
doc_staleanswerclienttimeout(cfg_printer_t *pctx, const cfg_type_t *type) {
|
||||
cfg_doc_enum_or_other(pctx, type, &cfg_type_uint32);
|
||||
}
|
||||
|
||||
static cfg_type_t cfg_type_staleanswerclienttimeout = {
|
||||
"staleanswerclienttimeout",
|
||||
parse_staleanswerclienttimeout,
|
||||
cfg_print_ustring,
|
||||
doc_staleanswerclienttimeout,
|
||||
&cfg_rep_string,
|
||||
staleanswerclienttimeout_enums
|
||||
};
|
||||
|
||||
/*%
|
||||
* Clauses that can be found within the 'view' statement,
|
||||
* with defaults in the 'options' statement.
|
||||
|
|
@ -2027,6 +2049,8 @@ static cfg_clausedef_t view_clauses[] = {
|
|||
{ "servfail-ttl", &cfg_type_duration, 0 },
|
||||
{ "sortlist", &cfg_type_bracketed_aml, 0 },
|
||||
{ "stale-answer-enable", &cfg_type_boolean, 0 },
|
||||
{ "stale-answer-client-timeout", &cfg_type_staleanswerclienttimeout,
|
||||
0 },
|
||||
{ "stale-answer-ttl", &cfg_type_duration, 0 },
|
||||
{ "stale-cache-enable", &cfg_type_boolean, 0 },
|
||||
{ "stale-refresh-time", &cfg_type_duration, 0 },
|
||||
|
|
|
|||
|
|
@ -421,6 +421,10 @@ ns_client_send(ns_client_t *client) {
|
|||
|
||||
REQUIRE(NS_CLIENT_VALID(client));
|
||||
|
||||
if ((client->query.attributes & NS_QUERYATTR_ANSWERED) != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* XXXWPK TODO
|
||||
* Delay the response according to the -T delay option
|
||||
|
|
@ -670,6 +674,8 @@ renderend:
|
|||
ns_statscounter_truncatedresp);
|
||||
}
|
||||
|
||||
client->query.attributes |= NS_QUERYATTR_ANSWERED;
|
||||
|
||||
return;
|
||||
|
||||
cleanup:
|
||||
|
|
@ -2326,6 +2332,7 @@ ns__client_setup(ns_client_t *client, ns_clientmgr_t *mgr, bool new) {
|
|||
.query = query };
|
||||
}
|
||||
|
||||
client->query.attributes &= ~NS_QUERYATTR_ANSWERED;
|
||||
client->state = NS_CLIENTSTATE_INACTIVE;
|
||||
client->udpsize = 512;
|
||||
client->ednsversion = -1;
|
||||
|
|
|
|||
|
|
@ -117,6 +117,8 @@ struct ns_query {
|
|||
#define NS_QUERYATTR_DNS64EXCLUDE 0x08000
|
||||
#define NS_QUERYATTR_RRL_CHECKED 0x10000
|
||||
#define NS_QUERYATTR_REDIRECT 0x20000
|
||||
#define NS_QUERYATTR_ANSWERED 0x40000
|
||||
#define NS_QUERYATTR_STALEOK 0x80000
|
||||
|
||||
typedef struct query_ctx query_ctx_t;
|
||||
|
||||
|
|
|
|||
535
lib/ns/query.c
535
lib/ns/query.c
|
|
@ -86,11 +86,11 @@
|
|||
*/
|
||||
#define MAX_RESTARTS 16
|
||||
|
||||
#define QUERY_ERROR(qctx, r) \
|
||||
do { \
|
||||
qctx->result = r; \
|
||||
qctx->want_restart = false; \
|
||||
qctx->line = __LINE__; \
|
||||
#define QUERY_ERROR(qctx, r) \
|
||||
do { \
|
||||
(qctx)->result = r; \
|
||||
(qctx)->want_restart = false; \
|
||||
(qctx)->line = __LINE__; \
|
||||
} while (0)
|
||||
|
||||
/*% Partial answer? */
|
||||
|
|
@ -133,12 +133,21 @@
|
|||
|
||||
#define REDIRECT(c) (((c)->query.attributes & NS_QUERYATTR_REDIRECT) != 0)
|
||||
|
||||
/*% Was the query already answered due to stale-answer-client-timeout? */
|
||||
#define QUERY_ANSWERED(q) (((q)->attributes & NS_QUERYATTR_ANSWERED) != 0)
|
||||
|
||||
/*% Does the query only wants to check for stale RRset? */
|
||||
#define QUERY_STALEONLY(q) (((q)->dboptions & DNS_DBFIND_STALEONLY) != 0)
|
||||
|
||||
/*% Does the rdataset 'r' have an attached 'No QNAME Proof'? */
|
||||
#define NOQNAME(r) (((r)->attributes & DNS_RDATASETATTR_NOQNAME) != 0)
|
||||
|
||||
/*% Does the rdataset 'r' contain a stale answer? */
|
||||
#define STALE(r) (((r)->attributes & DNS_RDATASETATTR_STALE) != 0)
|
||||
|
||||
/*% Does the rdataset 'r' is stale and within stale-refresh-time? */
|
||||
#define STALE_WINDOW(r) (((r)->attributes & DNS_RDATASETATTR_STALE_WINDOW) != 0)
|
||||
|
||||
#ifdef WANT_QUERYTRACE
|
||||
static inline void
|
||||
client_trace(ns_client_t *client, int level, const char *message) {
|
||||
|
|
@ -173,10 +182,11 @@ client_trace(ns_client_t *client, int level, const char *message) {
|
|||
#define CCTRACE(l, m) ((void)m)
|
||||
#endif /* WANT_QUERYTRACE */
|
||||
|
||||
#define DNS_GETDB_NOEXACT 0x01U
|
||||
#define DNS_GETDB_NOLOG 0x02U
|
||||
#define DNS_GETDB_PARTIAL 0x04U
|
||||
#define DNS_GETDB_IGNOREACL 0x08U
|
||||
#define DNS_GETDB_NOEXACT 0x01U
|
||||
#define DNS_GETDB_NOLOG 0x02U
|
||||
#define DNS_GETDB_PARTIAL 0x04U
|
||||
#define DNS_GETDB_IGNOREACL 0x08U
|
||||
#define DNS_GETDB_STALEFIRST 0X0CU
|
||||
|
||||
#define PENDINGOK(x) (((x)&DNS_DBFIND_PENDINGOK) != 0)
|
||||
|
||||
|
|
@ -558,7 +568,10 @@ query_send(ns_client_t *client) {
|
|||
|
||||
inc_stats(client, counter);
|
||||
ns_client_send(client);
|
||||
isc_nmhandle_detach(&client->reqhandle);
|
||||
|
||||
if (!QUERY_STALEONLY(&client->query)) {
|
||||
isc_nmhandle_detach(&client->reqhandle);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -585,7 +598,10 @@ query_error(ns_client_t *client, isc_result_t result, int line) {
|
|||
log_queryerror(client, result, line, loglevel);
|
||||
|
||||
ns_client_error(client, result);
|
||||
isc_nmhandle_detach(&client->reqhandle);
|
||||
|
||||
if (!QUERY_STALEONLY(&client->query)) {
|
||||
isc_nmhandle_detach(&client->reqhandle);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -598,7 +614,10 @@ query_next(ns_client_t *client, isc_result_t result) {
|
|||
inc_stats(client, ns_statscounter_failure);
|
||||
}
|
||||
ns_client_drop(client, result);
|
||||
isc_nmhandle_detach(&client->reqhandle);
|
||||
|
||||
if (!QUERY_STALEONLY(&client->query)) {
|
||||
isc_nmhandle_detach(&client->reqhandle);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void
|
||||
|
|
@ -5106,6 +5125,26 @@ qctx_init(ns_client_t *client, dns_fetchevent_t **eventp, dns_rdatatype_t qtype,
|
|||
CALL_HOOK_NORETURN(NS_QUERY_QCTX_INITIALIZED, qctx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Make 'dst' and exact copy of 'src', with exception of the
|
||||
* option field, which is reset to zero.
|
||||
* This function also attaches dst's view and db to the src's
|
||||
* view and cachedb.
|
||||
*/
|
||||
static void
|
||||
qctx_copy(const query_ctx_t *qctx, query_ctx_t *dst) {
|
||||
REQUIRE(qctx != NULL);
|
||||
REQUIRE(dst != NULL);
|
||||
|
||||
memmove(dst, qctx, sizeof(*dst));
|
||||
dst->view = NULL;
|
||||
dst->db = NULL;
|
||||
dst->options = 0;
|
||||
dns_view_attach(qctx->view, &dst->view);
|
||||
dns_db_attach(qctx->view->cachedb, &dst->db);
|
||||
CCTRACE(ISC_LOG_DEBUG(3), "qctx_copy");
|
||||
}
|
||||
|
||||
/*%
|
||||
* Clean up and disassociate the rdataset and node pointers in qctx.
|
||||
*/
|
||||
|
|
@ -5158,7 +5197,7 @@ qctx_freedata(query_ctx_t *qctx) {
|
|||
dns_db_detach(&qctx->zdb);
|
||||
}
|
||||
|
||||
if (qctx->event != NULL) {
|
||||
if (qctx->event != NULL && !QUERY_STALEONLY(&qctx->client->query)) {
|
||||
free_devent(qctx->client, ISC_EVENT_PTR(&qctx->event),
|
||||
&qctx->event);
|
||||
}
|
||||
|
|
@ -5562,12 +5601,138 @@ ns__query_start(query_ctx_t *qctx) {
|
|||
}
|
||||
}
|
||||
|
||||
return (query_lookup(qctx));
|
||||
if (!qctx->is_zone && (qctx->view->staleanswerclienttimeout == 0) &&
|
||||
dns_view_staleanswerenabled(qctx->view))
|
||||
{
|
||||
/*
|
||||
* If stale answers are enabled and
|
||||
* stale-answer-client-timeout is zero, then we can promptly
|
||||
* answer with a stale RRset if one is available in cache.
|
||||
*/
|
||||
qctx->options |= DNS_GETDB_STALEFIRST;
|
||||
}
|
||||
|
||||
result = query_lookup(qctx);
|
||||
|
||||
/*
|
||||
* Clear "look-also-for-stale-data" flag.
|
||||
* If a fetch is created to resolve this query, then,
|
||||
* when it completes, this option is not expected to be set.
|
||||
*/
|
||||
qctx->options &= ~DNS_GETDB_STALEFIRST;
|
||||
|
||||
cleanup:
|
||||
return (result);
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate buffers in 'qctx' used to store query results.
|
||||
*
|
||||
* 'buffer' must be a pointer to an object whose lifetime
|
||||
* doesn't expire while 'qctx' is in use.
|
||||
*/
|
||||
static isc_result_t
|
||||
qctx_prepare_buffers(query_ctx_t *qctx, isc_buffer_t *buffer) {
|
||||
REQUIRE(qctx != NULL);
|
||||
REQUIRE(qctx->client != NULL);
|
||||
REQUIRE(buffer != NULL);
|
||||
|
||||
qctx->dbuf = ns_client_getnamebuf(qctx->client);
|
||||
if (ISC_UNLIKELY(qctx->dbuf == NULL)) {
|
||||
CCTRACE(ISC_LOG_ERROR,
|
||||
"qctx_prepare_buffers: ns_client_getnamebuf "
|
||||
"failed");
|
||||
return (ISC_R_NOMEMORY);
|
||||
}
|
||||
|
||||
qctx->fname = ns_client_newname(qctx->client, qctx->dbuf, buffer);
|
||||
if (ISC_UNLIKELY(qctx->fname == NULL)) {
|
||||
CCTRACE(ISC_LOG_ERROR,
|
||||
"qctx_prepare_buffers: ns_client_newname failed");
|
||||
|
||||
return (ISC_R_NOMEMORY);
|
||||
}
|
||||
|
||||
qctx->rdataset = ns_client_newrdataset(qctx->client);
|
||||
if (ISC_UNLIKELY(qctx->rdataset == NULL)) {
|
||||
CCTRACE(ISC_LOG_ERROR,
|
||||
"qctx_prepare_buffers: ns_client_newrdataset failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if ((WANTDNSSEC(qctx->client) || qctx->findcoveringnsec) &&
|
||||
(!qctx->is_zone || dns_db_issecure(qctx->db)))
|
||||
{
|
||||
qctx->sigrdataset = ns_client_newrdataset(qctx->client);
|
||||
if (qctx->sigrdataset == NULL) {
|
||||
CCTRACE(ISC_LOG_ERROR,
|
||||
"qctx_prepare_buffers: "
|
||||
"ns_client_newrdataset failed (2)");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
return (ISC_R_SUCCESS);
|
||||
|
||||
error:
|
||||
if (qctx->fname != NULL) {
|
||||
ns_client_releasename(qctx->client, &qctx->fname);
|
||||
}
|
||||
if (qctx->rdataset != NULL) {
|
||||
ns_client_putrdataset(qctx->client, &qctx->rdataset);
|
||||
}
|
||||
|
||||
return (ISC_R_NOMEMORY);
|
||||
}
|
||||
|
||||
/*
|
||||
* Setup a new query context for resolving a query.
|
||||
*
|
||||
* This function is only called if both these conditions are met:
|
||||
* 1. BIND is configured with stale-answer-client-timeout 0.
|
||||
* 2. A stale RRset is found in cache during initial query
|
||||
* database lookup.
|
||||
*
|
||||
* We continue with this function for refreshing/resolving an RRset
|
||||
* after answering a client with stale data.
|
||||
*/
|
||||
static void
|
||||
query_refresh_rrset(query_ctx_t *orig_qctx) {
|
||||
isc_buffer_t buffer;
|
||||
query_ctx_t qctx;
|
||||
|
||||
REQUIRE(orig_qctx != NULL);
|
||||
REQUIRE(orig_qctx->client != NULL);
|
||||
|
||||
qctx_copy(orig_qctx, &qctx);
|
||||
qctx.client->query.dboptions &= ~(DNS_DBFIND_STALEONLY |
|
||||
DNS_DBFIND_STALEOK |
|
||||
DNS_DBFIND_STALEENABLED);
|
||||
|
||||
/*
|
||||
* We'll need some resources...
|
||||
*/
|
||||
if (qctx_prepare_buffers(&qctx, &buffer) != ISC_R_SUCCESS) {
|
||||
dns_db_detach(&qctx.db);
|
||||
qctx_destroy(&qctx);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pretend we didn't find anything in cache.
|
||||
*/
|
||||
(void)query_gotanswer(&qctx, ISC_R_NOTFOUND);
|
||||
|
||||
if (qctx.fname != NULL) {
|
||||
ns_client_releasename(qctx.client, &qctx.fname);
|
||||
}
|
||||
if (qctx.rdataset != NULL) {
|
||||
ns_client_putrdataset(qctx.client, &qctx.rdataset);
|
||||
}
|
||||
|
||||
qctx_destroy(&qctx);
|
||||
}
|
||||
|
||||
/*%
|
||||
* Perform a local database lookup, in either an authoritative or
|
||||
* cache database. If unable to answer, call ns_query_done(); otherwise
|
||||
|
|
@ -5575,15 +5740,18 @@ cleanup:
|
|||
*/
|
||||
static isc_result_t
|
||||
query_lookup(query_ctx_t *qctx) {
|
||||
isc_buffer_t b;
|
||||
isc_buffer_t buffer;
|
||||
isc_result_t result = ISC_R_UNSET;
|
||||
dns_clientinfomethods_t cm;
|
||||
dns_clientinfo_t ci;
|
||||
dns_name_t *rpzqname = NULL;
|
||||
unsigned int dboptions;
|
||||
dns_ttl_t stale_ttl = 0;
|
||||
dns_ttl_t stale_refresh = 0;
|
||||
bool dbfind_stale = false;
|
||||
bool stale_only = false;
|
||||
bool stale_found = false;
|
||||
bool refresh_rrset = false;
|
||||
bool stale_refresh_window = false;
|
||||
|
||||
CCTRACE(ISC_LOG_DEBUG(3), "query_lookup");
|
||||
|
||||
|
|
@ -5595,37 +5763,12 @@ query_lookup(query_ctx_t *qctx) {
|
|||
/*
|
||||
* We'll need some resources...
|
||||
*/
|
||||
qctx->dbuf = ns_client_getnamebuf(qctx->client);
|
||||
if (ISC_UNLIKELY(qctx->dbuf == NULL)) {
|
||||
CCTRACE(ISC_LOG_ERROR, "query_lookup: ns_client_getnamebuf "
|
||||
"failed (2)");
|
||||
QUERY_ERROR(qctx, ISC_R_NOMEMORY);
|
||||
result = qctx_prepare_buffers(qctx, &buffer);
|
||||
if (result != ISC_R_SUCCESS) {
|
||||
QUERY_ERROR(qctx, result);
|
||||
return (ns_query_done(qctx));
|
||||
}
|
||||
|
||||
qctx->fname = ns_client_newname(qctx->client, qctx->dbuf, &b);
|
||||
qctx->rdataset = ns_client_newrdataset(qctx->client);
|
||||
|
||||
if (ISC_UNLIKELY(qctx->fname == NULL || qctx->rdataset == NULL)) {
|
||||
CCTRACE(ISC_LOG_ERROR, "query_lookup: ns_client_newname failed "
|
||||
"(2)");
|
||||
QUERY_ERROR(qctx, ISC_R_NOMEMORY);
|
||||
return (ns_query_done(qctx));
|
||||
}
|
||||
|
||||
if ((WANTDNSSEC(qctx->client) || qctx->findcoveringnsec) &&
|
||||
(!qctx->is_zone || dns_db_issecure(qctx->db)))
|
||||
{
|
||||
qctx->sigrdataset = ns_client_newrdataset(qctx->client);
|
||||
if (qctx->sigrdataset == NULL) {
|
||||
CCTRACE(ISC_LOG_ERROR, "query_lookup: "
|
||||
"ns_client_newrdataset failed "
|
||||
"(2)");
|
||||
QUERY_ERROR(qctx, ISC_R_NOMEMORY);
|
||||
return (ns_query_done(qctx));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Now look for an answer in the database.
|
||||
*/
|
||||
|
|
@ -5635,6 +5778,16 @@ query_lookup(query_ctx_t *qctx) {
|
|||
rpzqname = qctx->client->query.qname;
|
||||
}
|
||||
|
||||
if ((qctx->options & DNS_GETDB_STALEFIRST) != 0) {
|
||||
/*
|
||||
* If DNS_GETDB_STALEFIRST is set, it means that a stale
|
||||
* RRset may be returned as part of this lookup. An attempt
|
||||
* to refresh the RRset will still take place if an
|
||||
* active RRset is not available.
|
||||
*/
|
||||
qctx->client->query.dboptions |= DNS_DBFIND_STALEONLY;
|
||||
}
|
||||
|
||||
dboptions = qctx->client->query.dboptions;
|
||||
if (!qctx->is_zone && qctx->findcoveringnsec &&
|
||||
(qctx->type != dns_rdatatype_null || !dns_name_istat(rpzqname)))
|
||||
|
|
@ -5644,18 +5797,9 @@ query_lookup(query_ctx_t *qctx) {
|
|||
|
||||
(void)dns_db_getservestalerefresh(qctx->client->view->cachedb,
|
||||
&stale_refresh);
|
||||
(void)dns_db_getservestalettl(qctx->client->view->cachedb, &stale_ttl);
|
||||
if (stale_refresh > 0) {
|
||||
if (qctx->client->view->staleanswersok == dns_stale_answer_yes)
|
||||
{
|
||||
dboptions |= DNS_DBFIND_STALEENABLED;
|
||||
} else if (qctx->client->view->staleanswersok ==
|
||||
dns_stale_answer_conf) {
|
||||
if (qctx->client->view->staleanswersenable &&
|
||||
stale_ttl > 0) {
|
||||
dboptions |= DNS_DBFIND_STALEENABLED;
|
||||
}
|
||||
}
|
||||
if (stale_refresh > 0 &&
|
||||
dns_view_staleanswerenabled(qctx->client->view)) {
|
||||
dboptions |= DNS_DBFIND_STALEENABLED;
|
||||
}
|
||||
|
||||
result = dns_db_findext(qctx->db, rpzqname, qctx->version, qctx->type,
|
||||
|
|
@ -5681,69 +5825,214 @@ query_lookup(query_ctx_t *qctx) {
|
|||
/*
|
||||
* If DNS_DBFIND_STALEOK is set this means we are dealing with a
|
||||
* lookup following a failed lookup and it is okay to serve a stale
|
||||
* answer. This will start a time window in rbtdb, tracking the last
|
||||
* time the RRset lookup failed.
|
||||
*
|
||||
* A stale answer may also be served if this is a normal lookup,
|
||||
* the view has enabled serve-stale (DNS_DBFIND_STALE_ENABLED is set),
|
||||
* and the request is within the stale-refresh-time window. If this
|
||||
* is the case we have to make sure that the lookup found a stale
|
||||
* answer, otherwise "fresh" answers are also treated as stale.
|
||||
* answer. This will (re)start the 'stale-refresh-time' window in
|
||||
* rbtdb, tracking the last time the RRset lookup failed.
|
||||
*/
|
||||
dbfind_stale = ((dboptions & DNS_DBFIND_STALEOK) != 0);
|
||||
if (dbfind_stale != 0 ||
|
||||
(((dboptions & DNS_DBFIND_STALEENABLED) != 0) &&
|
||||
STALE(qctx->rdataset)))
|
||||
|
||||
/*
|
||||
* If DNS_DBFIND_STALEENABLED is set, this may be a normal lookup, but
|
||||
* we are allowed to immediately respond with a stale answer if the
|
||||
* request is within the 'stale-refresh-time' window.
|
||||
*/
|
||||
stale_refresh_window = (STALE_WINDOW(qctx->rdataset) &&
|
||||
(dboptions & DNS_DBFIND_STALEENABLED) != 0);
|
||||
|
||||
/*
|
||||
* If DNS_DBFIND_STALEONLY is set, a stale positive answer is requested.
|
||||
* This can happen if 'stale-answer-client-timeout' is enabled.
|
||||
*
|
||||
* If 'stale-answer-client-timeout' is set to 0, and a stale positive
|
||||
* answer is found, send it to the client, and try to refresh the
|
||||
* RRset. If a stale negative answer is found, continue with recursion
|
||||
* (perhaps the query will be resolved eventually and the answer from
|
||||
* the authoritative is returned to the client, or the query will
|
||||
* timeout, in that case DNS_DBFIND_STALEOK may be set, and a stale
|
||||
* negative answer is returned (or SERVFAIL).
|
||||
*
|
||||
* If 'stale-answer-client-timeout' is non-zero, and a stale positive
|
||||
* answer is found, send it to the client. Don't try to refresh the
|
||||
* RRset because a fetch is already in progress. If a stale negative
|
||||
* answer is found, then abort the lookup and the client has to wait
|
||||
* until recursion is finished.
|
||||
*/
|
||||
stale_only = ((dboptions & DNS_DBFIND_STALEONLY) != 0);
|
||||
|
||||
if (dbfind_stale ||
|
||||
(STALE(qctx->rdataset) && (stale_only || stale_refresh_window)))
|
||||
{
|
||||
char namebuf[DNS_NAME_FORMATSIZE];
|
||||
bool success;
|
||||
|
||||
inc_stats(qctx->client, ns_statscounter_trystale);
|
||||
|
||||
qctx->client->query.dboptions &= ~DNS_DBFIND_STALEOK;
|
||||
if (dns_rdataset_isassociated(qctx->rdataset) &&
|
||||
dns_rdataset_count(qctx->rdataset) > 0 &&
|
||||
STALE(qctx->rdataset))
|
||||
{
|
||||
qctx->rdataset->ttl = qctx->view->staleanswerttl;
|
||||
success = true;
|
||||
stale_found = true;
|
||||
} else {
|
||||
success = false;
|
||||
stale_found = false;
|
||||
}
|
||||
|
||||
dns_name_format(qctx->client->query.qname, namebuf,
|
||||
sizeof(namebuf));
|
||||
|
||||
if (dbfind_stale) {
|
||||
isc_log_write(ns_lctx, NS_LOGCATEGORY_SERVE_STALE,
|
||||
NS_LOGMODULE_QUERY, ISC_LOG_INFO,
|
||||
"%s resolver failure, stale answer %s",
|
||||
namebuf,
|
||||
success ? "used" : "unavailable");
|
||||
} else {
|
||||
stale_found ? "used" : "unavailable");
|
||||
if (!stale_found) {
|
||||
/*
|
||||
* Resolver failure, no stale data, nothing
|
||||
* more we can do, return SERVFAIL.
|
||||
*/
|
||||
QUERY_ERROR(qctx, DNS_R_SERVFAIL);
|
||||
return (ns_query_done(qctx));
|
||||
}
|
||||
|
||||
stale_only = false;
|
||||
} else if (stale_refresh_window) {
|
||||
/*
|
||||
* A recent lookup failed, so during this time window
|
||||
* we are allowed to return stale data immediately.
|
||||
*/
|
||||
isc_log_write(ns_lctx, NS_LOGCATEGORY_SERVE_STALE,
|
||||
NS_LOGMODULE_QUERY, ISC_LOG_INFO,
|
||||
"%s query within stale refresh time, "
|
||||
"stale answer %s",
|
||||
namebuf,
|
||||
success ? "used" : "unavailable");
|
||||
}
|
||||
stale_found ? "used" : "unavailable");
|
||||
|
||||
if (!success) {
|
||||
QUERY_ERROR(qctx, DNS_R_SERVFAIL);
|
||||
return (ns_query_done(qctx));
|
||||
if (!stale_found) {
|
||||
/*
|
||||
* During the stale refresh window explicitly
|
||||
* do not try to refresh the data, because a
|
||||
* recent lookup failed.
|
||||
*/
|
||||
QUERY_ERROR(qctx, DNS_R_SERVFAIL);
|
||||
return (ns_query_done(qctx));
|
||||
}
|
||||
|
||||
stale_only = false;
|
||||
} else if ((dboptions & DNS_DBFIND_STALEONLY) != 0) {
|
||||
if ((qctx->options & DNS_GETDB_STALEFIRST) != 0) {
|
||||
if (stale_found && result == ISC_R_SUCCESS) {
|
||||
/*
|
||||
* Immediately return the stale answer,
|
||||
* start a resolver fetch to refresh
|
||||
* the data in cache.
|
||||
*/
|
||||
isc_log_write(
|
||||
ns_lctx,
|
||||
NS_LOGCATEGORY_SERVE_STALE,
|
||||
NS_LOGMODULE_QUERY,
|
||||
ISC_LOG_INFO,
|
||||
"%s stale answer used, an "
|
||||
"attempt to refresh the RRset "
|
||||
"will still be made",
|
||||
namebuf);
|
||||
refresh_rrset = true;
|
||||
} else {
|
||||
/*
|
||||
* We have nothing useful in cache to
|
||||
* return immediately.
|
||||
*/
|
||||
qctx_clean(qctx);
|
||||
qctx_freedata(qctx);
|
||||
dns_db_attach(
|
||||
qctx->client->view->cachedb,
|
||||
&qctx->db);
|
||||
qctx->client->query.dboptions &=
|
||||
~DNS_DBFIND_STALEONLY;
|
||||
qctx->options &= ~DNS_GETDB_STALEFIRST;
|
||||
if (qctx->client->query.fetch != NULL) {
|
||||
dns_resolver_destroyfetch(
|
||||
&qctx->client->query
|
||||
.fetch);
|
||||
}
|
||||
return query_lookup(qctx);
|
||||
}
|
||||
} else {
|
||||
/*
|
||||
* The 'stale-answer-client-timeout' triggered,
|
||||
* return the stale answer if available,
|
||||
* otherwise wait until the resolver finishes.
|
||||
*/
|
||||
isc_log_write(
|
||||
ns_lctx, NS_LOGCATEGORY_SERVE_STALE,
|
||||
NS_LOGMODULE_QUERY, ISC_LOG_INFO,
|
||||
"%s client timeout, stale answer %s",
|
||||
namebuf,
|
||||
stale_found ? "used" : "unavailable");
|
||||
if (!stale_found || result != ISC_R_SUCCESS) {
|
||||
return (result);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
stale_only = false;
|
||||
}
|
||||
|
||||
if (!stale_only) {
|
||||
/*
|
||||
* The DNS_DBFIND_STALEONLY option may be set, but if we
|
||||
* didn't have stale data in cache, or we responded with
|
||||
* a stale answer because of 'stale-refresh-time', this is
|
||||
* actually not a 'stale-only' lookup. Clear the flag to
|
||||
* allow the client to be detach the handle.
|
||||
*/
|
||||
qctx->client->query.dboptions &= ~DNS_DBFIND_STALEONLY;
|
||||
}
|
||||
|
||||
result = query_gotanswer(qctx, result);
|
||||
|
||||
if (refresh_rrset) {
|
||||
/*
|
||||
* If we reached this point then it means that we have found a
|
||||
* stale RRset entry in cache and BIND is configured to allow
|
||||
* queries to be answered with stale data if no active RRset
|
||||
* is available, i.e. "stale-anwer-client-timeout 0". But, we
|
||||
* still need to refresh the RRset.
|
||||
*/
|
||||
query_refresh_rrset(qctx);
|
||||
}
|
||||
return (query_gotanswer(qctx, result));
|
||||
|
||||
cleanup:
|
||||
return (result);
|
||||
}
|
||||
|
||||
/*
|
||||
* Event handler to resume processing a query after recursion.
|
||||
* If the query has timed out or been canceled or the system
|
||||
* is shutting down, clean up and exit; otherwise, call
|
||||
* query_resume() to continue the ongoing work.
|
||||
* Create a new query context with the sole intent
|
||||
* of looking up for a stale RRset in cache.
|
||||
* If an entry is found, we mark the original query as
|
||||
* answered, in order to avoid answering the query twice,
|
||||
* when the original fetch finishes.
|
||||
*/
|
||||
static inline void
|
||||
query_lookup_staleonly(ns_client_t *client) {
|
||||
query_ctx_t qctx;
|
||||
|
||||
qctx_init(client, NULL, client->query.qtype, &qctx);
|
||||
dns_db_attach(client->view->cachedb, &qctx.db);
|
||||
client->query.dboptions |= DNS_DBFIND_STALEONLY;
|
||||
(void)query_lookup(&qctx);
|
||||
if (qctx.node != NULL) {
|
||||
dns_db_detachnode(qctx.db, &qctx.node);
|
||||
}
|
||||
qctx_freedata(&qctx);
|
||||
client->query.dboptions &= ~DNS_DBFIND_STALEONLY;
|
||||
qctx_destroy(&qctx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Event handler to resume processing a query after recursion, or when a
|
||||
* client timeout is triggered. If the query has timed out or been cancelled
|
||||
* or the system is shutting down, clean up and exit. If a client timeout is
|
||||
* triggered, see if we can respond with a stale answer from cache. Otherwise,
|
||||
* call query_resume() to continue the ongoing work.
|
||||
*/
|
||||
static void
|
||||
fetch_callback(isc_task_t *task, isc_event_t *event) {
|
||||
|
|
@ -5758,7 +6047,8 @@ fetch_callback(isc_task_t *task, isc_event_t *event) {
|
|||
|
||||
UNUSED(task);
|
||||
|
||||
REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE);
|
||||
REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE ||
|
||||
event->ev_type == DNS_EVENT_TRYSTALE);
|
||||
client = devent->ev_arg;
|
||||
REQUIRE(NS_CLIENT_VALID(client));
|
||||
REQUIRE(task == client->task);
|
||||
|
|
@ -5766,6 +6056,14 @@ fetch_callback(isc_task_t *task, isc_event_t *event) {
|
|||
|
||||
CTRACE(ISC_LOG_DEBUG(3), "fetch_callback");
|
||||
|
||||
if (event->ev_type == DNS_EVENT_TRYSTALE) {
|
||||
query_lookup_staleonly(client);
|
||||
isc_event_free(ISC_EVENT_PTR(&event));
|
||||
return;
|
||||
} else {
|
||||
client->query.dboptions &= ~DNS_DBFIND_STALEONLY;
|
||||
}
|
||||
|
||||
LOCK(&client->query.fetchlock);
|
||||
if (client->query.fetch != NULL) {
|
||||
/*
|
||||
|
|
@ -6086,6 +6384,13 @@ ns_query_recurse(ns_client_t *client, dns_rdatatype_t qtype, dns_name_t *qname,
|
|||
peeraddr = &client->peeraddr;
|
||||
}
|
||||
|
||||
if (client->view->staleanswerclienttimeout > 0 &&
|
||||
client->view->staleanswerclienttimeout != (uint32_t)-1 &&
|
||||
dns_view_staleanswerenabled(client->view))
|
||||
{
|
||||
client->query.fetchoptions |= DNS_FETCHOPT_TRYSTALE_ONTIMEOUT;
|
||||
}
|
||||
|
||||
isc_nmhandle_attach(client->handle, &client->fetchhandle);
|
||||
result = dns_resolver_createfetch(
|
||||
client->view->resolver, qname, qtype, qdomain, nameservers,
|
||||
|
|
@ -7136,45 +7441,20 @@ root_key_sentinel_return_servfail(query_ctx_t *qctx, isc_result_t result) {
|
|||
*/
|
||||
static bool
|
||||
query_usestale(query_ctx_t *qctx) {
|
||||
bool staleanswersok = false;
|
||||
dns_ttl_t stale_ttl = 0;
|
||||
isc_result_t result;
|
||||
|
||||
qctx_clean(qctx);
|
||||
qctx_freedata(qctx);
|
||||
|
||||
/*
|
||||
* Stale answers only make sense if stale_ttl > 0 but we want rndc to
|
||||
* be able to control returning stale answers if they are configured.
|
||||
*/
|
||||
dns_db_attach(qctx->client->view->cachedb, &qctx->db);
|
||||
result = dns_db_getservestalettl(qctx->db, &stale_ttl);
|
||||
if (result == ISC_R_SUCCESS && stale_ttl > 0) {
|
||||
switch (qctx->client->view->staleanswersok) {
|
||||
case dns_stale_answer_yes:
|
||||
staleanswersok = true;
|
||||
break;
|
||||
case dns_stale_answer_conf:
|
||||
staleanswersok = qctx->client->view->staleanswersenable;
|
||||
break;
|
||||
case dns_stale_answer_no:
|
||||
staleanswersok = false;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
staleanswersok = false;
|
||||
}
|
||||
|
||||
if (staleanswersok) {
|
||||
if (dns_view_staleanswerenabled(qctx->client->view)) {
|
||||
dns_db_attach(qctx->client->view->cachedb, &qctx->db);
|
||||
qctx->client->query.dboptions |= DNS_DBFIND_STALEOK;
|
||||
if (qctx->client->query.fetch != NULL) {
|
||||
dns_resolver_destroyfetch(&qctx->client->query.fetch);
|
||||
}
|
||||
} else {
|
||||
dns_db_detach(&qctx->db);
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
return (staleanswersok);
|
||||
return (false);
|
||||
}
|
||||
|
||||
/*%
|
||||
|
|
@ -7688,7 +7968,9 @@ query_addanswer(query_ctx_t *qctx) {
|
|||
query_filter64(qctx);
|
||||
ns_client_putrdataset(qctx->client, &qctx->rdataset);
|
||||
} else {
|
||||
if (!qctx->is_zone && RECURSIONOK(qctx->client)) {
|
||||
if (!qctx->is_zone && RECURSIONOK(qctx->client) &&
|
||||
!QUERY_STALEONLY(&qctx->client->query))
|
||||
{
|
||||
query_prefetch(qctx->client, qctx->fname,
|
||||
qctx->rdataset);
|
||||
}
|
||||
|
|
@ -7796,7 +8078,7 @@ query_respond(query_ctx_t *qctx) {
|
|||
* We shouldn't ever fail to add 'rdataset'
|
||||
* because it's already in the answer.
|
||||
*/
|
||||
INSIST(qctx->rdataset == NULL);
|
||||
INSIST(qctx->rdataset == NULL || QUERY_ANSWERED(&qctx->client->query));
|
||||
|
||||
query_addauth(qctx);
|
||||
|
||||
|
|
@ -11213,6 +11495,7 @@ isc_result_t
|
|||
ns_query_done(query_ctx_t *qctx) {
|
||||
isc_result_t result = ISC_R_UNSET;
|
||||
const dns_namelist_t *secs = qctx->client->message->sections;
|
||||
bool query_stale_only;
|
||||
|
||||
CCTRACE(ISC_LOG_DEBUG(3), "ns_query_done");
|
||||
|
||||
|
|
@ -11281,7 +11564,10 @@ ns_query_done(query_ctx_t *qctx) {
|
|||
* If we're recursing then just return; the query will
|
||||
* resume when recursion ends.
|
||||
*/
|
||||
if (RECURSING(qctx->client)) {
|
||||
if (RECURSING(qctx->client) &&
|
||||
(!QUERY_STALEONLY(&qctx->client->query) ||
|
||||
((qctx->options & DNS_GETDB_STALEFIRST) != 0)))
|
||||
{
|
||||
return (qctx->result);
|
||||
}
|
||||
|
||||
|
|
@ -11292,8 +11578,10 @@ ns_query_done(query_ctx_t *qctx) {
|
|||
* to the AA bit if the auth-nxdomain config option
|
||||
* says so, then render and send the response.
|
||||
*/
|
||||
query_setup_sortlist(qctx);
|
||||
query_glueanswer(qctx);
|
||||
if (!QUERY_ANSWERED(qctx->client)) {
|
||||
query_setup_sortlist(qctx);
|
||||
query_glueanswer(qctx);
|
||||
}
|
||||
|
||||
if (qctx->client->message->rcode == dns_rcode_nxdomain &&
|
||||
qctx->view->auth_nxdomain)
|
||||
|
|
@ -11315,9 +11603,16 @@ ns_query_done(query_ctx_t *qctx) {
|
|||
|
||||
CALL_HOOK(NS_QUERY_DONE_SEND, qctx);
|
||||
|
||||
/*
|
||||
* Client may have been detached after query_send(), so
|
||||
* we test and store the flag state here, for safety.
|
||||
*/
|
||||
query_stale_only = QUERY_STALEONLY(&qctx->client->query);
|
||||
query_send(qctx->client);
|
||||
|
||||
qctx->detach_client = true;
|
||||
if (!query_stale_only) {
|
||||
qctx->detach_client = true;
|
||||
}
|
||||
return (qctx->result);
|
||||
|
||||
cleanup:
|
||||
|
|
|
|||
Loading…
Reference in a new issue