From: Colin Vidal Date: Thu, 22 Jan 2026 10:07:01 +0000 (+0100) Subject: Populate the delegation DB from referrals answers X-Git-Tag: v9.21.21~4^2~30 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c7b75f448f4f9c6c7d46cc7be4e42d18cb9945cc;p=thirdparty%2Fbind9.git Populate the delegation DB from referrals answers 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. --- diff --git a/bin/named/server.c b/bin/named/server.c index 4b9138453d9..037a67f0594 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -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); } diff --git a/lib/dns/include/dns/view.h b/lib/dns/include/dns/view.h index 296a3ce273d..b0d98499ae5 100644 --- a/lib/dns/include/dns/view.h +++ b/lib/dns/include/dns/view.h @@ -67,6 +67,7 @@ #include #include #include +#include #include #include #include @@ -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. diff --git a/lib/dns/resolver.c b/lib/dns/resolver.c index f6e7c8055a5..c5f98ad3ef2 100644 --- a/lib/dns/resolver.c +++ b/lib/dns/resolver.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -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 diff --git a/lib/dns/view.c b/lib/dns/view.c index d31d17161ad..042339b2cdb 100644 --- a/lib/dns/view.c +++ b/lib/dns/view.c @@ -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);