diff --git a/CHANGES b/CHANGES index b54bd83a2a..40b003e472 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,10 @@ +5484. [func] Expire the 0 TTL RRSet quickly rather using them for + stale answers. [GL #1829] + +5483. [func] Keeping "stale" answers in cache has been disabled by + default and can be re-enabled with a new configuration + option "stale-cache-enable". [GL #1712] + 5482. [bug] BIND 9 would fail to bind to IPv6 addresses in a tentative state when a new IPv6 address was added to the system, but the Duplicate Address Detection (DAD) diff --git a/bin/named/config.c b/bin/named/config.c index f073bcd328..de45c0a752 100644 --- a/bin/named/config.c +++ b/bin/named/config.c @@ -196,6 +196,7 @@ options {\n\ # sortlist \n\ stale-answer-enable false;\n\ stale-answer-ttl 1; /* 1 second */\n\ + stale-cache-enable false;\n\ synth-from-dnssec no;\n\ # topology \n\ transfer-format many-answers;\n\ diff --git a/bin/named/named.conf.rst b/bin/named/named.conf.rst index 2b982d580a..f66c485ad6 100644 --- a/bin/named/named.conf.rst +++ b/bin/named/named.conf.rst @@ -401,6 +401,7 @@ OPTIONS stacksize ( default | unlimited | sizeval ); stale-answer-enable boolean; stale-answer-ttl duration; + stale-cache-enable boolean; startup-notify-rate integer; statistics-file quoted_string; synth-from-dnssec boolean; @@ -785,6 +786,7 @@ VIEW sortlist { address_match_element; ... }; stale-answer-enable boolean; stale-answer-ttl duration; + stale-cache-enable boolean; synth-from-dnssec boolean; transfer-format ( many-answers | one-answer ); transfer-source ( ipv4_address | * ) [ port ( integer | * ) ] [ diff --git a/bin/named/server.c b/bin/named/server.c index 81cf4b592f..88726bb4c7 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -3895,7 +3895,7 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config, uint32_t max_cache_size_percent = 0; size_t max_adb_size; uint32_t lame_ttl, fail_ttl; - uint32_t max_stale_ttl; + uint32_t max_stale_ttl = 0; dns_tsig_keyring_t *ring = NULL; dns_view_t *pview = NULL; /* Production view */ isc_mem_t *cmctx = NULL, *hmctx = NULL; @@ -4358,9 +4358,18 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config, view->synthfromdnssec = cfg_obj_asboolean(obj); obj = NULL; - result = named_config_get(maps, "max-stale-ttl", &obj); + result = named_config_get(maps, "stale-cache-enable", &obj); INSIST(result == ISC_R_SUCCESS); - max_stale_ttl = ISC_MAX(cfg_obj_asduration(obj), 1); + if (cfg_obj_asboolean(obj)) { + obj = NULL; + result = named_config_get(maps, "max-stale-ttl", &obj); + INSIST(result == ISC_R_SUCCESS); + max_stale_ttl = ISC_MAX(cfg_obj_asduration(obj), 1); + } + /* + * If 'stale-cache-enable' is false, max_stale_ttl is set to 0, + * meaning keeping stale RRsets in cache is disabled. + */ obj = NULL; result = named_config_get(maps, "stale-answer-enable", &obj); diff --git a/bin/tests/system/serve-stale/ns1/named1.conf.in b/bin/tests/system/serve-stale/ns1/named1.conf.in index 04fe0ce3bd..6586849d85 100644 --- a/bin/tests/system/serve-stale/ns1/named1.conf.in +++ b/bin/tests/system/serve-stale/ns1/named1.conf.in @@ -30,6 +30,7 @@ options { max-stale-ttl 3600; stale-answer-ttl 2; stale-answer-enable yes; + stale-cache-enable yes; servfail-ttl 0; }; diff --git a/bin/tests/system/serve-stale/ns1/named2.conf.in b/bin/tests/system/serve-stale/ns1/named2.conf.in index 6361f591a6..06fae5369c 100644 --- a/bin/tests/system/serve-stale/ns1/named2.conf.in +++ b/bin/tests/system/serve-stale/ns1/named2.conf.in @@ -30,6 +30,7 @@ options { max-stale-ttl 20; stale-answer-ttl 3; stale-answer-enable yes; + stale-cache-enable yes; servfail-ttl 0; }; diff --git a/bin/tests/system/serve-stale/ns3/named.conf.in b/bin/tests/system/serve-stale/ns3/named.conf.in index 451cfd47ae..d76b348151 100644 --- a/bin/tests/system/serve-stale/ns3/named.conf.in +++ b/bin/tests/system/serve-stale/ns3/named.conf.in @@ -28,7 +28,7 @@ options { listen-on-v6 { none; }; recursion yes; dump-file "named_dump3.db"; - // This configuration has no serve-stale options set. + stale-cache-enable yes; }; zone "." { diff --git a/bin/tests/system/serve-stale/ns4/named.conf.in b/bin/tests/system/serve-stale/ns4/named.conf.in index c2b05e1ec3..392ec1d6f5 100644 --- a/bin/tests/system/serve-stale/ns4/named.conf.in +++ b/bin/tests/system/serve-stale/ns4/named.conf.in @@ -29,6 +29,7 @@ options { recursion yes; dump-file "named_dump4.db"; stale-answer-enable no; + stale-cache-enable yes; }; zone "." { diff --git a/bin/tests/system/serve-stale/ns5/named.conf.in b/bin/tests/system/serve-stale/ns5/named.conf.in new file mode 100644 index 0000000000..7fdba0432f --- /dev/null +++ b/bin/tests/system/serve-stale/ns5/named.conf.in @@ -0,0 +1,40 @@ +/* + * 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.5 port @CONTROLPORT@ allow { any; } keys { rndc_key; }; +}; + +options { + query-source address 10.53.0.5; + notify-source 10.53.0.5; + transfer-source 10.53.0.5; + port @PORT@; + pid-file "named.pid"; + listen-on { 10.53.0.5; }; + listen-on-v6 { none; }; + recursion yes; + dump-file "named_dump5.db"; + stale-answer-enable yes; + stale-cache-enable no; + max-cache-ttl 24h; +}; + +zone "." { + type secondary; + primaries { 10.53.0.1; }; + file "root.bk"; +}; diff --git a/bin/tests/system/serve-stale/setup.sh b/bin/tests/system/serve-stale/setup.sh index 963278e976..868c6c876c 100644 --- a/bin/tests/system/serve-stale/setup.sh +++ b/bin/tests/system/serve-stale/setup.sh @@ -16,3 +16,4 @@ $SHELL clean.sh copy_setports ns1/named1.conf.in ns1/named.conf copy_setports ns3/named.conf.in ns3/named.conf copy_setports ns4/named.conf.in ns4/named.conf +copy_setports ns5/named.conf.in ns5/named.conf diff --git a/bin/tests/system/serve-stale/tests.sh b/bin/tests/system/serve-stale/tests.sh index 7c9de93bdd..a9f2098f4e 100755 --- a/bin/tests/system/serve-stale/tests.sh +++ b/bin/tests/system/serve-stale/tests.sh @@ -84,7 +84,7 @@ $RNDCCMD 10.53.0.1 stats > /dev/null 2>&1 [ -f ns1/named.stats ] || ret=1 cp ns1/named.stats ns1/named.stats.$n # Check first 10 lines of Cache DB statistics. After prime queries, we expect -# two active TXT one nxrrset TXT, and one NXDOMAIN. +# two active TXT, one active Others, one nxrrset TXT, and one NXDOMAIN. grep -A 10 "++ Cache DB RRsets ++" ns1/named.stats.$n > ns1/named.stats.$n.cachedb || ret=1 grep "1 Others" ns1/named.stats.$n.cachedb > /dev/null || ret=1 grep "2 TXT" ns1/named.stats.$n.cachedb > /dev/null || ret=1 @@ -500,7 +500,7 @@ $RNDCCMD 10.53.0.1 stats > /dev/null 2>&1 [ -f ns1/named.stats ] || ret=1 cp ns1/named.stats ns1/named.stats.$n # Check first 10 lines of Cache DB statistics. After prime queries, we expect -# two active TXT RRsets, one nxrrset TXT, and one NXDOMAIN. +# two active TXT RRsets, one active Others, one nxrrset TXT, and one NXDOMAIN. grep -A 10 "++ Cache DB RRsets ++" ns1/named.stats.$n > ns1/named.stats.$n.cachedb || ret=1 grep "2 TXT" ns1/named.stats.$n.cachedb > /dev/null || ret=1 grep "1 Others" ns1/named.stats.$n.cachedb > /dev/null || ret=1 @@ -703,7 +703,7 @@ $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 prime queries, we expect -# two active TXT RRsets, one nxrrset TXT, and one NXDOMAIN. +# two active TXT RRsets, one active Others, one nxrrset TXT, and one NXDOMAIN. grep -A 10 "++ Cache DB RRsets ++" ns3/named.stats.$n > ns3/named.stats.$n.cachedb || ret=1 grep "2 TXT" ns3/named.stats.$n.cachedb > /dev/null || ret=1 grep "1 Others" ns3/named.stats.$n.cachedb > /dev/null || ret=1 @@ -861,7 +861,7 @@ if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status+ret)) # -# Now test server with serve-stale disabled. +# Now test server with serve-stale answers disabled. # echo_i "test server with serve-stale disabled" @@ -875,7 +875,7 @@ if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status+ret)) n=$((n+1)) -echo_i "prime cache longttl.example (serve-stale disabled) ($n)" +echo_i "prime cache longttl.example (serve-stale answers disabled) ($n)" ret=0 $DIG -p ${PORT} @10.53.0.4 longttl.example TXT > dig.out.test$n grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1 @@ -884,7 +884,7 @@ if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status+ret)) n=$((n+1)) -echo_i "prime cache data.example (serve-stale disabled) ($n)" +echo_i "prime cache data.example (serve-stale answers disabled) ($n)" ret=0 $DIG -p ${PORT} @10.53.0.4 data.example TXT > dig.out.test$n grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1 @@ -894,7 +894,7 @@ if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status+ret)) n=$((n+1)) -echo_i "prime cache othertype.example (serve-stale disabled) ($n)" +echo_i "prime cache othertype.example (serve-stale answers disabled) ($n)" ret=0 $DIG -p ${PORT} @10.53.0.4 othertype.example CAA > dig.out.test$n grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1 @@ -904,7 +904,7 @@ if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status+ret)) n=$((n+1)) -echo_i "prime cache nodata.example (serve-stale disabled) ($n)" +echo_i "prime cache nodata.example (serve-stale answers disabled) ($n)" ret=0 $DIG -p ${PORT} @10.53.0.4 nodata.example TXT > dig.out.test$n grep "status: NOERROR" dig.out.test$n > /dev/null || ret=1 @@ -914,7 +914,7 @@ if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status+ret)) n=$((n+1)) -echo_i "prime cache nxdomain.example (serve-stale disabled) ($n)" +echo_i "prime cache nxdomain.example (serve-stale answers disabled) ($n)" ret=0 $DIG -p ${PORT} @10.53.0.4 nxdomain.example TXT > dig.out.test$n grep "status: NXDOMAIN" dig.out.test$n > /dev/null || ret=1 @@ -924,14 +924,14 @@ if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status+ret)) n=$((n+1)) -echo_i "verify prime cache statistics (serve-stale disabled) ($n)" +echo_i "verify prime cache statistics (serve-stale answers disabled) ($n)" ret=0 rm -f ns4/named.stats $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 prime queries, we expect -# two active TXT RRsets, one nxrrset TXT, and one NXDOMAIN. +# two active TXT RRsets, one active Others, one nxrrset TXT, and one NXDOMAIN. grep -A 10 "++ Cache DB RRsets ++" ns4/named.stats.$n > ns4/named.stats.$n.cachedb || ret=1 grep "2 TXT" ns4/named.stats.$n.cachedb > /dev/null || ret=1 grep "1 Others" ns4/named.stats.$n.cachedb > /dev/null || ret=1 @@ -972,7 +972,7 @@ waitfile dig.out.test$((n+3)) waitfile dig.out.test$((n+4)) n=$((n+1)) -echo_i "check fail of data.example (serve-stale disabled) ($n)" +echo_i "check fail of data.example (serve-stale answers disabled) ($n)" ret=0 grep "status: SERVFAIL" dig.out.test$n > /dev/null || ret=1 grep "ANSWER: 0," dig.out.test$n > /dev/null || ret=1 @@ -980,7 +980,7 @@ if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status+ret)) n=$((n+1)) -echo_i "check fail of othertype.example (serve-stale disabled) ($n)" +echo_i "check fail of othertype.example (serve-stale answers disabled) ($n)" ret=0 grep "status: SERVFAIL" dig.out.test$n > /dev/null || ret=1 grep "ANSWER: 0," dig.out.test$n > /dev/null || ret=1 @@ -988,7 +988,7 @@ if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status+ret)) n=$((n+1)) -echo_i "check fail of nodata.example (serve-stale disabled) ($n)" +echo_i "check fail of nodata.example (serve-stale answers disabled) ($n)" ret=0 grep "status: SERVFAIL" dig.out.test$n > /dev/null || ret=1 grep "ANSWER: 0," dig.out.test$n > /dev/null || ret=1 @@ -996,7 +996,7 @@ if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status+ret)) n=$((n+1)) -echo_i "check fail of nxdomain.example (serve-stale disabled) ($n)" +echo_i "check fail of nxdomain.example (serve-stale answers disabled) ($n)" ret=0 grep "status: SERVFAIL" dig.out.test$n > /dev/null || ret=1 grep "ANSWER: 0," dig.out.test$n > /dev/null || ret=1 @@ -1004,7 +1004,7 @@ if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status+ret)) n=$((n+1)) -echo_i "verify stale cache statistics (serve-stale disabled) ($n)" +echo_i "verify stale cache statistics (serve-stale answers disabled) ($n)" ret=0 rm -f ns4/named.stats $RNDCCMD 10.53.0.4 stats > /dev/null 2>&1 @@ -1024,7 +1024,7 @@ if [ $ret != 0 ]; then echo_i "failed"; fi # Dump the cache. n=$((n+1)) -echo_i "dump the cache (serve-stale disabled) ($n)" +echo_i "dump the cache (serve-stale answers disabled) ($n)" ret=0 $RNDCCMD 10.53.0.4 dumpdb -cache > rndc.out.test$n 2>&1 || ret=1 done=0 @@ -1049,7 +1049,7 @@ LASTWEEK=`TZ=UTC perl -e 'my $now = time(); printf("%04d%02d%02d%02d%02d%02d", $y+1900, $mo+1, $d, $h, $m, $s);'` n=$((n+1)) -echo_i "mock the cache date to $LASTWEEK (serve-stale disabled) ($n)" +echo_i "mock the cache date to $LASTWEEK (serve-stale answers disabled) ($n)" ret=0 sed -E "s/DATE [0-9]{14}/DATE $LASTWEEK/g" ns4/named_dump4.db > ns4/named_dumpdb4.db.out || ret=1 cp ns4/named_dumpdb4.db.out ns4/named_dumpdb4.db @@ -1060,7 +1060,7 @@ echo_i "start ns4" start_server --noclean --restart --port ${PORT} serve-stale ns4 n=$((n+1)) -echo_i "verify ancient cache statistics (serve-stale disabled) ($n)" +echo_i "verify ancient cache statistics (serve-stale answers disabled) ($n)" ret=0 rm -f ns4/named.stats $RNDCCMD 10.53.0.4 stats #> /dev/null 2>&1 @@ -1076,5 +1076,217 @@ grep "#NXDOMAIN" ns4/named.stats.$n.cachedb > /dev/null && ret=1 status=$((status+ret)) if [ $ret != 0 ]; then echo_i "failed"; fi +# +# Test the server with stale-cache disabled. +# +echo_i "test server with serve-stale cache disabled" + +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 longttl.example (serve-stale cache disabled) ($n)" +ret=0 +$DIG -p ${PORT} @10.53.0.5 longttl.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 data.example (serve-stale cache disabled) ($n)" +ret=0 +$DIG -p ${PORT} @10.53.0.5 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)) + +n=$((n+1)) +echo_i "prime cache othertype.example (serve-stale cache disabled) ($n)" +ret=0 +$DIG -p ${PORT} @10.53.0.5 othertype.example CAA > 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 "othertype\.example\..*2.*IN.*CAA.*0.*issue" 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 (serve-stale cache disabled) ($n)" +ret=0 +$DIG -p ${PORT} @10.53.0.5 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 +grep "example\..*2.*IN.*SOA" 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 nxdomain.example (serve-stale cache disabled) ($n)" +ret=0 +$DIG -p ${PORT} @10.53.0.5 nxdomain.example TXT > dig.out.test$n +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 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +n=$((n+1)) +echo_i "verify prime cache statistics (serve-stale cache disabled) ($n)" +ret=0 +rm -f ns5/named.stats +$RNDCCMD 10.53.0.5 stats > /dev/null 2>&1 +[ -f ns5/named.stats ] || ret=1 +cp ns5/named.stats ns5/named.stats.$n +# Check first 10 lines of Cache DB statistics. After serve-stale queries, +# we expect two active TXT RRsets, one active Others, one nxrrset TXT, and +# one NXDOMAIN. +grep -A 10 "++ Cache DB RRsets ++" ns5/named.stats.$n > ns5/named.stats.$n.cachedb || ret=1 +grep "2 TXT" ns5/named.stats.$n.cachedb > /dev/null || ret=1 +grep "1 Others" ns5/named.stats.$n.cachedb > /dev/null || ret=1 +grep "1 !TXT" ns5/named.stats.$n.cachedb > /dev/null || ret=1 +grep "1 NXDOMAIN" ns5/named.stats.$n.cachedb > /dev/null || ret=1 +status=$((status+ret)) +if [ $ret != 0 ]; then echo_i "failed"; fi + +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 "check 'rndc serve-stale status' ($n)" +ret=0 +$RNDCCMD 10.53.0.5 serve-stale status > rndc.out.test$n 2>&1 || ret=1 +grep "_default: off (not-cached)" rndc.out.test$n > /dev/null || ret=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +sleep 2 + +echo_i "sending queries for tests $((n+1))-$((n+4))..." +$DIG -p ${PORT} @10.53.0.5 data.example TXT > dig.out.test$((n+1)) & +$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)) + +n=$((n+1)) +echo_i "check fail of data.example (serve-stale cache disabled) ($n)" +ret=0 +grep "status: SERVFAIL" 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 "check fail of othertype.example (serve-stale cache disabled) ($n)" +ret=0 +grep "status: SERVFAIL" 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 "check fail of nodata.example (serve-stale cache disabled) ($n)" +ret=0 +grep "status: SERVFAIL" 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 "check fail of nxdomain.example (serve-stale cache disabled) ($n)" +ret=0 +grep "status: SERVFAIL" 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 "verify stale cache statistics (serve-stale cache disabled) ($n)" +ret=0 +rm -f ns5/named.stats +$RNDCCMD 10.53.0.5 stats > /dev/null 2>&1 +[ -f ns5/named.stats ] || ret=1 +cp ns5/named.stats ns5/named.stats.$n +# Check first 10 lines of Cache DB statistics. After serve-stale queries, +# we expect one active TXT (longttl) and the rest to be expired from cache. +grep -A 10 "++ Cache DB RRsets ++" ns5/named.stats.$n > ns5/named.stats.$n.cachedb || ret=1 +grep -F "1 Others" ns5/named.stats.$n.cachedb > /dev/null || ret=1 +grep -F "2 TXT" ns5/named.stats.$n.cachedb > /dev/null || ret=1 +grep -F "1 !TXT" ns5/named.stats.$n.cachedb > /dev/null || ret=1 +grep -F "1 NXDOMAIN" ns5/named.stats.$n.cachedb > /dev/null || ret=1 +status=$((status+ret)) +if [ $ret != 0 ]; then echo_i "failed"; fi + +# Dump the cache. +n=$((n+1)) +echo_i "dump the cache (serve-stale cache disabled) ($n)" +ret=0 +$RNDCCMD 10.53.0.5 dumpdb -cache > rndc.out.test$n 2>&1 || ret=1 +done=0 +for i in 0 1 2 3 4 5 6 7 8 9; do + grep '^; Dump complete$' ns5/named_dump5.db > /dev/null 2>&1 && done=1 + if [ $done != 1 ]; then sleep 1; fi +done +if [ $done != 1 ]; then ret=1; fi +status=$((status+ret)) +if [ $ret != 0 ]; then echo_i "failed"; fi + +echo_i "stop ns5" +$PERL ../stop.pl --use-rndc --port ${CONTROLPORT} serve-stale ns5 + +# Load the cache as if it was five minutes (RBTDB_VIRTUAL) older. +FIVEMINUTESAGO=`TZ=UTC perl -e 'my $now = time(); + my $fiveMinutesAgo = 300; + my ($s, $m, $h, $d, $mo, $y) = (localtime($fiveMinutesAgo))[0, 1, 2, 3, 4, 5]; + printf("%04d%02d%02d%02d%02d%02d", $y+1900, $mo+1, $d, $h, $m, $s);'` + +n=$((n+1)) +echo_i "mock the cache date to $FIVEMINUTESAGO (serve-stale cache disabled) ($n)" +ret=0 +sed -E "s/DATE [0-9]{14}/DATE $FIVEMINUTESAGO/g" ns5/named_dump5.db > ns5/named_dumpdb5.db.out || ret=1 +cp ns5/named_dumpdb5.db.out ns5/named_dumpdb5.db +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + +echo_i "start ns5" +start_server --noclean --restart --port ${PORT} serve-stale ns5 + +n=$((n+1)) +echo_i "verify ancient cache statistics (serve-stale cache disabled) ($n)" +ret=0 +rm -f ns5/named.stats +$RNDCCMD 10.53.0.5 stats #> /dev/null 2>&1 +[ -f ns5/named.stats ] || ret=1 +cp ns5/named.stats ns5/named.stats.$n +# Check first 10 lines of Cache DB statistics. After last queries, we expect +# everything to be removed or scheduled to be removed. +grep -A 10 "++ Cache DB RRsets ++" ns5/named.stats.$n > ns5/named.stats.$n.cachedb || ret=1 +grep -F "#TXT" ns5/named.stats.$n.cachedb > /dev/null && ret=1 +grep -F "#Others" ns5/named.stats.$n.cachedb > /dev/null && ret=1 +grep -F "#!TXT" ns5/named.stats.$n.cachedb > /dev/null && ret=1 +grep -F "#NXDOMAIN" ns5/named.stats.$n.cachedb > /dev/null && ret=1 +status=$((status+ret)) +if [ $ret != 0 ]; then echo_i "failed"; fi + echo_i "exit status: $status" [ $status -eq 0 ] || exit 1 diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst index 82795ca4f5..6a782a831f 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -1815,9 +1815,9 @@ Boolean Options The default is ``yes``. ``stale-answer-enable`` - If ``yes``, enable the returning of "stale" cached answers when the name servers - for a zone are not answering. The default is not to return stale - answers. + If ``yes``, enable the returning of "stale" cached answers when the name + servers for a zone are not answering and the ``stale-cache-enable`` option is + also enabled. The default is not to return stale answers. Stale answers can also be enabled or disabled at runtime via ``rndc serve-stale on`` or ``rndc serve-stale off``; these override @@ -1831,6 +1831,9 @@ Boolean Options Information about stale answers is logged under the ``serve-stale`` log category. +``stale-cache-enable`` + If ``yes``, enable the retaining of "stale" cached answers. Default ``no``. + ``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 @@ -3246,15 +3249,20 @@ Tuning (such as NS and glue AAAA/A records) in the resolution process. ``max-stale-ttl`` - If stale answers are enabled, ``max-stale-ttl`` sets the maximum time + If retaining stale RRsets in cache is enabled, and returning of stale cached + answers is also enabled, ``max-stale-ttl`` sets the maximum time for which the server retains records past their normal expiry to return them as stale records, when the servers for those records are not reachable. The default is 12 hours. The minimum allowed is 1 second; a value of 0 is updated silently to 1 second. - For stale answers to be returned, they must be enabled, either in the - configuration file using ``stale-answer-enable`` or via - ``rndc serve-stale on``. + For stale answers to be returned, the retaining of them in cache must be + enabled via the configuration option ``stale-cache-enable``, and returning + cached answers must be enabled, either in the configuration file using the + ``stale-answer-enable`` option or by calling ``rndc serve-stale on``. + + When ``stale-cache-enable`` is set to ``no``, setting the ``max-stale-ttl`` + has no effect, the value of ``max-cache-ttl`` will be ``0`` in such case. ``resolver-nonbackoff-tries`` This specifies how many retries occur before exponential backoff kicks in. The diff --git a/doc/man/named.conf.5in b/doc/man/named.conf.5in index 0e77255d9b..85f5b6d22a 100644 --- a/doc/man/named.conf.5in +++ b/doc/man/named.conf.5in @@ -464,6 +464,7 @@ options { stacksize ( default | unlimited | sizeval ); stale\-answer\-enable boolean; stale\-answer\-ttl duration; + stale\-cache\-enable boolean; startup\-notify\-rate integer; statistics\-file quoted_string; synth\-from\-dnssec boolean; @@ -876,6 +877,7 @@ view string [ class ] { sortlist { address_match_element; ... }; stale\-answer\-enable boolean; stale\-answer\-ttl duration; + stale\-cache\-enable boolean; synth\-from\-dnssec boolean; transfer\-format ( many\-answers | one\-answer ); transfer\-source ( ipv4_address | * ) [ port ( integer | * ) ] [ diff --git a/doc/misc/options b/doc/misc/options index 0a648e8a46..7640b8d4c8 100644 --- a/doc/misc/options +++ b/doc/misc/options @@ -365,6 +365,7 @@ options { stacksize ( default | unlimited | ); stale-answer-enable ; stale-answer-ttl ; + stale-cache-enable ; startup-notify-rate ; statistics-file ; statistics-interval ; // ancient @@ -747,6 +748,7 @@ view [ ] { sortlist { ; ... }; stale-answer-enable ; stale-answer-ttl ; + stale-cache-enable ; suppress-initial-notify ; // not yet implemented synth-from-dnssec ; topology { ; ... }; // ancient diff --git a/doc/misc/options.active b/doc/misc/options.active index 018548edcc..3deea19bb5 100644 --- a/doc/misc/options.active +++ b/doc/misc/options.active @@ -329,6 +329,7 @@ options { stacksize ( default | unlimited | ); stale-answer-enable ; stale-answer-ttl ; + stale-cache-enable ; startup-notify-rate ; statistics-file ; synth-from-dnssec ; @@ -676,6 +677,7 @@ view [ ] { sortlist { ; ... }; stale-answer-enable ; stale-answer-ttl ; + stale-cache-enable ; synth-from-dnssec ; transfer-format ( many-answers | one-answer ); transfer-source ( | * ) [ port ( | * ) ] [ diff --git a/doc/misc/options.grammar.rst b/doc/misc/options.grammar.rst index 034302cc29..dffce2f4a6 100644 --- a/doc/misc/options.grammar.rst +++ b/doc/misc/options.grammar.rst @@ -258,6 +258,7 @@ stacksize ( default | unlimited | ); stale-answer-enable ; stale-answer-ttl ; + stale-cache-enable ; startup-notify-rate ; statistics-file ; synth-from-dnssec ; diff --git a/doc/notes/notes-current.rst b/doc/notes/notes-current.rst index 0480bcbf6b..3ee01476ab 100644 --- a/doc/notes/notes-current.rst +++ b/doc/notes/notes-current.rst @@ -36,6 +36,9 @@ New Features - Statistics channels have also been updated to use the new BIND network manager API. [GL #2022] +- A new configuration option ``stale-cache-enable`` has been introduced to + enable or disable the keeping of stale answers in cache. [GL #1712] + Feature Changes ~~~~~~~~~~~~~~~ @@ -46,6 +49,11 @@ Feature Changes prevents interruption to query resolution when the hash tables need to be increased in size. [GL #1775] +- Keeping stale answers in cache has been disabled by default. + +- The resource records received with 0 TTL are no longer kept in the cache + to be used for stale answers. [GL #1829] + Bug Fixes ~~~~~~~~~ diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c index 0e26425a30..9ca6de2ae0 100644 --- a/lib/dns/rbtdb.c +++ b/lib/dns/rbtdb.c @@ -1630,6 +1630,8 @@ mark_header_stale(dns_rbtdb_t *rbtdb, rdatasetheader_t *header) { uint_least16_t attributes = atomic_load_acquire(&header->attributes); uint_least16_t newattributes = 0; + INSIST((attributes & RDATASET_ATTR_ZEROTTL) == 0); + /* * If we are already stale there is nothing to do. */ @@ -4522,9 +4524,11 @@ check_stale_header(dns_rbtnode_t *node, rdatasetheader_t *header, /* * If this data is in the stale window keep it and if * DNS_DBFIND_STALEOK is not set we tell the caller to - * skip this record. + * skip this record. We skip the records with ZEROTTL + * (these records should not be cached anyway). */ - if (KEEPSTALE(search->rbtdb) && stale > search->now) { + if (!ZEROTTL(header) && KEEPSTALE(search->rbtdb) && + stale > search->now) { mark_header_stale(search->rbtdb, header); *header_prev = header; return ((search->options & DNS_DBFIND_STALEOK) == 0); diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index 2e93cb3faa..9084e63db5 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -2045,6 +2045,7 @@ static cfg_clausedef_t view_clauses[] = { { "sortlist", &cfg_type_bracketed_aml, 0 }, { "stale-answer-enable", &cfg_type_boolean, 0 }, { "stale-answer-ttl", &cfg_type_duration, 0 }, + { "stale-cache-enable", &cfg_type_boolean, 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 },