From: Evan Hunt Date: Sat, 1 Feb 2025 21:09:22 +0000 (-0800) Subject: fix the cache findzonecut implementation X-Git-Tag: v9.21.5~11^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1f095b902c834fd7bf0805605548ef8ca14c5af3;p=thirdparty%2Fbind9.git fix the cache findzonecut implementation 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. --- diff --git a/bin/tests/system/qmin/tests.sh b/bin/tests/system/qmin/tests.sh index d104161f402..6cf8d2b9d07 100755 --- a/bin/tests/system/qmin/tests.sh +++ b/bin/tests/system/qmin/tests.sh @@ -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 diff --git a/lib/dns/qpcache.c b/lib/dns/qpcache.c index cccf5226940..ab8f22add26 100644 --- a/lib/dns/qpcache.c +++ b/lib/dns/qpcache.c @@ -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; }