]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
fix the cache findzonecut implementation
authorEvan Hunt <each@isc.org>
Sat, 1 Feb 2025 21:09:22 +0000 (13:09 -0800)
committerOndřej Surý <ondrej@isc.org>
Sun, 2 Feb 2025 17:43:50 +0000 (18:43 +0100)
the search for the deepest known zone cut in the cache could
improperly reject a node containing stale data, even if the
NS rdataset wasn't the data that was stale.

this change also improves the efficiency of the search by
stopping it when both NS and RRSIG(NS) have been found.

bin/tests/system/qmin/tests.sh
lib/dns/qpcache.c

index d104161f40237b0bd60561e081f7d155df77e4db..6cf8d2b9d07d8fd53f17fd0f9320ca73ccaf7a42 100755 (executable)
@@ -333,7 +333,7 @@ $DIG $DIGOPTS -x 2001:4f8::1 @10.53.0.6 >dig.out.test$n || ret=1
 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
 grep "1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.f.4.0.1.0.0.2.ip6.arpa. 1 IN PTR nee.com." dig.out.test$n >/dev/null || ret=1
 sleep 1
-grep -v ADDR ans2/query.log >ans2/query.log.trimmed
+grep -F "ip6.arpa." ans2/query.log >ans2/query.log.trimmed
 cat <<__EOF | diff ans2/query.log.trimmed - >/dev/null || ret=1
 NS 1.0.0.2.ip6.arpa.
 NS 8.f.4.0.1.0.0.2.ip6.arpa.
@@ -512,18 +512,22 @@ n=$((n + 1))
 echo_i "query for .stale is properly minimized when qname-minimization is in strict mode (stale cache) ($n)"
 ret=0
 $CLEANQL
+$RNDCCMD 10.53.0.6 flush
 $DIG $DIGOPTS @10.53.0.6 txt a.b.stale. >dig.out.test$n || ret=1
 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
 grep "a\.b\.stale\..*1.*IN.*TXT.*hooray" dig.out.test$n >/dev/null || ret=1
 sleep 1
 sort ans2/query.log >ans2/query.log.sorted
 cat <<__EOF | diff ans2/query.log.sorted - >/dev/null || ret=1
+ADDR ns.b.stale.
+ADDR ns2.stale.
 NS b.stale.
 NS stale.
 __EOF
 test -f ans3/query.log && ret=1
 sort ans4/query.log >ans4/query.log.sorted
 cat <<__EOF | diff ans4/query.log.sorted - >/dev/null || ret=1
+ADDR ns.b.stale.
 NS b.stale.
 TXT a.b.stale.
 __EOF
@@ -535,17 +539,21 @@ n=$((n + 1))
 echo_i "query for .stale is properly minimized when qname-minimization is in relaxed mode (stale cache) ($n)"
 ret=0
 $CLEANQL
+$RNDCCMD 10.53.0.7 flush
 $DIG $DIGOPTS @10.53.0.7 txt a.b.stale. >dig.out.test$n || ret=1
 grep "status: NOERROR" dig.out.test$n >/dev/null || ret=1
 grep "a\.b\.stale\..*1.*IN.*TXT.*hooray" dig.out.test$n >/dev/null || ret=1
 sleep 1
 sort ans2/query.log >ans2/query.log.sorted
 cat <<__EOF | diff ans2/query.log.sorted - >/dev/null || ret=1
+ADDR ns.b.stale.
+ADDR ns2.stale.
 NS b.stale.
 __EOF
 test -f ans3/query.log && ret=1
 sort ans4/query.log >ans4/query.log.sorted
 cat <<__EOF | diff ans4/query.log.sorted - >/dev/null || ret=1
+ADDR ns.b.stale.
 TXT a.b.stale.
 __EOF
 for ans in ans2 ans3 ans4; do mv -f $ans/query.log query-$ans-$n.log 2>/dev/null || true; done
index cccf52269407f0a88319804a5ad9d857764d603d..ab8f22add26d64feda05117c8c44896ace2b6dd9 100644 (file)
@@ -2087,45 +2087,35 @@ qpcache_findzonecut(dns_db_t *db, const dns_name_t *name, unsigned int options,
 
        for (header = node->data; header != NULL; header = header_next) {
                header_next = header->next;
+               bool ns = (header->type == dns_rdatatype_ns ||
+                          header->type == DNS_SIGTYPE(dns_rdatatype_ns));
                if (check_stale_header(node, header, &nlocktype, nlock, &search,
                                       &header_prev))
                {
-                       /*
-                        * The function dns_qp_lookup found us a matching
-                        * node for 'name' and stored the result in 'dcname'.
-                        * This is the deepest known zonecut in our database.
-                        * However, this node may be stale and if serve-stale
-                        * is not enabled (in other words 'stale-answer-enable'
-                        * is set to no), this node may not be used as a
-                        * zonecut we know about. If so, find the deepest
-                        * zonecut from this node up and return that instead.
-                        */
-                       NODE_UNLOCK(nlock, &nlocktype);
-                       result = find_deepest_zonecut(
-                               &search, node, nodep, foundname, rdataset,
-                               sigrdataset DNS__DB_FLARG_PASS);
-                       dns_name_copy(foundname, dcname);
-                       goto tree_exit;
-               } else if (EXISTS(header) && !ANCIENT(header)) {
-                       /*
-                        * If we found a type we were looking for, remember
-                        * it.
-                        */
-                       if (header->type == dns_rdatatype_ns) {
+                       if (ns) {
                                /*
-                                * Remember a NS rdataset even if we're
-                                * not specifically looking for it, because
-                                * we might need it later.
+                                * We found a cached NS, but was either
+                                * ancient or it was stale and serve-stale
+                                * is disabled, so this node can't be used
+                                * as a zone cut we know about. Instead we
+                                * bail out and call find_deepest_zonecut()
+                                * below.
                                 */
+                               break;
+                       }
+               } else if (EXISTS(header) && !ANCIENT(header)) {
+                       if (header->type == dns_rdatatype_ns) {
                                found = header;
+                               if (foundsig != NULL) {
+                                       break;
+                               }
                        } else if (header->type ==
                                   DNS_SIGTYPE(dns_rdatatype_ns))
                        {
-                               /*
-                                * If we need the NS rdataset, we'll also
-                                * need its signature.
-                                */
                                foundsig = header;
+                               if (found != NULL) {
+                                       break;
+                               }
                        }
                        header_prev = header;
                } else {
@@ -2135,12 +2125,14 @@ qpcache_findzonecut(dns_db_t *db, const dns_name_t *name, unsigned int options,
 
        if (found == NULL) {
                /*
-                * No NS records here.
+                * No active NS records found. Call find_deepest_zonecut()
+                * to look for them in nodes above this one.
                 */
                NODE_UNLOCK(nlock, &nlocktype);
                result = find_deepest_zonecut(&search, node, nodep, foundname,
                                              rdataset,
                                              sigrdataset DNS__DB_FLARG_PASS);
+               dns_name_copy(foundname, dcname);
                goto tree_exit;
        }