]> 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)
committerEvan Hunt <each@isc.org>
Sun, 2 Feb 2025 21:22:32 +0000 (13:22 -0800)
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.

(cherry picked from commit 1f095b902c834fd7bf0805605548ef8ca14c5af3)

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

index f8ded241ede1256615d2fe857e4569dbfcc090d0..09909c71d4f2b27947a7c1d07424f7ab7a065121 100755 (executable)
@@ -318,7 +318,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.
@@ -493,18 +493,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
@@ -516,17 +520,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 1d60c14308476dc3b0fca7ff8115bdf4f4b7e88f..6636d4a992ca1a6648b3796fdda8eadececc6add 100644 (file)
@@ -5519,43 +5519,33 @@ cache_findzonecut(dns_db_t *db, const dns_name_t *name, unsigned int options,
        header_prev = NULL;
        for (header = node->data; header != NULL; header = header_next) {
                header_next = header->next;
+               bool ns = (header->type == dns_rdatatype_ns ||
+                          header->type == RBTDB_RDATATYPE_SIGNS);
                if (check_stale_header(node, header, &locktype, lock, &search,
                                       &header_prev))
                {
-                       /*
-                        * The function dns_rbt_findnode found us the 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(lock, locktype);
-                       result = find_deepest_zonecut(&search, node, nodep,
-                                                     foundname, rdataset,
-                                                     sigrdataset);
-                       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 it 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 == RBTDB_RDATATYPE_SIGNS) {
-                               /*
-                                * If we need the NS rdataset, we'll also
-                                * need its signature.
-                                */
                                foundsig = header;
+                               if (found != NULL) {
+                                       break;
+                               }
                        }
                        header_prev = header;
                } else {
@@ -5565,11 +5555,13 @@ cache_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(lock, locktype);
                result = find_deepest_zonecut(&search, node, nodep, foundname,
                                              rdataset, sigrdataset);
+               dns_name_copy(foundname, dcname);
                goto tree_exit;
        }