]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Populate the delegation DB from referrals answers
authorColin Vidal <colin@isc.org>
Thu, 22 Jan 2026 10:07:01 +0000 (11:07 +0100)
committerColin Vidal <colin@isc.org>
Mon, 30 Mar 2026 18:41:13 +0000 (20:41 +0200)
The resolver now caches NS records and their A/AAAA glues from referral
answers into the delegation database.

A new `cache_delegns()` function extracts NS names and associated glue
addresses from the authority/additional sections of a referral answer
and use those informations to build a delegation set, which is then
inserted into the delegation database.

The created delegation set contains a delegation per NS RR. If the NS RR
has matching A/AAAA RR, the delegation only store the addresses and not
the name. (Note this is technically possible to group all NS RR which
doesn't have glues into a single delegation, and the implementation can
be changed in that way in the future).

Each view has its own instance of the delegation database (they are
never shared between views), but a server restart/reload preserve the
delegation database state.

bin/named/server.c
lib/dns/include/dns/view.h
lib/dns/resolver.c
lib/dns/view.c

index 4b9138453d9789aa7da96e27b754f2a52f8f45ed..037a67f0594e1161ff12e82a0a1561ef142b48ae 100644 (file)
@@ -4128,6 +4128,12 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config,
        INSIST(result == ISC_R_SUCCESS);
        stale_refresh_time = cfg_obj_asduration(obj);
 
+       result = dns_viewlist_find(&named_g_server->viewlist, view->name,
+                                  view->rdclass, &pview);
+       if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS) {
+               goto cleanup;
+       }
+
        /*
         * Configure the view's cache.
         *
@@ -4193,11 +4199,6 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config,
                        }
                }
        } else if (strcmp(cachename, view->name) == 0) {
-               result = dns_viewlist_find(&named_g_server->viewlist, cachename,
-                                          view->rdclass, &pview);
-               if (result != ISC_R_NOTFOUND && result != ISC_R_SUCCESS) {
-                       goto cleanup;
-               }
                if (pview != NULL) {
                        if (!cache_reusable(pview, view, zero_no_soattl)) {
                                isc_log_write(NAMED_LOGCATEGORY_GENERAL,
@@ -4222,7 +4223,6 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config,
                        dns_resolver_getqueryrttstats(pview->resolver,
                                                      &resqueryinrttstats,
                                                      &resqueryoutrttstats);
-                       dns_view_detach(&pview);
                }
        }
 
@@ -4278,6 +4278,28 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config,
        CHECK(dns_view_createresolver(view, resopts, tlsctx_client_cache,
                                      dispatch4, dispatch6));
 
+       /*
+        * The deleg DB cache is preserved if reconfiguring/reloading the
+        * server.
+        */
+       if (pview != NULL) {
+               dns_delegdb_reuse(pview, view);
+       } else {
+               dns_delegdb_create(&view->deleg);
+       }
+
+       /*
+        * Totally arbitrary decision for now. This might need its own knob.
+        */
+       dns_delegdb_setsize(view->deleg, max_cache_size / 6);
+
+       /*
+        * The previous view isn't needed anymore.
+        */
+       if (pview != NULL) {
+               dns_view_detach(&pview);
+       }
+
        if (resstats == NULL) {
                isc_stats_create(mctx, &resstats, dns_resstatscounter_max);
        }
index 296a3ce273dcf5f9874aac9af3232a0664117001..b0d98499ae55a37351c8a29f93d51211c796606d 100644 (file)
@@ -67,6 +67,7 @@
 #include <dns/acl.h>
 #include <dns/catz.h>
 #include <dns/clientinfo.h>
+#include <dns/deleg.h>
 #include <dns/dnstap.h>
 #include <dns/fixedname.h>
 #include <dns/nta.h>
@@ -98,6 +99,7 @@ struct dns_view {
        dns_cache_t       *cache;
        dns_db_t          *cachedb;
        dns_db_t          *hints;
+       dns_delegdb_t     *deleg;
 
        /*
         * security roots and negative trust anchors.
index f6e7c8055a50822df7ab9069aede6033d18f29e5..c5f98ad3ef28b4db423e8eded4faa96cb9111958 100644 (file)
@@ -47,6 +47,7 @@
 #include <dns/adb.h>
 #include <dns/cache.h>
 #include <dns/db.h>
+#include <dns/deleg.h>
 #include <dns/dispatch.h>
 #include <dns/dns64.h>
 #include <dns/dnstap.h>
@@ -6547,6 +6548,120 @@ name_external(const dns_name_t *name, dns_rdatatype_t type, respctx_t *rctx) {
        return false;
 }
 
+static void
+cache_delegglue(dns_delegset_t *delegset, dns_deleg_t *deleg, dns_ttl_t *ttl,
+               dns_rdataset_t *rdataset) {
+       if (rdataset->ttl < *ttl) {
+               *ttl = rdataset->ttl;
+       }
+
+       DNS_RDATASET_FOREACH(rdataset) {
+               dns_rdata_t rdata = DNS_RDATA_INIT;
+               dns_rdata_in_a_t a;
+               isc_netaddr_t addr = { .family = AF_INET };
+
+               dns_rdataset_current(rdataset, &rdata);
+               dns_rdata_tostruct(&rdata, &a, NULL);
+               addr.type.in = a.in_addr;
+               dns_delegset_addaddr(delegset, deleg, &addr);
+       }
+}
+
+static void
+cache_delegglue6(dns_delegset_t *delegset, dns_deleg_t *deleg, dns_ttl_t *ttl,
+                dns_rdataset_t *rdataset) {
+       if (rdataset->ttl < *ttl) {
+               *ttl = rdataset->ttl;
+       }
+
+       DNS_RDATASET_FOREACH(rdataset) {
+               dns_rdata_t rdata = DNS_RDATA_INIT;
+               dns_rdata_in_aaaa_t aaaa;
+               isc_netaddr_t addr = { .family = AF_INET6 };
+
+               dns_rdataset_current(rdataset, &rdata);
+               dns_rdata_tostruct(&rdata, &aaaa, NULL);
+               addr.type.in6 = aaaa.in6_addr;
+               dns_delegset_addaddr(delegset, deleg, &addr);
+       }
+}
+
+/*
+ * Cache the parent-side NS RRset in a delegation.
+ *
+ * Currently the resolver doesn't support DELEG, but when it does, this
+ * code will need to bail out if there is already a delegset from DELEG
+ * RRset in this zonecut. (See DELEG draft 5.1.3.)
+ *
+ * Maybe the simplest way to enforce it could be to pass a boolean flag
+ * `nooverride` to `dns_deleg_writeset()` so it simply detaches the
+ * `delegset` if there is already a `delegset` at this zonecut in the DB.
+ * And the flag would be true only from `cache_delegns()`.
+ */
+static isc_result_t
+cache_delegns(respctx_t *rctx) {
+       fetchctx_t *fctx = rctx->fctx;
+       dns_delegdb_t *delegdb = fctx->res->view->deleg;
+       dns_delegset_t *delegset = NULL;
+       dns_ttl_t ttl = rctx->ns_rdataset->ttl;
+       isc_result_t result;
+
+       FCTXTRACE("cache_delegns");
+
+       dns_delegset_allocset(delegdb, &delegset);
+
+       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;
+
+               /*
+                * We can't "group" all NS-based delegations into a single
+                * `dns_deleg_t` because some of them might have glues, some
+                * other might not, and a `dns_deleg_t` can't have both
+                * addresses and NS names. Let's assume this is a GLUE-based
+                * deleg first.
+                */
+               dns_delegset_allocdeleg(delegset, DNS_DELEGTYPE_NS_GLUES,
+                                       &deleg);
+
+               dns_rdataset_current(rctx->ns_rdataset, &rdata);
+               INSIST(rdata.type == dns_rdatatype_ns);
+               dns_rdata_tostruct(&rdata, &ns, NULL);
+
+               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;
+               }
+
+               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;
+               }
+
+               if (ISC_LIST_EMPTY(deleg->addresses)) {
+                       /*
+                        * There is 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);
+               }
+       }
+
+       result = dns_delegset_insert(delegdb, rctx->ns_name, ttl, delegset);
+       dns_delegset_detach(&delegset);
+
+       return result;
+}
+
 static isc_result_t
 check_section(void *arg, const dns_name_t *addname, dns_rdatatype_t type,
              dns_rdataset_t *found, dns_section_t section) {
@@ -9041,6 +9156,12 @@ rctx_referral(respctx_t *rctx) {
                                          check_related, rctx, 0);
        FCTX_ATTR_CLR(fctx, FCTX_ATTR_GLUING);
 
+       /*
+        * An NS-based delegation can be cached immediately (i.e. there is
+        * no DNSSEC validation).
+        */
+       cache_delegns(rctx);
+
        /*
         * NS rdatasets with 0 TTL cause problems.
         * dns_view_findzonecut() will not find them when we
index d31d17161ad0b57d00d92e364d814660a4cc6200..042339b2cdb8ad108b0026de765fe0f3f5e5e90a 100644 (file)
@@ -173,6 +173,10 @@ destroy(dns_view_t *view) {
        isc_refcount_destroy(&view->references);
        isc_refcount_destroy(&view->weakrefs);
 
+       if (view->deleg != NULL) {
+               dns_delegdb_detach(&view->deleg);
+       }
+
        if (view->order != NULL) {
                dns_order_detach(&view->order);
        }
@@ -394,6 +398,10 @@ shutdown_view(dns_view_t *view) {
                dns_resolver_shutdown(view->resolver);
        }
 
+       if (view->deleg != NULL) {
+               dns_delegdb_shutdown(view->deleg);
+       }
+
        rcu_read_lock();
        adb = rcu_dereference(view->adb);
        if (adb != NULL) {
@@ -534,6 +542,7 @@ dns_view_createresolver(dns_view_t *view, unsigned int options,
 
        RETERR(dns_resolver_create(view, options, tlsctx_cache, dispatchv4,
                                   dispatchv6, &view->resolver));
+
        isc_mem_create("ADB", &mctx);
        dns_adb_create(mctx, view, &view->adb);
        isc_mem_detach(&mctx);