Merge branch '2066-fix-serve-stale' into 'main'

Resolve "Fix serve-stale so that it is usable when needed"

Closes #2066

See merge request isc-projects/bind9!4273
This commit is contained in:
Diego dos Santos Fronza 2020-11-11 16:19:10 +00:00
commit fece7a4881
29 changed files with 688 additions and 92 deletions

View file

@ -1,3 +1,8 @@
5533. [func] Add "stale-refresh-time" option, a time window that
starts after a failed lookup, during which stale rrset
will be served directly from cache before a new
attempt to refresh it is made. [GL #2066]
5532. [cleanup] Unused header files were removed:
bin/rndc/include/rndc/os.h, lib/isc/timer_p.h,
lib/isccfg/include/isccfg/dnsconf.h and code related

View file

@ -196,6 +196,7 @@ options {\n\
servfail-ttl 1;\n\
# sortlist <none>\n\
stale-answer-enable false;\n\
stale-refresh-time 30; /* 30 seconds */\n\
stale-answer-ttl 1; /* 1 second */\n\
stale-cache-enable false;\n\
synth-from-dnssec no;\n\

View file

@ -401,6 +401,7 @@ OPTIONS
stale-answer-enable boolean;
stale-answer-ttl duration;
stale-cache-enable boolean;
stale-refresh-time duration;
startup-notify-rate integer;
statistics-file quoted_string;
synth-from-dnssec boolean;
@ -797,6 +798,7 @@ VIEW
stale-answer-enable boolean;
stale-answer-ttl duration;
stale-cache-enable boolean;
stale-refresh-time duration;
synth-from-dnssec boolean;
transfer-format ( many-answers | one-answer );
transfer-source ( ipv4_address | * ) [ port ( integer | * ) ] [

View file

@ -1869,7 +1869,7 @@ cache_reusable(dns_view_t *originview, dns_view_t *view,
static bool
cache_sharable(dns_view_t *originview, dns_view_t *view,
bool new_zero_no_soattl, uint64_t new_max_cache_size,
uint32_t new_stale_ttl) {
uint32_t new_stale_ttl, uint32_t new_stale_refresh_time) {
/*
* If the cache cannot even reused for the same view, it cannot be
* shared with other views.
@ -1883,6 +1883,8 @@ cache_sharable(dns_view_t *originview, dns_view_t *view,
* the sharing views.
*/
if (dns_cache_getservestalettl(originview->cache) != new_stale_ttl ||
dns_cache_getservestalerefresh(originview->cache) !=
new_stale_refresh_time ||
dns_cache_getcachesize(originview->cache) != new_max_cache_size)
{
return (false);
@ -3897,6 +3899,7 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config,
size_t max_adb_size;
uint32_t lame_ttl, fail_ttl;
uint32_t max_stale_ttl = 0;
uint32_t stale_refresh_time = 0;
dns_tsig_keyring_t *ring = NULL;
dns_view_t *pview = NULL; /* Production view */
isc_mem_t *cmctx = NULL, *hmctx = NULL;
@ -4395,6 +4398,11 @@ 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-refresh-time", &obj);
INSIST(result == ISC_R_SUCCESS);
stale_refresh_time = cfg_obj_asduration(obj);
/*
* Configure the view's cache.
*
@ -4429,7 +4437,8 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config,
nsc = cachelist_find(cachelist, cachename, view->rdclass);
if (nsc != NULL) {
if (!cache_sharable(nsc->primaryview, view, zero_no_soattl,
max_cache_size, max_stale_ttl))
max_cache_size, max_stale_ttl,
stale_refresh_time))
{
isc_log_write(named_g_lctx, NAMED_LOGCATEGORY_GENERAL,
NAMED_LOGMODULE_SERVER, ISC_LOG_ERROR,
@ -4529,6 +4538,7 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config,
dns_cache_setcachesize(cache, max_cache_size);
dns_cache_setservestalettl(cache, max_stale_ttl);
dns_cache_setservestalerefresh(cache, stale_refresh_time);
dns_cache_detach(&cache);
@ -16162,6 +16172,7 @@ named_server_servestale(named_server_t *server, isc_lex_t *lex,
view = ISC_LIST_NEXT(view, link))
{
dns_ttl_t stale_ttl = 0;
uint32_t stale_refresh = 0;
dns_db_t *db = NULL;
if (classtxt != NULL && rdclass != view->rdclass) {
@ -16181,6 +16192,7 @@ named_server_servestale(named_server_t *server, isc_lex_t *lex,
db = NULL;
dns_db_attach(view->cachedb, &db);
(void)dns_db_getservestalettl(db, &stale_ttl);
(void)dns_db_getservestalerefresh(db, &stale_refresh);
dns_db_detach(&db);
if (found) {
CHECK(putstr(text, "\n"));
@ -16210,8 +16222,10 @@ named_server_servestale(named_server_t *server, isc_lex_t *lex,
}
if (stale_ttl > 0) {
snprintf(msg, sizeof(msg),
" (stale-answer-ttl=%u max-stale-ttl=%u)",
view->staleanswerttl, stale_ttl);
" (stale-answer-ttl=%u max-stale-ttl=%u "
"stale-refresh-time=%u)",
view->staleanswerttl, stale_ttl,
stale_refresh);
CHECK(putstr(text, msg));
}
found = true;

View file

@ -0,0 +1,14 @@
/*
* 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.
*/
options {
stale-refresh-time 0;
};

View file

@ -0,0 +1,14 @@
/*
* 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.
*/
options {
stale-refresh-time 29;
};

View file

@ -139,6 +139,19 @@ grep '.*' < checkconf.out$n.2 > /dev/null && ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=`expr $status + $ret`
n=`expr $n + 1`
echo_i "checking named-checkconf servestale warnings ($n)"
ret=0
$CHECKCONF servestale.stale-refresh-time.0.conf > checkconf.out$n.1 2>&1
grep "'stale-refresh-time' should either be 0 or otherwise 30 seconds or higher" < checkconf.out$n.1 > /dev/null && ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=`expr $status + $ret`
ret=0
$CHECKCONF servestale.stale-refresh-time.29.conf > checkconf.out$n.1 2>&1
grep "'stale-refresh-time' should either be 0 or otherwise 30 seconds or higher" < checkconf.out$n.1 > /dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=`expr $status + $ret`
n=`expr $n + 1`
echo_i "range checking fields that do not allow zero ($n)"
ret=0

View file

@ -589,6 +589,8 @@ static dns_dbmethods_t sampledb_methods = {
NULL, /* getsize */
NULL, /* setservestalettl */
NULL, /* getservestalettl */
NULL, /* setservestalerefresh */
NULL, /* getservestalerefresh */
NULL, /* setgluecachestats */
NULL /* adjusthashsize */
};

View file

@ -28,9 +28,10 @@ options {
listen-on-v6 { none; };
recursion yes;
max-stale-ttl 3600;
stale-answer-ttl 2;
stale-answer-ttl 4;
stale-answer-enable yes;
stale-cache-enable yes;
stale-refresh-time 30;
servfail-ttl 0;
};

View file

@ -0,0 +1,41 @@
/*
* 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.
*/
key rndc_key {
secret "1234abcd8765";
algorithm hmac-sha256;
};
controls {
inet 10.53.0.1 port @CONTROLPORT@ allow { any; } keys { rndc_key; };
};
options {
query-source address 10.53.0.1;
notify-source 10.53.0.1;
transfer-source 10.53.0.1;
port @PORT@;
pid-file "named.pid";
listen-on { 10.53.0.1; };
listen-on-v6 { none; };
recursion yes;
max-stale-ttl 20;
stale-answer-ttl 3;
stale-answer-enable yes;
stale-cache-enable yes;
stale-refresh-time 0;
servfail-ttl 0;
};
zone "." {
type primary;
file "root.db";
};

View file

@ -12,15 +12,6 @@
. ../conf.sh
RNDCCMD="$RNDC -c ../common/rndc.conf -p ${CONTROLPORT} -s"
# wait up to 11 seconds to ensure that a file has been written
waitfile () {
for try in 0 1 2 3 4 5 6 7 8 9 10; do
[ -s "$1" ] && break
sleep 1
done
}
DIG="$DIG +time=11"
max_stale_ttl=$(sed -ne 's,^[[:space:]]*max-stale-ttl \([[:digit:]]*\).*,\1,p' $TOP_SRCDIR/bin/named/config.c)
@ -108,7 +99,7 @@ n=$((n+1))
echo_i "check 'rndc serve-stale status' ($n)"
ret=0
$RNDCCMD 10.53.0.1 serve-stale status > rndc.out.test$n 2>&1 || ret=1
grep '_default: on (stale-answer-ttl=2 max-stale-ttl=3600)' rndc.out.test$n > /dev/null || ret=1
grep '_default: on (stale-answer-ttl=4 max-stale-ttl=3600 stale-refresh-time=30)' rndc.out.test$n > /dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret))
@ -120,18 +111,14 @@ $DIG -p ${PORT} @10.53.0.1 othertype.example CAA > dig.out.test$((n+2)) &
$DIG -p ${PORT} @10.53.0.1 nodata.example TXT > dig.out.test$((n+3)) &
$DIG -p ${PORT} @10.53.0.1 nxdomain.example TXT > dig.out.test$((n+4))
# ensure all files have been written before proceeding
waitfile dig.out.test$((n+1))
waitfile dig.out.test$((n+2))
waitfile dig.out.test$((n+3))
waitfile dig.out.test$((n+4))
wait
n=$((n+1))
echo_i "check stale data.example ($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
grep "data\.example\..*2.*IN.*TXT.*A text record with a 2 second ttl" dig.out.test$n > /dev/null || ret=1
grep "data\.example\..*4.*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))
@ -153,7 +140,7 @@ echo_i "check stale othertype.example ($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
grep "othertype\.example\..*2.*IN.*CAA.*0.*issue" dig.out.test$n > /dev/null || ret=1
grep "othertype\.example\..*4.*IN.*CAA.*0.*issue" dig.out.test$n > /dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret))
@ -162,7 +149,7 @@ echo_i "check stale nodata.example ($n)"
ret=0
grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
grep "ANSWER: 0," dig.out.test$n > /dev/null || ret=1
grep "example\..*2.*IN.*SOA" dig.out.test$n > /dev/null || ret=1
grep "example\..*4.*IN.*SOA" dig.out.test$n > /dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret))
@ -171,7 +158,7 @@ echo_i "check stale nxdomain.example ($n)"
ret=0
grep "status: NXDOMAIN" dig.out.test$n > /dev/null || ret=1
grep "ANSWER: 0," dig.out.test$n > /dev/null || ret=1
grep "example\..*2.*IN.*SOA" dig.out.test$n > /dev/null || ret=1
grep "example\..*4.*IN.*SOA" dig.out.test$n > /dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret))
@ -194,6 +181,77 @@ grep "1 #NXDOMAIN" ns1/named.stats.$n.cachedb > /dev/null || ret=1
status=$((status+ret))
if [ $ret != 0 ]; then echo_i "failed"; fi
# Test stale-refresh-time when serve-stale is enabled via configuration.
# Steps for testing stale-refresh-time option (default).
# 1. Prime cache data.example txt
# 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)
# 6. Enable responses from authoritative server.
# 7. Query data.example
# 8. Check if response come from stale rrset, since the query
# is within stale-refresh-time window.
n=$((n+1))
echo_i "check 'rndc serve-stale status' ($n)"
ret=0
$RNDCCMD 10.53.0.1 serve-stale status > rndc.out.test$n 2>&1 || ret=1
grep '_default: on (stale-answer-ttl=4 max-stale-ttl=3600 stale-refresh-time=30)' rndc.out.test$n > /dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret))
# Step 1-3 done above.
# Step 4.
n=$((n+1))
echo_i "sending query for test ($n)"
$DIG -p ${PORT} @10.53.0.1 data.example TXT > dig.out.test$n
# Step 5.
echo_i "check stale data.example (stale-refresh-time) ($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
grep "data\.example\..*4.*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))
# Step 6.
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))
# Step 7.
echo_i "sending query for test $((n+1))"
$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 cache (stale-refresh-time) ($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
grep "data\.example\..*4.*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 disabling serve-stale via rndc.
#
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))
echo_i "running 'rndc serve-stale off' ($n)"
ret=0
@ -205,7 +263,7 @@ n=$((n+1))
echo_i "check 'rndc serve-stale status' ($n)"
ret=0
$RNDCCMD 10.53.0.1 serve-stale status > rndc.out.test$n 2>&1 || ret=1
grep '_default: off (rndc) (stale-answer-ttl=2 max-stale-ttl=3600)' rndc.out.test$n > /dev/null || ret=1
grep '_default: off (rndc) (stale-answer-ttl=4 max-stale-ttl=3600 stale-refresh-time=30)' rndc.out.test$n > /dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret))
@ -215,11 +273,7 @@ $DIG -p ${PORT} @10.53.0.1 othertype.example CAA > dig.out.test$((n+2)) &
$DIG -p ${PORT} @10.53.0.1 nodata.example TXT > dig.out.test$((n+3)) &
$DIG -p ${PORT} @10.53.0.1 nxdomain.example TXT > dig.out.test$((n+4))
# ensure all files have been written before proceeding
waitfile dig.out.test$((n+1))
waitfile dig.out.test$((n+2))
waitfile dig.out.test$((n+3))
waitfile dig.out.test$((n+4))
wait
n=$((n+1))
echo_i "check stale data.example (serve-stale off) ($n)"
@ -249,6 +303,9 @@ grep "status: SERVFAIL" dig.out.test$n > /dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret))
#
# Test enabling serve-stale via rndc.
#
n=$((n+1))
echo_i "running 'rndc serve-stale on' ($n)"
ret=0
@ -260,7 +317,7 @@ n=$((n+1))
echo_i "check 'rndc serve-stale status' ($n)"
ret=0
$RNDCCMD 10.53.0.1 serve-stale status > rndc.out.test$n 2>&1 || ret=1
grep '_default: on (rndc) (stale-answer-ttl=2 max-stale-ttl=3600)' rndc.out.test$n > /dev/null || ret=1
grep '_default: on (rndc) (stale-answer-ttl=4 max-stale-ttl=3600 stale-refresh-time=30)' rndc.out.test$n > /dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret))
@ -270,18 +327,14 @@ $DIG -p ${PORT} @10.53.0.1 othertype.example CAA > dig.out.test$((n+2)) &
$DIG -p ${PORT} @10.53.0.1 nodata.example TXT > dig.out.test$((n+3)) &
$DIG -p ${PORT} @10.53.0.1 nxdomain.example TXT > dig.out.test$((n+4))
# ensure all files have been written before proceeding
waitfile dig.out.test$((n+1))
waitfile dig.out.test$((n+2))
waitfile dig.out.test$((n+3))
waitfile dig.out.test$((n+4))
wait
n=$((n+1))
echo_i "check stale data.example (serve-stale on) ($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
grep "data\.example\..*2.*IN.*TXT.*A text record with a 2 second ttl" dig.out.test$n > /dev/null || ret=1
grep "data\.example\..*4.*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))
@ -290,7 +343,7 @@ echo_i "check stale othertype.example (serve-stale on) ($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
grep "othertype\.example\..*2.*IN.*CAA.*0.*issue" dig.out.test$n > /dev/null || ret=1
grep "othertype\.example\..*4.*IN.*CAA.*0.*issue" dig.out.test$n > /dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret))
@ -299,7 +352,7 @@ echo_i "check stale nodata.example (serve-stale on) ($n)"
ret=0
grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
grep "ANSWER: 0," dig.out.test$n > /dev/null || ret=1
grep "example\..*2.*IN.*SOA" dig.out.test$n > /dev/null || ret=1
grep "example\..*4.*IN.*SOA" dig.out.test$n > /dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret))
@ -308,7 +361,7 @@ echo_i "check stale nxdomain.example (serve-stale on) ($n)"
ret=0
grep "status: NXDOMAIN" dig.out.test$n > /dev/null || ret=1
grep "ANSWER: 0," dig.out.test$n > /dev/null || ret=1
grep "example\..*2.*IN.*SOA" dig.out.test$n > /dev/null || ret=1
grep "example\..*4.*IN.*SOA" dig.out.test$n > /dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret))
@ -330,7 +383,7 @@ n=$((n+1))
echo_i "check 'rndc serve-stale status' ($n)"
ret=0
$RNDCCMD 10.53.0.1 serve-stale status > rndc.out.test$n 2>&1 || ret=1
grep '_default: on (stale-answer-ttl=2 max-stale-ttl=3600)' rndc.out.test$n > /dev/null || ret=1
grep '_default: on (stale-answer-ttl=4 max-stale-ttl=3600 stale-refresh-time=30)' rndc.out.test$n > /dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret))
@ -340,18 +393,14 @@ $DIG -p ${PORT} @10.53.0.1 othertype.example CAA > dig.out.test$((n+2)) &
$DIG -p ${PORT} @10.53.0.1 nodata.example TXT > dig.out.test$((n+3)) &
$DIG -p ${PORT} @10.53.0.1 nxdomain.example TXT > dig.out.test$((n+4))
# ensure all files have been written before proceeding
waitfile dig.out.test$((n+1))
waitfile dig.out.test$((n+2))
waitfile dig.out.test$((n+3))
waitfile dig.out.test$((n+4))
wait
n=$((n+1))
echo_i "check stale data.example (serve-stale reset) ($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
grep "data\.example\..*2.*IN.*TXT.*A text record with a 2 second ttl" dig.out.test$n > /dev/null || ret=1
grep "data\.example\..*4.*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))
@ -360,7 +409,7 @@ echo_i "check stale othertype.example (serve-stale reset) ($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
grep "othertype.example\..*2.*IN.*CAA.*0.*issue" dig.out.test$n > /dev/null || ret=1
grep "othertype.example\..*4.*IN.*CAA.*0.*issue" dig.out.test$n > /dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret))
@ -369,7 +418,7 @@ echo_i "check stale nodata.example (serve-stale reset) ($n)"
ret=0
grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1
grep "ANSWER: 0," dig.out.test$n > /dev/null || ret=1
grep "example\..*2.*IN.*SOA" dig.out.test$n > /dev/null || ret=1
grep "example\..*4.*IN.*SOA" dig.out.test$n > /dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret))
@ -378,7 +427,7 @@ echo_i "check stale nxdomain.example (serve-stale reset) ($n)"
ret=0
grep "status: NXDOMAIN" dig.out.test$n > /dev/null || ret=1
grep "ANSWER: 0," dig.out.test$n > /dev/null || ret=1
grep "example\..*2.*IN.*SOA" dig.out.test$n > /dev/null || ret=1
grep "example\..*4.*IN.*SOA" dig.out.test$n > /dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret))
@ -393,7 +442,7 @@ n=$((n+1))
echo_i "check 'rndc serve-stale status' ($n)"
ret=0
$RNDCCMD 10.53.0.1 serve-stale status > rndc.out.test$n 2>&1 || ret=1
grep '_default: off (rndc) (stale-answer-ttl=2 max-stale-ttl=3600)' rndc.out.test$n > /dev/null || ret=1
grep '_default: off (rndc) (stale-answer-ttl=4 max-stale-ttl=3600 stale-refresh-time=30)' rndc.out.test$n > /dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret))
@ -421,7 +470,7 @@ n=$((n+1))
echo_i "check 'rndc serve-stale status' ($n)"
ret=0
$RNDCCMD 10.53.0.1 serve-stale status > rndc.out.test$n 2>&1 || ret=1
grep '_default: off (rndc) (stale-answer-ttl=3 max-stale-ttl=20)' rndc.out.test$n > /dev/null || ret=1
grep '_default: off (rndc) (stale-answer-ttl=3 max-stale-ttl=20 stale-refresh-time=30)' rndc.out.test$n > /dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret))
@ -440,7 +489,7 @@ n=$((n+1))
echo_i "check 'rndc serve-stale status' ($n)"
ret=0
$RNDCCMD 10.53.0.1 serve-stale status > rndc.out.test$n 2>&1 || ret=1
grep '_default: on (rndc) (stale-answer-ttl=3 max-stale-ttl=20)' rndc.out.test$n > /dev/null || ret=1
grep '_default: on (rndc) (stale-answer-ttl=3 max-stale-ttl=20 stale-refresh-time=30)' rndc.out.test$n > /dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret))
@ -498,6 +547,10 @@ 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.
t1=`$PERL -e 'print time()'`
n=$((n+1))
echo_i "verify prime cache statistics (low max-stale-ttl) ($n)"
ret=0
@ -532,11 +585,7 @@ $DIG -p ${PORT} @10.53.0.1 othertype.example CAA > dig.out.test$((n+2)) &
$DIG -p ${PORT} @10.53.0.1 nodata.example TXT > dig.out.test$((n+3)) &
$DIG -p ${PORT} @10.53.0.1 nxdomain.example TXT > dig.out.test$((n+4))
# ensure all files have been written before proceeding
waitfile dig.out.test$((n+1))
waitfile dig.out.test$((n+2))
waitfile dig.out.test$((n+3))
waitfile dig.out.test$((n+4))
wait
n=$((n+1))
echo_i "check stale data.example (low max-stale-ttl) ($n)"
@ -594,17 +643,27 @@ 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,
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.
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 -lt $interval_to_ancient ]; then
sleep $((interval_to_ancient - elapsed))
fi
echo_i "sending queries for tests $((n+1))-$((n+4))..."
$DIG -p ${PORT} @10.53.0.1 data.example TXT > dig.out.test$((n+1)) &
$DIG -p ${PORT} @10.53.0.1 othertype.example CAA > dig.out.test$((n+2)) &
$DIG -p ${PORT} @10.53.0.1 nodata.example TXT > dig.out.test$((n+3)) &
$DIG -p ${PORT} @10.53.0.1 nxdomain.example TXT > dig.out.test$((n+4))
# ensure all files have been written before proceeding
waitfile dig.out.test$((n+1))
waitfile dig.out.test$((n+2))
waitfile dig.out.test$((n+3))
waitfile dig.out.test$((n+4))
wait
n=$((n+1))
echo_i "check ancient data.example (low max-stale-ttl) ($n)"
@ -638,6 +697,201 @@ grep "ANSWER: 0," dig.out.test$n > /dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret))
# Test stale-refresh-time when serve-stale is enabled via rndc.
# Steps for testing stale-refresh-time option (default).
# 1. Prime cache data.example txt
# 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)
# 6. Enable responses from authoritative server.
# 7. Query data.example
# 8. Check if response come from stale rrset, since the query
# is within stale-refresh-time window.
n=$((n+1))
echo_i "flush cache, enable responses from authoritative server ($n)"
ret=0
$RNDCCMD 10.53.0.1 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 "check 'rndc serve-stale status' ($n)"
ret=0
$RNDCCMD 10.53.0.1 serve-stale status > rndc.out.test$n 2>&1 || ret=1
grep '_default: on (rndc) (stale-answer-ttl=3 max-stale-ttl=20 stale-refresh-time=30)' rndc.out.test$n > /dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret))
# Step 1.
n=$((n+1))
echo_i "prime cache data.example (stale-refresh-time rndc) ($n)"
ret=0
$DIG -p ${PORT} @10.53.0.1 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))
# Step 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))
# Step 3.
sleep 2
# Step 4.
n=$((n+1))
echo_i "sending query for test ($n)"
$DIG -p ${PORT} @10.53.0.1 data.example TXT > dig.out.test$n
# Step 5.
echo_i "check stale data.example (stale-refresh-time rndc) ($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
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))
# Step 6.
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))
# Step 7.
echo_i "sending query for test $((n+1))"
$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 cache (stale-refresh-time rndc) ($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
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))
# Steps for testing stale-refresh-time option (disabled).
# 1. Prime cache data.example txt
# 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)
# 6. Enable responses from authoritative server.
# 7. Query data.example
# 8. Check if response come from stale rrset, since the query
# is within stale-refresh-time window.
n=$((n+1))
echo_i "updating ns1/named.conf ($n)"
ret=0
copy_setports ns1/named3.conf.in ns1/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 ns1 10.53.0.1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret))
n=$((n+1))
echo_i "check 'rndc serve-stale status' ($n)"
ret=0
$RNDCCMD 10.53.0.1 serve-stale status > rndc.out.test$n 2>&1 || ret=1
grep '_default: on (rndc) (stale-answer-ttl=3 max-stale-ttl=20 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 "flush cache, enable responses from authoritative server ($n)"
ret=0
$RNDCCMD 10.53.0.1 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))
# Step 1.
n=$((n+1))
echo_i "prime cache data.example (stale-refresh-time disabled) ($n)"
ret=0
$DIG -p ${PORT} @10.53.0.1 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))
# Step 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))
# Step 3.
sleep 2
# Step 4.
n=$((n+1))
echo_i "sending query for test ($n)"
$DIG -p ${PORT} @10.53.0.1 data.example TXT > dig.out.test$n
# Step 5.
echo_i "check stale data.example (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
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))
# Step 6.
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))
# Step 7.
echo_i "sending query for test $((n+1))"
$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)"
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\..*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))
#
# Now test server with no serve-stale options set.
#
@ -731,7 +985,7 @@ 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: off (stale-answer-ttl=1 max-stale-ttl=$max_stale_ttl)" rndc.out.test$n > /dev/null || ret=1
grep "_default: off (stale-answer-ttl=1 max-stale-ttl=$max_stale_ttl stale-refresh-time=30)" rndc.out.test$n > /dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret))
@ -743,11 +997,7 @@ $DIG -p ${PORT} @10.53.0.3 othertype.example CAA > dig.out.test$((n+2)) &
$DIG -p ${PORT} @10.53.0.3 nodata.example TXT > dig.out.test$((n+3)) &
$DIG -p ${PORT} @10.53.0.3 nxdomain.example TXT > dig.out.test$((n+4))
# ensure all files have been written before proceeding
waitfile dig.out.test$((n+1))
waitfile dig.out.test$((n+2))
waitfile dig.out.test$((n+3))
waitfile dig.out.test$((n+4))
wait
n=$((n+1))
echo_i "check fail of data.example (max-stale-ttl default) ($n)"
@ -812,7 +1062,7 @@ 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 (rndc) (stale-answer-ttl=1 max-stale-ttl=$max_stale_ttl)" rndc.out.test$n > /dev/null || ret=1
grep "_default: on (rndc) (stale-answer-ttl=1 max-stale-ttl=$max_stale_ttl stale-refresh-time=30)" rndc.out.test$n > /dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret))
@ -824,11 +1074,7 @@ $DIG -p ${PORT} @10.53.0.3 othertype.example CAA > dig.out.test$((n+2)) &
$DIG -p ${PORT} @10.53.0.3 nodata.example TXT > dig.out.test$((n+3)) &
$DIG -p ${PORT} @10.53.0.3 nxdomain.example TXT > dig.out.test$((n+4))
# ensure all files have been written before proceeding
waitfile dig.out.test$((n+1))
waitfile dig.out.test$((n+2))
waitfile dig.out.test$((n+3))
waitfile dig.out.test$((n+4))
wait
n=$((n+1))
echo_i "check data.example (max-stale-ttl default) ($n)"
@ -959,7 +1205,7 @@ n=$((n+1))
echo_i "check 'rndc serve-stale status' ($n)"
ret=0
$RNDCCMD 10.53.0.4 serve-stale status > rndc.out.test$n 2>&1 || ret=1
grep "_default: off (stale-answer-ttl=1 max-stale-ttl=$max_stale_ttl)" rndc.out.test$n > /dev/null || ret=1
grep "_default: off (stale-answer-ttl=1 max-stale-ttl=$max_stale_ttl stale-refresh-time=30)" rndc.out.test$n > /dev/null || ret=1
if [ $ret != 0 ]; then echo_i "failed"; fi
status=$((status+ret))
@ -971,11 +1217,7 @@ $DIG -p ${PORT} @10.53.0.4 othertype.example CAA > dig.out.test$((n+2)) &
$DIG -p ${PORT} @10.53.0.4 nodata.example TXT > dig.out.test$((n+3)) &
$DIG -p ${PORT} @10.53.0.4 nxdomain.example TXT > dig.out.test$((n+4))
# ensure all files have been written before proceeding
waitfile dig.out.test$((n+1))
waitfile dig.out.test$((n+2))
waitfile dig.out.test$((n+3))
waitfile dig.out.test$((n+4))
wait
n=$((n+1))
echo_i "check fail of data.example (serve-stale answers disabled) ($n)"
@ -1181,11 +1423,7 @@ $DIG -p ${PORT} @10.53.0.5 othertype.example CAA > dig.out.test$((n+2)) &
$DIG -p ${PORT} @10.53.0.5 nodata.example TXT > dig.out.test$((n+3)) &
$DIG -p ${PORT} @10.53.0.5 nxdomain.example TXT > dig.out.test$((n+4))
# ensure all files have been written before proceeding
waitfile dig.out.test$((n+1))
waitfile dig.out.test$((n+2))
waitfile dig.out.test$((n+3))
waitfile dig.out.test$((n+4))
wait
n=$((n+1))
echo_i "check fail of data.example (serve-stale cache disabled) ($n)"

View file

@ -1839,6 +1839,19 @@ Boolean Options
``stale-cache-enable``
If ``yes``, enable the retaining of "stale" cached answers. Default ``no``.
``stale-refresh-time``
If the name servers for a given zone are not answering, this sets the time
window for which ``named`` will promptly return "stale" cached answers for
that RRSet being requested before a new attempt in contacting the servers
is made. For convenience, TTL-style time-unit suffixes may be used to
specify the value. It also accepts ISO 8601 duration formats.
The default ``stale-refresh-time`` is 30 seconds, as RFC 8767 recommends
that attempts to refresh to be done no more frequently than every 30
seconds. A value of zero disables the feature, meaning that normal
resolution will take place first, if that fails only then ``named`` will
return "stale" cached answers.
``nocookie-udp-size``
This sets the maximum size of UDP responses that are sent to queries
without a valid server COOKIE. A value below 128 is silently

View file

@ -464,6 +464,7 @@ options {
stale\-answer\-enable boolean;
stale\-answer\-ttl duration;
stale\-cache\-enable boolean;
stale\-refresh\-time duration;
startup\-notify\-rate integer;
statistics\-file quoted_string;
synth\-from\-dnssec boolean;
@ -892,6 +893,7 @@ view string [ class ] {
stale\-answer\-enable boolean;
stale\-answer\-ttl duration;
stale\-cache\-enable boolean;
stale\-refresh\-time duration;
synth\-from\-dnssec boolean;
transfer\-format ( many\-answers | one\-answer );
transfer\-source ( ipv4_address | * ) [ port ( integer | * ) ] [

View file

@ -366,6 +366,7 @@ options {
stale-answer-enable <boolean>;
stale-answer-ttl <duration>;
stale-cache-enable <boolean>;
stale-refresh-time <duration>;
startup-notify-rate <integer>;
statistics-file <quoted_string>;
statistics-interval <integer>; // ancient
@ -758,6 +759,7 @@ view <string> [ <class> ] {
stale-answer-enable <boolean>;
stale-answer-ttl <duration>;
stale-cache-enable <boolean>;
stale-refresh-time <duration>;
suppress-initial-notify <boolean>; // not yet implemented
synth-from-dnssec <boolean>;
topology { <address_match_element>; ... }; // ancient

View file

@ -329,6 +329,7 @@ options {
stale-answer-enable <boolean>;
stale-answer-ttl <duration>;
stale-cache-enable <boolean>;
stale-refresh-time <duration>;
startup-notify-rate <integer>;
statistics-file <quoted_string>;
synth-from-dnssec <boolean>;
@ -686,6 +687,7 @@ view <string> [ <class> ] {
stale-answer-enable <boolean>;
stale-answer-ttl <duration>;
stale-cache-enable <boolean>;
stale-refresh-time <duration>;
synth-from-dnssec <boolean>;
transfer-format ( many-answers | one-answer );
transfer-source ( <ipv4_address> | * ) [ port ( <integer> | * ) ] [

View file

@ -259,6 +259,7 @@
stale-answer-enable <boolean>;
stale-answer-ttl <duration>;
stale-cache-enable <boolean>;
stale-refresh-time <duration>;
startup-notify-rate <integer>;
statistics-file <quoted_string>;
synth-from-dnssec <boolean>;

View file

@ -26,6 +26,10 @@ New Features
- None.
- A new configuration option ``stale-refresh-time`` has been introduced, it
allows stale RRset to be served directly from cache for a period of time
after a failed lookup, before a new attempt to refresh it is made. [GL #2066]
Removed Features
~~~~~~~~~~~~~~~~

View file

@ -1662,6 +1662,17 @@ check_options(const cfg_obj_t *options, isc_log_t *logctx, isc_mem_t *mctx,
}
}
obj = NULL;
(void)cfg_map_get(options, "stale-refresh-time", &obj);
if (obj != NULL) {
uint32_t refresh_time = cfg_obj_asduration(obj);
if (refresh_time > 0 && refresh_time < 30) {
cfg_obj_log(obj, logctx, ISC_LOG_WARNING,
"'stale-refresh-time' should either be 0 "
"or otherwise 30 seconds or higher");
}
}
return (result);
}

View file

@ -142,6 +142,7 @@ struct dns_cache {
char **db_argv;
size_t size;
dns_ttl_t serve_stale_ttl;
dns_ttl_t serve_stale_refresh;
isc_stats_t *stats;
/* Locked by 'filelock'. */
@ -999,6 +1000,28 @@ dns_cache_getservestalettl(dns_cache_t *cache) {
return (result == ISC_R_SUCCESS ? ttl : 0);
}
void
dns_cache_setservestalerefresh(dns_cache_t *cache, dns_ttl_t interval) {
REQUIRE(VALID_CACHE(cache));
LOCK(&cache->lock);
cache->serve_stale_refresh = interval;
UNLOCK(&cache->lock);
(void)dns_db_setservestalerefresh(cache->db, interval);
}
dns_ttl_t
dns_cache_getservestalerefresh(dns_cache_t *cache) {
isc_result_t result;
dns_ttl_t interval;
REQUIRE(VALID_CACHE(cache));
result = dns_db_getservestalerefresh(cache->db, &interval);
return (result == ISC_R_SUCCESS ? interval : 0);
}
/*
* The cleaner task is shutting down; do the necessary cleanup.
*/

View file

@ -1089,6 +1089,28 @@ dns_db_getservestalettl(dns_db_t *db, dns_ttl_t *ttl) {
return (ISC_R_NOTIMPLEMENTED);
}
isc_result_t
dns_db_setservestalerefresh(dns_db_t *db, uint32_t interval) {
REQUIRE(DNS_DB_VALID(db));
REQUIRE((db->attributes & DNS_DBATTR_CACHE) != 0);
if (db->methods->setservestalerefresh != NULL) {
return ((db->methods->setservestalerefresh)(db, interval));
}
return (ISC_R_NOTIMPLEMENTED);
}
isc_result_t
dns_db_getservestalerefresh(dns_db_t *db, uint32_t *interval) {
REQUIRE(DNS_DB_VALID(db));
REQUIRE((db->attributes & DNS_DBATTR_CACHE) != 0);
if (db->methods->getservestalerefresh != NULL) {
return ((db->methods->getservestalerefresh)(db, interval));
}
return (ISC_R_NOTIMPLEMENTED);
}
isc_result_t
dns_db_setgluecachestats(dns_db_t *db, isc_stats_t *stats) {
REQUIRE(dns_db_iszone(db));

View file

@ -967,6 +967,8 @@ static dns_dbmethods_t rpsdb_db_methods = {
NULL, /* getsize */
NULL, /* setservestalettl */
NULL, /* getservestalettl */
NULL, /* setservestalerefresh */
NULL, /* getservestalerefresh */
NULL, /* setgluecachestats */
NULL /* adjusthashsize */
};

View file

@ -255,6 +255,28 @@ dns_cache_getservestalettl(dns_cache_t *cache);
*\li 'cache' to be valid.
*/
void
dns_cache_setservestalerefresh(dns_cache_t *cache, dns_ttl_t interval);
/*%<
* Sets the length of time to wait before attempting to refresh a rrset
* if a previous attempt in doing so has failed.
* During this time window if stale rrset are available in cache they
* will be directly returned to client.
*
* Requires:
*\li 'cache' to be valid.
*/
dns_ttl_t
dns_cache_getservestalerefresh(dns_cache_t *cache);
/*%<
* Gets the 'stale-refresh-time' value, set by a previous call to
* 'dns_cache_setservestalerefresh'.
*
* Requires:
*\li 'cache' to be valid.
*/
isc_result_t
dns_cache_flush(dns_cache_t *cache);
/*%<

View file

@ -178,6 +178,8 @@ typedef struct dns_dbmethods {
uint64_t *records, uint64_t *bytes);
isc_result_t (*setservestalettl)(dns_db_t *db, dns_ttl_t ttl);
isc_result_t (*getservestalettl)(dns_db_t *db, dns_ttl_t *ttl);
isc_result_t (*setservestalerefresh)(dns_db_t *db, uint32_t interval);
isc_result_t (*getservestalerefresh)(dns_db_t *db, uint32_t *interval);
isc_result_t (*setgluecachestats)(dns_db_t *db, isc_stats_t *stats);
isc_result_t (*adjusthashsize)(dns_db_t *db, size_t size);
} dns_dbmethods_t;
@ -238,6 +240,7 @@ struct dns_dbonupdatelistener {
#define DNS_DBFIND_ADDITIONALOK 0x0100
#define DNS_DBFIND_NOZONECUT 0x0200
#define DNS_DBFIND_STALEOK 0x0400
#define DNS_DBFIND_STALEENABLED 0x0800
/*@}*/
/*@{*/
@ -1701,6 +1704,39 @@ dns_db_getservestalettl(dns_db_t *db, dns_ttl_t *ttl);
* \li #ISC_R_NOTIMPLEMENTED - Not supported by this DB implementation.
*/
isc_result_t
dns_db_setservestalerefresh(dns_db_t *db, uint32_t interval);
/*%<
* Sets the length of time to wait before attempting to refresh a rrset
* if a previous attempt in doing so has failed.
* During this time window if stale rrset are available in cache they
* will be directly returned to client.
*
* Requires:
* \li 'db' is a valid cache database.
* \li 'interval' is number of seconds before attempting to refresh data.
*
* Returns:
* \li #ISC_R_SUCCESS
* \li #ISC_R_NOTIMPLEMENTED - Not supported by this DB implementation.
*/
isc_result_t
dns_db_getservestalerefresh(dns_db_t *db, uint32_t *interval);
/*%<
* Gets the length of time in which stale answers are directly returned from
* cache before attempting to refresh them, in case a previous attempt in
* doing so has failed.
*
* Requires:
* \li 'db' is a valid cache database.
* \li 'interval' is number of seconds before attempting to refresh data.
*
* Returns:
* \li #ISC_R_SUCCESS
* \li #ISC_R_NOTIMPLEMENTED - Not supported by this DB implementation.
*/
isc_result_t
dns_db_setgluecachestats(dns_db_t *db, isc_stats_t *stats);
/*%<

View file

@ -205,6 +205,7 @@ typedef struct rdatasetheader {
rbtdb_rdatatype_t type;
atomic_uint_least16_t attributes;
dns_trust_t trust;
isc_stdtime_t last_refresh_fail_ts;
struct noqname *noqname;
struct noqname *closest;
unsigned int is_mmapped : 1;
@ -488,6 +489,13 @@ struct dns_rbtdb {
*/
dns_ttl_t serve_stale_ttl;
/*
* The time after a failed lookup, where stale answers from cache
* may be used directly in a DNS response without attempting a
* new iterative lookup.
*/
uint32_t serve_stale_refresh;
/*
* This is a linked list used to implement the LRU cache. There will
* be node_lock_count linked lists here. Nodes in bucket 1 will be
@ -4547,6 +4555,27 @@ check_stale_header(dns_rbtnode_t *node, rdatasetheader_t *header,
stale > search->now) {
mark_header_stale(search->rbtdb, header);
*header_prev = header;
/*
* If DNS_DBFIND_STALEOK is set then it means we failed
* to resolve the name during recursion, in this case we
* mark the time in which the refresh failed.
*/
if ((search->options & DNS_DBFIND_STALEOK) != 0) {
header->last_refresh_fail_ts = search->now;
} else if ((search->options &
DNS_DBFIND_STALEENABLED) != 0 &&
search->now <
(header->last_refresh_fail_ts +
search->rbtdb->serve_stale_refresh))
{
/*
* If we are within interval between last
* refresh failure time + 'stale-refresh-time',
* then don't skip this stale entry but use it
* instead.
*/
return (false);
}
return ((search->options & DNS_DBFIND_STALEOK) == 0);
}
@ -8379,6 +8408,29 @@ getservestalettl(dns_db_t *db, dns_ttl_t *ttl) {
return (ISC_R_SUCCESS);
}
static isc_result_t
setservestalerefresh(dns_db_t *db, uint32_t interval) {
dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
REQUIRE(VALID_RBTDB(rbtdb));
REQUIRE(IS_CACHE(rbtdb));
/* currently no bounds checking. 0 means disable. */
rbtdb->serve_stale_refresh = interval;
return (ISC_R_SUCCESS);
}
static isc_result_t
getservestalerefresh(dns_db_t *db, uint32_t *interval) {
dns_rbtdb_t *rbtdb = (dns_rbtdb_t *)db;
REQUIRE(VALID_RBTDB(rbtdb));
REQUIRE(IS_CACHE(rbtdb));
*interval = rbtdb->serve_stale_refresh;
return (ISC_R_SUCCESS);
}
static dns_dbmethods_t zone_methods = { attach,
detach,
beginload,
@ -8426,6 +8478,8 @@ static dns_dbmethods_t zone_methods = { attach,
getsize,
NULL, /* setservestalettl */
NULL, /* getservestalettl */
NULL, /* setservestalerefresh */
NULL, /* getservestalerefresh */
setgluecachestats,
adjusthashsize };
@ -8476,6 +8530,8 @@ static dns_dbmethods_t cache_methods = { attach,
NULL, /* getsize */
setservestalettl,
getservestalettl,
setservestalerefresh,
getservestalerefresh,
NULL,
adjusthashsize };

View file

@ -1309,6 +1309,8 @@ static dns_dbmethods_t sdb_methods = {
NULL, /* getsize */
NULL, /* setservestalettl */
NULL, /* getservestalettl */
NULL, /* setservestalerefresh */
NULL, /* getservestalerefresh */
NULL, /* setgluecachestats */
NULL /* adjusthashsize */
};

View file

@ -1281,6 +1281,8 @@ static dns_dbmethods_t sdlzdb_methods = {
NULL, /* getsize */
NULL, /* setservestalettl */
NULL, /* getservestalettl */
NULL, /* setservestalerefresh */
NULL, /* getservestalerefresh */
NULL, /* setgluecachestats */
NULL /* adjusthashsize */
};

View file

@ -82,6 +82,7 @@ dns_cache_flushname
dns_cache_flushnode
dns_cache_getcachesize
dns_cache_getname
dns_cache_getservestalerefresh
dns_cache_getservestalettl
dns_cache_getstats
dns_cache_load
@ -93,6 +94,7 @@ dns_cache_renderxml
@END LIBXML2
dns_cache_setcachesize
dns_cache_setfilename
dns_cache_setservestalerefresh
dns_cache_setservestalettl
dns_cache_updatestats
dns_catz_add_zone
@ -198,6 +200,7 @@ dns_db_getnsec3parameters
dns_db_getoriginnode
dns_db_getrrsetstats
dns_db_getservestalettl
dns_db_getservestalerefresh
dns_db_getsigningtime
dns_db_getsize
dns_db_getsoaserial
@ -223,6 +226,7 @@ dns_db_serialize
dns_db_setcachestats
dns_db_setgluecachestats
dns_db_setservestalettl
dns_db_setservestalerefresh
dns_db_setsigningtime
dns_db_settask
dns_db_subtractrdataset

View file

@ -2051,6 +2051,7 @@ static cfg_clausedef_t view_clauses[] = {
{ "stale-answer-enable", &cfg_type_boolean, 0 },
{ "stale-answer-ttl", &cfg_type_duration, 0 },
{ "stale-cache-enable", &cfg_type_boolean, 0 },
{ "stale-refresh-time", &cfg_type_duration, 0 },
{ "suppress-initial-notify", &cfg_type_boolean, CFG_CLAUSEFLAG_NYI },
{ "synth-from-dnssec", &cfg_type_boolean, 0 },
{ "topology", &cfg_type_bracketed_aml, CFG_CLAUSEFLAG_ANCIENT },

View file

@ -5523,6 +5523,9 @@ query_lookup(query_ctx_t *qctx) {
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;
CCTRACE(ISC_LOG_DEBUG(3), "query_lookup");
@ -5581,6 +5584,22 @@ query_lookup(query_ctx_t *qctx) {
dboptions |= DNS_DBFIND_COVERINGNSEC;
}
dns_db_getservestalerefresh(qctx->client->view->cachedb,
&stale_refresh);
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;
}
}
}
result = dns_db_findext(qctx->db, rpzqname, qctx->version, qctx->type,
dboptions, qctx->client->now, &qctx->node,
qctx->fname, &cm, &ci, qctx->rdataset,
@ -5601,10 +5620,28 @@ query_lookup(query_ctx_t *qctx) {
dns_cache_updatestats(qctx->view->cache, result);
}
if ((qctx->client->query.dboptions & DNS_DBFIND_STALEOK) != 0) {
/*
* 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.
*/
dbfind_stale = ((dboptions & DNS_DBFIND_STALEOK) != 0);
if (dbfind_stale != 0 ||
(((dboptions & DNS_DBFIND_STALEENABLED) != 0) &&
STALE(qctx->rdataset)))
{
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 &&
@ -5618,10 +5655,20 @@ query_lookup(query_ctx_t *qctx) {
dns_name_format(qctx->client->query.qname, namebuf,
sizeof(namebuf));
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");
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 {
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");
}
if (!success) {
QUERY_ERROR(qctx, DNS_R_SERVFAIL);
@ -6833,7 +6880,6 @@ query_usestale(query_ctx_t *qctx) {
if (staleanswersok) {
qctx->client->query.dboptions |= DNS_DBFIND_STALEOK;
inc_stats(qctx->client, ns_statscounter_trystale);
if (qctx->client->query.fetch != NULL) {
dns_resolver_destroyfetch(&qctx->client->query.fetch);
}