]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Drop in-domain NS without glue from the delegation set 11971/head
authorOndřej Surý <ondrej@isc.org>
Wed, 6 May 2026 10:37:03 +0000 (12:37 +0200)
committerOndřej Surý <ondrej@isc.org>
Fri, 15 May 2026 05:26:38 +0000 (07:26 +0200)
Pull the dns_message_findname() lookups into cache_delegglue() and
cache_delegglue6() so each helper now owns its glue lookup and returns
the number of addresses cached.  cache_delegns() splits referrals into
two cases: in-domain (the NS name is below the delegation point) and
sibling/in-bailiwick.

An in-domain NS without glue is unresolvable by definition - the
resolver would have to ask the very server it's trying to find.  Log
"missing mandatory glue" at notice level and skip the deleg entirely
rather than leaving an unusable entry in the set.  A new
dns_delegset_freedeleg() undoes a fresh dns_delegset_allocdeleg() so
the rest of the delegation set is preserved.

bin/tests/system/expiredglue/tests_expiredglue.py
lib/dns/deleg.c
lib/dns/include/dns/deleg.h
lib/dns/resolver.c

index a7f3c3d137c799bfa01c9faf0d6f1829c6092d43..5e5ac5b4d545c263dbbbe43e3653fb5befdc667a 100644 (file)
@@ -45,11 +45,12 @@ def test_expiredglue(ns4):
     isctest.check.same_data(res3_2, res3)
 
 
-def test_loopdetected(ns4):
+def test_missing_mandatory_glue(ns4):
     msg = isctest.query.create("a.missing.tld.", "A")
     with ns4.watch_log_from_here() as watcher:
         res = isctest.query.udp(msg, ns4.ip)
 
-        # However, this is a valid fetch loop, and named detects it.
-        watcher.wait_for_line("loop detected resolving 'ns.missing.tld/A'")
+        # The NS for missing.tld. is in-domain and has no glue, so
+        # named drops the delegation rather than chasing it.
+        watcher.wait_for_line("missing mandatory glue for ns.missing.tld")
         isctest.check.servfail(res)
index 7b2560ca47a7f6c169021620706d229a2deebef8..5c8a767be24810043e1b8337fbd17a7a7203b4a0 100644 (file)
@@ -422,6 +422,21 @@ dns_delegset_allocdeleg(dns_delegset_t *delegset, dns_deleg_type_t type,
        *delegp = deleg;
 }
 
+void
+dns_delegset_freedeleg(dns_delegset_t *delegset, dns_deleg_t **delegp) {
+       REQUIRE(DNS_DELEGSET_VALID(delegset));
+       REQUIRE(delegp != NULL && *delegp != NULL);
+       REQUIRE(ISC_LIST_EMPTY((*delegp)->addresses));
+       REQUIRE(ISC_LIST_EMPTY((*delegp)->names));
+
+       dns_deleg_t *deleg = *delegp;
+       *delegp = NULL;
+
+       ISC_LIST_UNLINK(delegset->delegs, deleg, link);
+
+       isc_mem_put(delegset->mctx, deleg, sizeof(*deleg));
+}
+
 void
 dns_delegset_addaddr(dns_delegset_t *delegset, dns_deleg_t *deleg,
                     const isc_netaddr_t *addr) {
index 907d4ffc6889ee44ae55f308e71bbac229fb2e63..3b4573cf0a0f6986f07c4c94eb0c6b54a0518ab6 100644 (file)
@@ -151,6 +151,12 @@ dns_delegset_allocset(dns_delegdb_t *db, dns_delegset_t **delegsetp);
 void
 dns_delegset_allocdeleg(dns_delegset_t *delegset, dns_deleg_type_t type,
                        dns_deleg_t **delegp);
+/*
+ * Free the deleg struct and remove it from the delegation set. Can't
+ * be used on delegation set already attached in the DB.
+ */
+void
+dns_delegset_freedeleg(dns_delegset_t *delegset, dns_deleg_t **delegp);
 
 /*
  * Add a new IP into a delegation. Can't be used on a delegation from a
index 49eb16cb5947c398132a7a0934c99ce39cc44ef8..e0575ab8a83c05150733873ad39b511767a1336f 100644 (file)
@@ -6640,10 +6640,19 @@ name_external(const dns_name_t *name, dns_rdatatype_t type, respctx_t *rctx) {
        return false;
 }
 
-static void
+static size_t
 cache_delegglue(dns_delegset_t *delegset, dns_deleg_t *deleg, dns_ttl_t *ttl,
-               dns_rdataset_t *rdataset) {
+               respctx_t *rctx, const dns_name_t *nsname) {
+       dns_rdataset_t *rdataset = NULL;
        size_t naddrs = 0;
+       isc_result_t result;
+
+       result = dns_message_findname(rctx->query->rmessage,
+                                     DNS_SECTION_ADDITIONAL, nsname,
+                                     dns_rdatatype_a, 0, NULL, &rdataset);
+       if (result != ISC_R_SUCCESS) {
+               return 0;
+       }
 
        if (rdataset->ttl < *ttl) {
                *ttl = rdataset->ttl;
@@ -6664,12 +6673,22 @@ cache_delegglue(dns_delegset_t *delegset, dns_deleg_t *deleg, dns_ttl_t *ttl,
                        break;
                }
        }
+       return naddrs;
 }
 
-static void
+static size_t
 cache_delegglue6(dns_delegset_t *delegset, dns_deleg_t *deleg, dns_ttl_t *ttl,
-                dns_rdataset_t *rdataset) {
+                respctx_t *rctx, const dns_name_t *nsname) {
+       dns_rdataset_t *rdataset = NULL;
        size_t naddrs = 0;
+       isc_result_t result;
+
+       result = dns_message_findname(rctx->query->rmessage,
+                                     DNS_SECTION_ADDITIONAL, nsname,
+                                     dns_rdatatype_aaaa, 0, NULL, &rdataset);
+       if (result != ISC_R_SUCCESS) {
+               return 0;
+       }
 
        if (rdataset->ttl < *ttl) {
                *ttl = rdataset->ttl;
@@ -6690,6 +6709,7 @@ cache_delegglue6(dns_delegset_t *delegset, dns_deleg_t *deleg, dns_ttl_t *ttl,
                        break;
                }
        }
+       return naddrs;
 }
 
 /*
@@ -6713,6 +6733,8 @@ cache_delegns(respctx_t *rctx) {
        dns_fixedname_t fparent;
        dns_name_t *parent = dns_fixedname_initname(&fparent);
        size_t labels;
+       size_t ns_count = 0;
+       size_t max_servers = fctx->res->view->max_delegation_servers;
        isc_result_t result;
 
        FCTXTRACE("cache_delegns");
@@ -6730,14 +6752,11 @@ cache_delegns(respctx_t *rctx) {
                dns_name_getlabelsequence(rctx->ns_name, 1, labels - 1, parent);
        }
 
-       size_t ns_count = 0;
-       size_t max_servers = fctx->res->view->max_delegation_servers;
-
        DNS_RDATASET_FOREACH(rctx->ns_rdataset) {
-               dns_rdataset_t *gluerdataset = NULL;
                dns_rdata_t rdata = DNS_RDATA_INIT;
                dns_rdata_ns_t ns;
                dns_deleg_t *deleg = NULL;
+               size_t naddrs = 0;
 
                if (ns_count >= max_servers) {
                        break;
@@ -6758,32 +6777,41 @@ cache_delegns(respctx_t *rctx) {
                INSIST(rdata.type == dns_rdatatype_ns);
                dns_rdata_tostruct(&rdata, &ns, NULL);
 
-               if (labels > 1 && dns_name_issubdomain(&ns.name, parent)) {
-                       result = dns_message_findname(rctx->query->rmessage,
-                                                     DNS_SECTION_ADDITIONAL,
-                                                     &ns.name, dns_rdatatype_a,
-                                                     0, NULL, &gluerdataset);
-                       if (result == ISC_R_SUCCESS) {
-                               cache_delegglue(delegset, deleg, &ttl,
-                                               gluerdataset);
-                               gluerdataset = NULL;
+               /* in-domain GLUE */
+               if (dns_name_issubdomain(&ns.name, rctx->ns_name)) {
+                       naddrs += cache_delegglue(delegset, deleg, &ttl, rctx,
+                                                 &ns.name);
+                       naddrs += cache_delegglue6(delegset, deleg, &ttl, rctx,
+                                                  &ns.name);
+                       if (naddrs == 0) {
+                               INSIST(ISC_LIST_EMPTY(deleg->addresses));
+                               char namebuf[DNS_NAME_FORMATSIZE];
+                               dns_name_format(&ns.name, namebuf,
+                                               sizeof(namebuf));
+
+                               isc_log_write(DNS_LOGCATEGORY_RESOLVER,
+                                             DNS_LOGMODULE_RESOLVER,
+                                             ISC_LOG_NOTICE,
+                                             "missing mandatory glue for %s",
+                                             namebuf);
+                               dns_delegset_freedeleg(delegset, &deleg);
                        }
+                       continue;
+               }
 
-                       result = dns_message_findname(
-                               rctx->query->rmessage, DNS_SECTION_ADDITIONAL,
-                               &ns.name, dns_rdatatype_aaaa, 0, NULL,
-                               &gluerdataset);
-                       if (result == ISC_R_SUCCESS) {
-                               cache_delegglue6(delegset, deleg, &ttl,
-                                                gluerdataset);
-                               gluerdataset = NULL;
-                       }
+               /* in-bailiwick/sibling GLUE */
+               if (labels > 1 && dns_name_issubdomain(&ns.name, parent)) {
+                       naddrs += cache_delegglue(delegset, deleg, &ttl, rctx,
+                                                 &ns.name);
+                       naddrs += cache_delegglue6(delegset, deleg, &ttl, rctx,
+                                                  &ns.name);
                }
 
-               if (ISC_LIST_EMPTY(deleg->addresses)) {
+               if (naddrs == 0) {
+                       INSIST(ISC_LIST_EMPTY(deleg->addresses));
                        /*
-                        * There is actually no glues for this NSRRset, so this
-                        * is actually a DNS_DELEGTYPE_NS_NAMES.
+                        * There are actually no glues for this NSRRset,
+                        * so this is actually a DNS_DELEGTYPE_NS_NAMES.
                         */
                        deleg->type = DNS_DELEGTYPE_NS_NAMES;
                        dns_delegset_addns(delegset, deleg, &ns.name);