From: Matthijs Mekking Date: Fri, 12 Mar 2021 14:12:27 +0000 (+0100) Subject: Check staleness in bind_rdataset X-Git-Tag: v9.17.13~65^2~1 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=debee6157b7d78a8aba60159777a7025374edce7;p=thirdparty%2Fbind9.git Check staleness in bind_rdataset Before binding an RRset, check the time and see if this record is stale (or perhaps even ancient). Marking a header stale or ancient happens only when looking up an RRset in cache, but binding an RRset can also happen on other occasions (for example when dumping the database). Check the time and compare it to the header. If according to the time the entry is stale, but not ancient, set the STALE attribute. If according to the time is ancient, set the ANCIENT attribute. We could mark the header stale or ancient here, but that requires locking, so that's why we only compare the current time against the rdh_ttl. Adjust the test to check the dump-db before querying for data. In the dumped file the entry should be marked as stale, despite no cache lookup happened since the initial query. --- diff --git a/bin/tests/system/serve-stale/tests.sh b/bin/tests/system/serve-stale/tests.sh index f16aaf08445..62f6bf8bec6 100755 --- a/bin/tests/system/serve-stale/tests.sh +++ b/bin/tests/system/serve-stale/tests.sh @@ -105,6 +105,19 @@ status=$((status+ret)) sleep 2 +# Run rndc dumpdb, test whether the stale data has correct comment printed. +# The max-stale-ttl is 3600 seconds, so the comment should say the data is +# stale for somewhere between 3500-3599 seconds. +echo_i "check rndc dump stale data.example ($n)" +rndc_dumpdb ns1 || ret=1 +awk '/; stale/ { x=$0; getline; print x, $0}' ns1/named_dump.db.test$n | + grep "; stale (will be retained for 3[56].. more seconds) data\.example.*A text record with a 2 second ttl" > /dev/null 2>&1 || ret=1 +# Also make sure the not expired data does not have a stale comment. +awk '/; answer/ { x=$0; getline; print x, $0}' ns1/named_dump.db.test$n | + grep "; answer longttl\.example.*A text record with a 600 second ttl" > /dev/null 2>&1 || ret=1 +if [ $ret != 0 ]; then echo_i "failed"; fi +status=$((status+ret)) + 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 longttl.example TXT > dig.out.test$((n+2)) & @@ -123,19 +136,6 @@ grep "data\.example\..*4.*IN.*TXT.*A text record with a 2 second ttl" dig.out.te if [ $ret != 0 ]; then echo_i "failed"; fi status=$((status+ret)) -# Run rndc dumpdb, test whether the stale data has correct comment printed. -# The max-stale-ttl is 3600 seconds, so the comment should say the data is -# stale for somewhere between 3500-3599 seconds. -echo_i "check rndc dump stale data.example ($n)" -rndc_dumpdb ns1 || ret=1 -awk '/; stale/ { x=$0; getline; print x, $0}' ns1/named_dump.db.test$n | - grep "; stale (will be retained for 35.. more seconds) data\.example.*A text record with a 2 second ttl" > /dev/null 2>&1 || ret=1 -# Also make sure the not expired data does not have a stale comment. -awk '/; answer/ { x=$0; getline; print x, $0}' ns1/named_dump.db.test$n | - grep "; answer longttl\.example.*A text record with a 600 second ttl" > /dev/null 2>&1 || ret=1 -if [ $ret != 0 ]; then echo_i "failed"; fi -status=$((status+ret)) - n=$((n+1)) echo_i "check non-stale longttl.example ($n)" ret=0 diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c index 8af4e10e48b..f30d560e682 100644 --- a/lib/dns/rbtdb.c +++ b/lib/dns/rbtdb.c @@ -3088,6 +3088,8 @@ bind_rdataset(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, rdatasetheader_t *header, isc_stdtime_t now, isc_rwlocktype_t locktype, dns_rdataset_t *rdataset) { unsigned char *raw; /* RDATASLAB */ + bool stale = STALE(header); + bool ancient = ANCIENT(header); /* * Caller must be holding the node reader lock. @@ -3105,6 +3107,29 @@ bind_rdataset(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, rdatasetheader_t *header, INSIST(rdataset->methods == NULL); /* We must be disassociated. */ + /* + * Mark header stale or ancient if the RRset is no longer active. + */ + if (!ACTIVE(header, now)) { + dns_ttl_t stale_ttl = header->rdh_ttl + rbtdb->serve_stale_ttl; + /* + * 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. We skip the records with ZEROTTL + * (these records should not be cached anyway). + */ + + if (KEEPSTALE(rbtdb) && stale_ttl > now) { + stale = true; + } else { + /* + * We are not keeping stale, or it is outside the + * stale window. Mark ancient, i.e. ready for cleanup. + */ + ancient = true; + } + } + rdataset->methods = &rdataset_methods; rdataset->rdclass = rbtdb->common.rdclass; rdataset->type = RBTDB_RDATATYPE_BASE(header->type); @@ -3123,18 +3148,18 @@ bind_rdataset(dns_rbtdb_t *rbtdb, dns_rbtnode_t *node, rdatasetheader_t *header, if (PREFETCH(header)) { rdataset->attributes |= DNS_RDATASETATTR_PREFETCH; } - if (STALE(header) && !ANCIENT(header)) { - dns_ttl_t stale = header->rdh_ttl + rbtdb->serve_stale_ttl; - if (stale > now) { - stale = stale - now; + if (stale && !ancient) { + dns_ttl_t stale_ttl = header->rdh_ttl + rbtdb->serve_stale_ttl; + if (stale_ttl > now) { + stale_ttl = stale_ttl - now; } else { - stale = 0; + stale_ttl = 0; } if (STALE_WINDOW(header)) { rdataset->attributes |= DNS_RDATASETATTR_STALE_WINDOW; } rdataset->attributes |= DNS_RDATASETATTR_STALE; - rdataset->stale_ttl = stale; + rdataset->stale_ttl = stale_ttl; rdataset->ttl = 0; } else if (IS_CACHE(rbtdb) && !ACTIVE(header, now)) { rdataset->attributes |= DNS_RDATASETATTR_ANCIENT;