]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Check staleness in bind_rdataset
authorMatthijs Mekking <matthijs@isc.org>
Fri, 12 Mar 2021 14:12:27 +0000 (15:12 +0100)
committerMatthijs Mekking <matthijs@isc.org>
Tue, 13 Apr 2021 07:48:20 +0000 (09:48 +0200)
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.

bin/tests/system/serve-stale/tests.sh
lib/dns/rbtdb.c

index f16aaf08445306c3f22f8035e204136272c50e4d..62f6bf8bec6b8228a5b05ff87e253750c47df6ee 100755 (executable)
@@ -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
index 8af4e10e48bceae3d9689f14d84f23a124a0636c..f30d560e682233172cb8f165378b93a047472246 100644 (file)
@@ -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;