diff --git a/bin/named/server.c b/bin/named/server.c index 4b9138453d..037a67f059 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 296a3ce273..b0d98499ae 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 f6e7c8055a..c5f98ad3ef 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 d31d17161a..042339b2cd 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);