From 291d0d8d90adea5499af6dc5a030ef0eaba890cf Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Sat, 1 Feb 2025 13:09:22 -0800 Subject: [PATCH] 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. (cherry picked from commit 1f095b902c834fd7bf0805605548ef8ca14c5af3) --- bin/tests/system/qmin/tests.sh | 10 ++++++- lib/dns/rbtdb.c | 52 ++++++++++++++-------------------- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/bin/tests/system/qmin/tests.sh b/bin/tests/system/qmin/tests.sh index f8ded241ed..09909c71d4 100755 --- a/bin/tests/system/qmin/tests.sh +++ b/bin/tests/system/qmin/tests.sh @@ -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 diff --git a/lib/dns/rbtdb.c b/lib/dns/rbtdb.c index 1d60c14308..6636d4a992 100644 --- a/lib/dns/rbtdb.c +++ b/lib/dns/rbtdb.c @@ -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; + if (ns) { + /* + * 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 we found a type we were looking for, remember - * it. - */ if (header->type == dns_rdatatype_ns) { - /* - * Remember a NS rdataset even if we're - * not specifically looking for it, because - * we might need it later. - */ 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; }