]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Rename view->hints to view->rootdb and rearm priming
authorOndřej Surý <ondrej@sury.org>
Wed, 15 Apr 2026 06:11:17 +0000 (08:11 +0200)
committerOndřej Surý <ondrej@sury.org>
Thu, 16 Apr 2026 11:39:18 +0000 (13:39 +0200)
With the parent-centric resolver, dns_view_bestzonecut() consults the
delegation DB (view->deleg) rather than the main cache for the closest
zonecut.  Root is never the target of a referral, so it never lands in
delegdb; bestzonecut therefore falls through to the hints lookup on
every query whose closest ancestor is root.  prime_done() only called
dns_root_checkhints(), which logs discrepancies but does not update
any store bestzonecut looks at, so the fresh root NS records obtained
by priming were never used and priming kept re-firing.

Rename view->hints to view->rootdb and refresh it when a priming
fetch completes: the '.' NS rdataset is replaced with the fetched
one, and for each listed nameserver the matching A/AAAA glue is
copied from the response's ADDITIONAL section.  Only glue for names
that actually appear as NS targets is accepted, so a hostile response
cannot inject unrelated records.  Glue the response did not carry is
left untouched, so the hints-file records loaded at startup remain as
a fallback.

Each view gets its own rootdb: the previous shared
named_g_server->in_roothints is gone, and configure_view() calls
dns_rootns_create() per view when the class-IN defaults are needed.
That keeps the priming writer one-per-DB, so concurrent priming in
different views cannot race on the same zone-DB version.

The rootdb refresh runs synchronously from the resolver response path,
so records go straight from the wire into rootdb with no cache round
trip and no dependency on DNSSEC validation state.  A new
DNS_FETCHOPT_PRIMING option marks the priming fetch; prime_done()
itself is now pure cleanup.

Track the rootdb freshness window in view->rootdb_expires and trigger
re-priming lazily from dns_view_find() and bestzonecut_rootdb() only
when the window has elapsed.  Stale records are still served while the
fresh priming fetch is in flight.

Drop dns_root_checkhints() and its helpers; the rootdb is now the
authoritative source the resolver consults.

12 files changed:
bin/delv/delv.c
bin/named/include/named/server.h
bin/named/server.c
bin/tests/system/dnstap/tests.sh
bin/tests/system/minimalresponses/common.py
lib/dns/include/dns/resolver.h
lib/dns/include/dns/rootns.h
lib/dns/include/dns/view.h
lib/dns/resolver.c
lib/dns/rootns.c
lib/dns/view.c
lib/ns/query.c

index ebce81c5d660680cf31418ab5553f9ff3880ffc6..c63c7be5e7210db9f477f4fab6a52b96637fdb2e 100644 (file)
@@ -2165,7 +2165,7 @@ run_server(void *arg) {
 
        CHECK(dns_rootns_create(isc_g_mctx, dns_rdataclass_in, hintfile,
                                &roothints));
-       dns_view_sethints(view, roothints);
+       dns_view_setrootdb(view, roothints);
        dns_db_detach(&roothints);
 
        view->qminimization = qmin;
index b15d6075221f35e60b9abaed36cd6bb77412eb41..db230937375d60d5cc0b55c8eafe8cf96362b719 100644 (file)
@@ -67,7 +67,6 @@ struct named_server {
        dns_kasplist_t     kasplist;
        dns_keystorelist_t keystorelist;
        ns_interfacemgr_t *interfacemgr;
-       dns_db_t          *in_roothints;
 
        isc_timer_t *interface_timer;
        isc_timer_t *heartbeat_timer;
index 34ea760143d5b58bb552ecc2d5ad089ad9b7c00c..2321ba6250dfa0eab15ec34d87f57dc1a628b604 100644 (file)
@@ -453,6 +453,9 @@ static isc_result_t
 configure_alternates(const cfg_obj_t *config, dns_view_t *view,
                     const cfg_obj_t *alternates);
 
+static isc_result_t
+configure_rootdb(dns_view_t *view, const char *filename);
+
 static isc_result_t
 configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
               const cfg_obj_t *vconfig, dns_view_t *view,
@@ -4538,20 +4541,22 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config,
        }
 
        /*
-        * We have default hints for class IN if we need them.
+        * We have default root hints for class IN if we need them.
+        * Each view gets its own rootdb so a priming response only
+        * writes into that view's copy.
         */
-       if (view->rdclass == dns_rdataclass_in && view->hints == NULL) {
-               dns_view_sethints(view, named_g_server->in_roothints);
+       if (view->rdclass == dns_rdataclass_in && view->rootdb == NULL) {
+               CHECK(configure_rootdb(view, NULL));
        }
 
        /*
-        * If we still have no hints, this is a non-IN view with no
+        * If we still have no root hints, this is a non-IN view with no
         * "hints zone" configured.  Issue a warning, except if this
         * is a root server.  Root servers never need to consult
         * their hints, so it's no point requiring users to configure
         * them.
         */
-       if (view->hints == NULL) {
+       if (view->rootdb == NULL) {
                dns_zone_t *rootzone = NULL;
                (void)dns_view_findzone(view, dns_rootname, DNS_ZTFIND_EXACT,
                                        &rootzone);
@@ -5543,14 +5548,14 @@ cleanup:
 }
 
 static isc_result_t
-configure_hints(dns_view_t *view, const char *filename) {
+configure_rootdb(dns_view_t *view, const char *filename) {
        isc_result_t result;
        dns_db_t *db;
 
        db = NULL;
        result = dns_rootns_create(view->mctx, view->rdclass, filename, &db);
        if (result == ISC_R_SUCCESS) {
-               dns_view_sethints(view, db);
+               dns_view_setrootdb(view, db);
                dns_db_detach(&db);
        }
 
@@ -6071,7 +6076,7 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
                if (dns_name_equal(origin, dns_rootname)) {
                        const char *hintsfile = cfg_obj_asstring(fileobj);
 
-                       CHECK(configure_hints(view, hintsfile));
+                       CHECK(configure_rootdb(view, hintsfile));
                } else {
                        isc_log_write(NAMED_LOGCATEGORY_GENERAL,
                                      NAMED_LOGMODULE_SERVER, ISC_LOG_WARNING,
@@ -9207,8 +9212,6 @@ shutdown_server(void *arg) {
        named_geoip_shutdown();
 #endif /* HAVE_GEOIP2 */
 
-       dns_db_detach(&server->in_roothints);
-
        isc_loopmgr_resume();
 }
 
@@ -9457,10 +9460,6 @@ named_server_create(isc_mem_t *mctx, named_server_t **serverp) {
        ISC_LIST_INIT(server->keystorelist);
        ISC_LIST_INIT(server->viewlist);
 
-       CHECKFATAL(dns_rootns_create(mctx, dns_rdataclass_in, NULL,
-                                    &server->in_roothints),
-                  "setting up root hints");
-
        atomic_init(&server->reload_status, NAMED_RELOAD_IN_PROGRESS);
 
        ns_server_create(mctx, get_matching_view, &server->sctx);
index c354c3447892a3be5dfe1ac87edb267ae225ff88..3d34c6b87f195f607abba0ab90be0877be89e81f 100644 (file)
@@ -363,8 +363,8 @@ echo_i "checking UDP message counts"
 ret=0
 check_count ns1 $udp1 0
 check_count ns2 $udp2 2
-check_count ns3 $udp3 2
-check_count ns5 $udp5 4
+check_count ns3 $udp3 4
+check_count ns5 $udp5 6
 if [ $ret != 0 ]; then echo_i "failed"; fi
 status=$((status + ret))
 
@@ -399,7 +399,7 @@ echo_i "checking CLIENT_QUERY message counts"
 ret=0
 check_count ns1 $cq1 0
 check_count ns2 $cq2 0
-check_count ns3 $cq3 1
+check_count ns3 $cq3 2
 check_count ns5 $cq5 1
 if [ $ret != 0 ]; then echo_i "failed"; fi
 status=$((status + ret))
@@ -408,7 +408,7 @@ echo_i "checking CLIENT_RESPONSE message counts"
 ret=0
 check_count ns1 $cr1 0
 check_count ns2 $cr2 0
-check_count ns3 $cr3 1
+check_count ns3 $cr3 2
 check_count ns5 $cr5 1
 if [ $ret != 0 ]; then echo_i "failed"; fi
 status=$((status + ret))
@@ -436,7 +436,7 @@ ret=0
 check_count ns1 $fq1 0
 check_count ns2 $fq2 0
 check_count ns3 $fq3 0
-check_count ns5 $fq5 1
+check_count ns5 $fq5 2
 if [ $ret != 0 ]; then echo_i "failed"; fi
 status=$((status + ret))
 
@@ -445,7 +445,7 @@ ret=0
 check_count ns1 $fr1 0
 check_count ns2 $fr2 0
 check_count ns3 $fr3 0
-check_count ns5 $fr5 1
+check_count ns5 $fr5 2
 if [ $ret != 0 ]; then echo_i "failed"; fi
 status=$((status + ret))
 
index 9dad7c9d5c95851e29c82da41eb002d525797783..d9dd301b560951f25e20bb473b02980145f3f8eb 100644 (file)
@@ -25,7 +25,7 @@ EXAMPLE4_NS = "example4. 300 IN NS ns.example4."
 AEXAMPLE4_A = "a.example4. 300 IN A 10.53.0.40"
 NSEXAMPLE4_A = "ns.example4. 300 IN A 10.53.0.4"
 
-AROOTSERVER_NS = ". 999999 IN NS a.root-servers.nil."
+AROOTSERVER_NS = ". 300 IN NS a.root-servers.nil."
 
 INPUTPARAMS = "ns, qname, qtype, rd, cached, rcode, answer, authority, additional"
 
index 95977c66d6309bd207d3ab43d3b5a07b71759601..8def29ed6b4eea7e7273483bddb955019032bea3 100644 (file)
@@ -138,6 +138,11 @@ enum {
                                                 * CD=0, but retry with CD=1
                                                 * if it returns SERVFAIL.
                                                 */
+       DNS_FETCHOPT_PRIMING = 1 << 19,         /*%< Root priming fetch.
+                                                * Copies the '.' NS answer
+                                                * and root-server glue from
+                                                * the response into
+                                                * view->rootdb. */
 
        /*% EDNS version bits: */
        DNS_FETCHOPT_EDNSVERSIONSET = 1 << 23,
index df6026a1edd5f3d2ffa8032198e06d686bb4278d..574dfe11bf3475dbd768305548b91ce7001e6a7c 100644 (file)
 isc_result_t
 dns_rootns_create(isc_mem_t *mctx, dns_rdataclass_t rdclass,
                  const char *filename, dns_db_t **target);
-
-void
-dns_root_checkhints(dns_view_t *view, dns_db_t *hints, dns_db_t *db);
-/*
- * Reports differences between hints and the real roots.
- *
- * Requires view, hints and (cache) db to be valid.
- */
index e8c0af78b1c8ac88957894eac16b9ad4ca342554..2eadc2689870c11bb4dc91770f8fefd32f553fc4 100644 (file)
@@ -57,6 +57,7 @@
 #include <stdbool.h>
 #include <stdio.h>
 
+#include <isc/atomic.h>
 #include <isc/magic.h>
 #include <isc/mutex.h>
 #include <isc/net.h>
@@ -87,19 +88,20 @@ typedef struct MDB_env MDB_env;
 
 struct dns_view {
        /* Unlocked. */
-       unsigned int       magic;
-       isc_mem_t         *mctx;
-       dns_rdataclass_t   rdclass;
-       char              *name;
-       dns_zt_t          *zonetable;
-       dns_resolver_t    *resolver;
-       dns_adb_t         *adb;
-       dns_requestmgr_t  *requestmgr;
-       dns_dispatchmgr_t *dispatchmgr;
-       dns_cache_t       *cache;
-       dns_db_t          *cachedb;
-       dns_db_t          *hints;
-       dns_delegdb_t     *deleg;
+       unsigned int         magic;
+       isc_mem_t           *mctx;
+       dns_rdataclass_t     rdclass;
+       char                *name;
+       dns_zt_t            *zonetable;
+       dns_resolver_t      *resolver;
+       dns_adb_t           *adb;
+       dns_requestmgr_t    *requestmgr;
+       dns_dispatchmgr_t   *dispatchmgr;
+       dns_cache_t         *cache;
+       dns_db_t            *cachedb;
+       dns_db_t            *rootdb;
+       atomic_uint_fast32_t rootdb_expires;
+       dns_delegdb_t       *deleg;
 
        /*
         * security roots and negative trust anchors.
@@ -418,20 +420,25 @@ dns_view_setcache(dns_view_t *view, dns_cache_t *cache, bool shared);
  */
 
 void
-dns_view_sethints(dns_view_t *view, dns_db_t *hints);
+dns_view_setrootdb(dns_view_t *view, dns_db_t *rootdb);
 /*%<
- * Set the view's hints database.
+ * Set the view's root delegation database.
+ *
+ * This seeds the rootdb at cold start from a root hints file; it is then
+ * overwritten by the resolver when priming succeeds (see
+ * dns_resolver_prime()).  Callers consult the rootdb via dns_view_find()
+ * and bestzonecut to obtain the root NS set regardless of TTL.
  *
  * Requires:
  *
- *\li  'view' is a valid, unfrozen view, whose hints database has not been
+ *\li  'view' is a valid, unfrozen view, whose root database has not been
  *     set.
  *
- *\li  'hints' is a valid zone database.
+ *\li  'rootdb' is a valid zone database.
  *
  * Ensures:
  *
- * \li         The hints database of 'view' is 'hints'.
+ * \li         The root database of 'view' is 'rootdb'.
  */
 
 void
index 454df5a5a3fa8560f8c8dd1bff4b2eca84a15aae..3e5018f241f4e1734d34987abfec8c24360693d5 100644 (file)
@@ -1035,6 +1035,9 @@ rctx_chaseds(respctx_t *rctx, dns_message_t *message,
 static void
 rctx_done(respctx_t *rctx, isc_result_t result);
 
+static void
+update_rootdb(dns_view_t *view, dns_message_t *message);
+
 static void
 rctx_logpacket(respctx_t *rctx);
 
@@ -7886,6 +7889,16 @@ resquery_response_continue(void *arg, isc_result_t result) {
                        rctx_done(rctx, tresult);
                        goto cleanup;
                }
+
+               /*
+                * For a priming response, copy the '.' NS answer and
+                * root-server glue straight from the wire into
+                * view->rootdb so bestzonecut and ADB see the refreshed
+                * set without a cache round trip.
+                */
+               if ((fctx->options & DNS_FETCHOPT_PRIMING) != 0) {
+                       update_rootdb(fctx->res->view, query->rmessage);
+               }
        }
 
        /*
@@ -10005,12 +10018,128 @@ dns_resolver_create(dns_view_t *view, unsigned int options,
        return ISC_R_SUCCESS;
 }
 
+/*
+ * Copy the A or AAAA rdataset at 'target' out of 'message's ADDITIONAL
+ * section into 'rootdb' under 'ver', replacing any existing record of
+ * that type.  Returns ISC_R_SUCCESS if the glue was stored or if the
+ * response did not carry glue for this target (both benign); any other
+ * result indicates a zone-DB failure that the caller should roll back
+ * on.  Shrinks '*minttlp' to the TTL of the stored rdataset.
+ */
+static isc_result_t
+update_rootdb_glue(dns_db_t *rootdb, dns_dbversion_t *ver,
+                  dns_message_t *message, const dns_name_t *target,
+                  dns_rdatatype_t type, isc_stdtime_t now,
+                  dns_ttl_t *minttlp) {
+       dns_name_t *name = NULL;
+       dns_rdataset_t *rdataset = NULL;
+       dns_dbnode_t *node = NULL;
+       isc_result_t result;
+
+       result = dns_message_findname(message, DNS_SECTION_ADDITIONAL, target,
+                                     type, 0, &name, &rdataset);
+       if (result != ISC_R_SUCCESS) {
+               /* No glue for this target in the response. */
+               return ISC_R_SUCCESS;
+       }
+
+       RETERR(dns_db_findnode(rootdb, name, true, &node));
+
+       (void)dns_db_deleterdataset(rootdb, node, ver, type, 0);
+       result = dns_db_addrdataset(rootdb, node, ver, now, rdataset, 0, NULL);
+       dns_db_detachnode(&node);
+       if (result != ISC_R_SUCCESS && result != DNS_R_UNCHANGED) {
+               return result;
+       }
+
+       if (rdataset->ttl < *minttlp) {
+               *minttlp = rdataset->ttl;
+       }
+       return ISC_R_SUCCESS;
+}
+
+/*
+ * Refresh 'view->rootdb' from a priming response message.  The '.' NS
+ * rdataset is replaced with the fetched one and, for each nameserver
+ * it lists, the matching A/AAAA glue from the response's ADDITIONAL
+ * section is copied in.  Only glue for names that actually appear as
+ * NS targets is accepted; arbitrary ADDITIONAL records are ignored so
+ * a hostile response cannot inject unrelated data into rootdb.  Glue
+ * the response did not carry is left untouched, so the hints-file
+ * records loaded at startup remain as a fallback.
+ *
+ * The version is committed only if every write succeeded; any failure
+ * rolls the whole update back so rootdb never ends up with a '.' NS
+ * rdataset that was deleted but not re-added.
+ *
+ * Called synchronously from response processing while the message is
+ * still live, so records go straight from the wire into rootdb.
+ */
+static void
+update_rootdb(dns_view_t *view, dns_message_t *message) {
+       dns_db_t *rootdb = view->rootdb;
+       dns_dbversion_t *ver = NULL;
+       dns_dbnode_t *node = NULL;
+       dns_rdataset_t *nsset = NULL;
+       isc_stdtime_t now = isc_stdtime_now();
+       dns_ttl_t minttl = UINT32_MAX;
+       isc_result_t result;
+
+       if (rootdb == NULL) {
+               return;
+       }
+
+       result = dns_message_findname(message, DNS_SECTION_ANSWER, dns_rootname,
+                                     dns_rdatatype_ns, 0, NULL, &nsset);
+       if (result != ISC_R_SUCCESS) {
+               return;
+       }
+
+       result = dns_db_newversion(rootdb, &ver);
+       if (result != ISC_R_SUCCESS) {
+               return;
+       }
+
+       CHECK(dns_db_findnode(rootdb, dns_rootname, true, &node));
+
+       (void)dns_db_deleterdataset(rootdb, node, ver, dns_rdatatype_ns, 0);
+       result = dns_db_addrdataset(rootdb, node, ver, now, nsset, 0, NULL);
+       dns_db_detachnode(&node);
+       if (result != ISC_R_SUCCESS && result != DNS_R_UNCHANGED) {
+               goto cleanup;
+       }
+       result = ISC_R_SUCCESS;
+       minttl = nsset->ttl;
+
+       DNS_RDATASET_FOREACH(nsset) {
+               dns_rdata_t rdata = DNS_RDATA_INIT;
+               dns_rdata_ns_t ns;
+
+               dns_rdataset_current(nsset, &rdata);
+               if (dns_rdata_tostruct(&rdata, &ns, NULL) != ISC_R_SUCCESS) {
+                       continue;
+               }
+
+               CHECK(update_rootdb_glue(rootdb, ver, message, &ns.name,
+                                        dns_rdatatype_a, now, &minttl));
+               CHECK(update_rootdb_glue(rootdb, ver, message, &ns.name,
+                                        dns_rdatatype_aaaa, now, &minttl));
+       }
+
+       atomic_store_relaxed(&view->rootdb_expires, (uint32_t)(now + minttl));
+
+cleanup:
+       if (node != NULL) {
+               dns_db_detachnode(&node);
+       }
+       dns_db_closeversion(rootdb, &ver, result == ISC_R_SUCCESS);
+}
+
 static void
 prime_done(void *arg) {
        dns_fetchresponse_t *resp = (dns_fetchresponse_t *)arg;
        dns_resolver_t *res = resp->arg;
        dns_fetch_t *fetch = NULL;
-       dns_db_t *db = NULL;
 
        REQUIRE(VALID_RESOLVER(res));
 
@@ -10027,14 +10156,6 @@ prime_done(void *arg) {
 
        atomic_compare_exchange_enforced(&res->priming, &(bool){ true }, false);
 
-       if (resp->result == ISC_R_SUCCESS && res->view->cache != NULL &&
-           res->view->hints != NULL)
-       {
-               dns_cache_attachdb(res->view->cache, &db);
-               dns_root_checkhints(res->view, res->view->hints, db);
-               dns_db_detach(&db);
-       }
-
        if (resp->node != NULL) {
                dns_db_detachnode(&resp->node);
        }
@@ -10083,9 +10204,9 @@ dns_resolver_prime(dns_resolver_t *res) {
                LOCK(&res->primelock);
                result = dns_resolver_createfetch(
                        res, dns_rootname, dns_rdatatype_ns, NULL, NULL, NULL,
-                       NULL, 0, DNS_FETCHOPT_NOFORWARD, 0, NULL, NULL, NULL,
-                       isc_loop(), prime_done, res, NULL, rdataset, NULL,
-                       &res->primefetch);
+                       NULL, 0, DNS_FETCHOPT_NOFORWARD | DNS_FETCHOPT_PRIMING,
+                       0, NULL, NULL, NULL, isc_loop(), prime_done, res, NULL,
+                       rdataset, NULL, &res->primefetch);
                UNLOCK(&res->primelock);
 
                if (result != ISC_R_SUCCESS) {
index 3306dbec944d63fc220ecd04594796da8bb96e5b..88d853fff45e2946b2d8c845b20b47cb148284da 100644 (file)
@@ -34,9 +34,6 @@
 #include <dns/rootns.h>
 #include <dns/view.h>
 
-/*
- * Also update 'upcoming' when updating 'root_ns'.
- */
 static char root_ns[] =
        ";\n"
        "; Internet Root Nameservers\n"
@@ -82,23 +79,6 @@ static char root_ns[] =
        "M.ROOT-SERVERS.NET.     3600000 IN      A       202.12.27.33\n"
        "M.ROOT-SERVERS.NET.     3600000 IN      AAAA    2001:DC3::35\n";
 
-static unsigned char b_data[] = "\001b\014root-servers\003net";
-
-static struct upcoming {
-       const dns_name_t name;
-       dns_rdatatype_t type;
-       isc_stdtime_t time;
-} upcoming[] = { {
-                        .name = DNS_NAME_INITABSOLUTE(b_data),
-                        .type = dns_rdatatype_a,
-                        .time = 1701086400 /* November 27 2023, 12:00 UTC */
-                },
-                {
-                        .name = DNS_NAME_INITABSOLUTE(b_data),
-                        .type = dns_rdatatype_aaaa,
-                        .time = 1701086400 /* November 27 2023, 12:00 UTC */
-                } };
-
 static isc_result_t
 in_rootns(dns_rdataset_t *rootns, dns_name_t *name) {
        dns_rdata_ns_t ns;
@@ -250,259 +230,3 @@ cleanup:
 
        return result;
 }
-
-static void
-report(dns_view_t *view, dns_name_t *name, bool missing, dns_rdata_t *rdata) {
-       const char *viewname = "", *sep = "";
-       char namebuf[DNS_NAME_FORMATSIZE];
-       char typebuf[DNS_RDATATYPE_FORMATSIZE];
-       char databuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:123.123.123.123")];
-       isc_buffer_t buffer;
-       isc_result_t result;
-
-       if (strcmp(view->name, "_bind") != 0 &&
-           strcmp(view->name, "_default") != 0)
-       {
-               viewname = view->name;
-               sep = ": view ";
-       }
-
-       dns_name_format(name, namebuf, sizeof(namebuf));
-       dns_rdatatype_format(rdata->type, typebuf, sizeof(typebuf));
-       isc_buffer_init(&buffer, databuf, sizeof(databuf) - 1);
-       result = dns_rdata_totext(rdata, NULL, &buffer);
-       RUNTIME_CHECK(result == ISC_R_SUCCESS);
-       databuf[isc_buffer_usedlength(&buffer)] = '\0';
-
-       if (missing) {
-               isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_HINTS,
-                             ISC_LOG_WARNING,
-                             "checkhints%s%s: %s/%s (%s) missing from hints",
-                             sep, viewname, namebuf, typebuf, databuf);
-       } else {
-               isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_HINTS,
-                             ISC_LOG_WARNING,
-                             "checkhints%s%s: %s/%s (%s) extra record "
-                             "in hints",
-                             sep, viewname, namebuf, typebuf, databuf);
-       }
-}
-
-static bool
-inrrset(dns_rdataset_t *rrset, dns_rdata_t *rdata) {
-       DNS_RDATASET_FOREACH(rrset) {
-               dns_rdata_t current = DNS_RDATA_INIT;
-               dns_rdataset_current(rrset, &current);
-               if (dns_rdata_compare(rdata, &current) == 0) {
-                       return true;
-               }
-       }
-       return false;
-}
-
-static bool
-changing(const dns_name_t *name, dns_rdatatype_t type, isc_stdtime_t now) {
-       for (size_t i = 0; i < ARRAY_SIZE(upcoming); i++) {
-               if (upcoming[i].time > now && upcoming[i].type == type &&
-                   dns_name_equal(&upcoming[i].name, name))
-               {
-                       return true;
-               }
-       }
-       return false;
-}
-
-/*
- * Check that the address RRsets match.
- *
- * Note we don't complain about missing glue records.
- */
-
-static void
-check_address_records(dns_view_t *view, dns_db_t *hints, dns_db_t *db,
-                     dns_name_t *name, isc_stdtime_t now) {
-       isc_result_t hresult, rresult;
-       dns_rdataset_t hintrrset, rootrrset;
-       dns_name_t *foundname;
-       dns_fixedname_t fixed;
-
-       dns_rdataset_init(&hintrrset);
-       dns_rdataset_init(&rootrrset);
-       foundname = dns_fixedname_initname(&fixed);
-
-       hresult = dns_db_find(hints, name, NULL, dns_rdatatype_a, 0, now, NULL,
-                             foundname, &hintrrset, NULL);
-       rresult = dns_db_find(db, name, NULL, dns_rdatatype_a,
-                             DNS_DBFIND_GLUEOK, now, NULL, foundname,
-                             &rootrrset, NULL);
-       if (hresult == ISC_R_SUCCESS &&
-           (rresult == ISC_R_SUCCESS || rresult == DNS_R_GLUE))
-       {
-               DNS_RDATASET_FOREACH(&rootrrset) {
-                       dns_rdata_t rdata = DNS_RDATA_INIT;
-                       dns_rdataset_current(&rootrrset, &rdata);
-                       if (!inrrset(&hintrrset, &rdata) &&
-                           !changing(name, dns_rdatatype_a, now))
-                       {
-                               report(view, name, true, &rdata);
-                       }
-               }
-               DNS_RDATASET_FOREACH(&hintrrset) {
-                       dns_rdata_t rdata = DNS_RDATA_INIT;
-                       dns_rdataset_current(&hintrrset, &rdata);
-                       if (!inrrset(&rootrrset, &rdata) &&
-                           !changing(name, dns_rdatatype_a, now))
-                       {
-                               report(view, name, false, &rdata);
-                       }
-               }
-       }
-       if (hresult == ISC_R_NOTFOUND &&
-           (rresult == ISC_R_SUCCESS || rresult == DNS_R_GLUE))
-       {
-               DNS_RDATASET_FOREACH(&rootrrset) {
-                       dns_rdata_t rdata = DNS_RDATA_INIT;
-                       dns_rdataset_current(&rootrrset, &rdata);
-                       report(view, name, true, &rdata);
-               }
-       }
-       dns_rdataset_cleanup(&rootrrset);
-       dns_rdataset_cleanup(&hintrrset);
-
-       /*
-        * Check AAAA records.
-        */
-       hresult = dns_db_find(hints, name, NULL, dns_rdatatype_aaaa, 0, now,
-                             NULL, foundname, &hintrrset, NULL);
-       rresult = dns_db_find(db, name, NULL, dns_rdatatype_aaaa,
-                             DNS_DBFIND_GLUEOK, now, NULL, foundname,
-                             &rootrrset, NULL);
-       if (hresult == ISC_R_SUCCESS &&
-           (rresult == ISC_R_SUCCESS || rresult == DNS_R_GLUE))
-       {
-               DNS_RDATASET_FOREACH(&rootrrset) {
-                       dns_rdata_t rdata = DNS_RDATA_INIT;
-                       dns_rdataset_current(&rootrrset, &rdata);
-                       if (!inrrset(&hintrrset, &rdata) &&
-                           !changing(name, dns_rdatatype_aaaa, now))
-                       {
-                               report(view, name, true, &rdata);
-                       }
-               }
-               DNS_RDATASET_FOREACH(&hintrrset) {
-                       dns_rdata_t rdata = DNS_RDATA_INIT;
-                       dns_rdataset_current(&hintrrset, &rdata);
-                       if (!inrrset(&rootrrset, &rdata) &&
-                           !changing(name, dns_rdatatype_aaaa, now))
-                       {
-                               report(view, name, false, &rdata);
-                       }
-               }
-       }
-       if (hresult == ISC_R_NOTFOUND &&
-           (rresult == ISC_R_SUCCESS || rresult == DNS_R_GLUE))
-       {
-               DNS_RDATASET_FOREACH(&rootrrset) {
-                       dns_rdata_t rdata = DNS_RDATA_INIT;
-                       dns_rdataset_current(&rootrrset, &rdata);
-                       report(view, name, true, &rdata);
-               }
-       }
-       dns_rdataset_cleanup(&rootrrset);
-       dns_rdataset_cleanup(&hintrrset);
-}
-
-void
-dns_root_checkhints(dns_view_t *view, dns_db_t *hints, dns_db_t *db) {
-       isc_result_t result;
-       dns_rdata_ns_t ns;
-       dns_rdataset_t hintns, rootns;
-       const char *viewname = "", *sep = "";
-       isc_stdtime_t now = isc_stdtime_now();
-       dns_name_t *name;
-       dns_fixedname_t fixed;
-
-       REQUIRE(hints != NULL);
-       REQUIRE(db != NULL);
-       REQUIRE(view != NULL);
-
-       if (strcmp(view->name, "_bind") != 0 &&
-           strcmp(view->name, "_default") != 0)
-       {
-               viewname = view->name;
-               sep = ": view ";
-       }
-
-       dns_rdataset_init(&hintns);
-       dns_rdataset_init(&rootns);
-       name = dns_fixedname_initname(&fixed);
-
-       result = dns_db_find(hints, dns_rootname, NULL, dns_rdatatype_ns, 0,
-                            now, NULL, name, &hintns, NULL);
-       if (result != ISC_R_SUCCESS) {
-               isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_HINTS,
-                             ISC_LOG_WARNING,
-                             "checkhints%s%s: unable to get root NS rrset "
-                             "from hints: %s",
-                             sep, viewname, isc_result_totext(result));
-               goto cleanup;
-       }
-
-       result = dns_db_find(db, dns_rootname, NULL, dns_rdatatype_ns, 0, now,
-                            NULL, name, &rootns, NULL);
-       if (result != ISC_R_SUCCESS) {
-               isc_log_write(DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_HINTS,
-                             ISC_LOG_WARNING,
-                             "checkhints%s%s: unable to get root NS rrset "
-                             "from cache: %s",
-                             sep, viewname, isc_result_totext(result));
-               goto cleanup;
-       }
-
-       /*
-        * Look for missing root NS names.
-        */
-       DNS_RDATASET_FOREACH(&rootns) {
-               dns_rdata_t rdata = DNS_RDATA_INIT;
-               dns_rdataset_current(&rootns, &rdata);
-               result = dns_rdata_tostruct(&rdata, &ns, NULL);
-               RUNTIME_CHECK(result == ISC_R_SUCCESS);
-               result = in_rootns(&hintns, &ns.name);
-               if (result != ISC_R_SUCCESS) {
-                       char namebuf[DNS_NAME_FORMATSIZE];
-                       /* missing from hints */
-                       dns_name_format(&ns.name, namebuf, sizeof(namebuf));
-                       isc_log_write(DNS_LOGCATEGORY_GENERAL,
-                                     DNS_LOGMODULE_HINTS, ISC_LOG_WARNING,
-                                     "checkhints%s%s: unable to find root "
-                                     "NS '%s' in hints",
-                                     sep, viewname, namebuf);
-               } else {
-                       check_address_records(view, hints, db, &ns.name, now);
-               }
-       }
-
-       /*
-        * Look for extra root NS names.
-        */
-       DNS_RDATASET_FOREACH(&hintns) {
-               dns_rdata_t rdata = DNS_RDATA_INIT;
-               dns_rdataset_current(&hintns, &rdata);
-               result = dns_rdata_tostruct(&rdata, &ns, NULL);
-               RUNTIME_CHECK(result == ISC_R_SUCCESS);
-               result = in_rootns(&rootns, &ns.name);
-               if (result != ISC_R_SUCCESS) {
-                       char namebuf[DNS_NAME_FORMATSIZE];
-                       /* extra entry in hints */
-                       dns_name_format(&ns.name, namebuf, sizeof(namebuf));
-                       isc_log_write(DNS_LOGCATEGORY_GENERAL,
-                                     DNS_LOGMODULE_HINTS, ISC_LOG_WARNING,
-                                     "checkhints%s%s: extra NS '%s' in hints",
-                                     sep, viewname, namebuf);
-               }
-       }
-
-cleanup:
-       dns_rdataset_cleanup(&rootns);
-       dns_rdataset_cleanup(&hintns);
-}
index a73c55756c6a5f1cf0543d7597673b2f62ce615a..2dfd15c2a16c3afef5294a0ce3a29a94f4257811 100644 (file)
 #define UNREACH_HOLD_TIME_MAX_SEC     (UNREACH_HOLD_TIME_INITIAL_SEC << 6)
 #define UNREACH_BACKOFF_ELIGIBLE_SEC  ((uint16_t)120)
 
+/*
+ * True when rootdb has never been primed, or when its stored TTL has
+ * elapsed.  A stale rootdb still serves records; this is just the
+ * signal that the resolver should kick off a fresh priming fetch.
+ */
+static inline bool
+rootdb_stale(dns_view_t *view) {
+       uint32_t exp = atomic_load_relaxed(&view->rootdb_expires);
+       return exp == 0 || exp <= (uint32_t)isc_stdtime_now();
+}
+
+static inline void
+maybe_prime(dns_view_t *view) {
+       dns_resolver_t *res = NULL;
+
+       if (!rootdb_stale(view)) {
+               return;
+       }
+       if (dns_view_getresolver(view, &res) != ISC_R_SUCCESS) {
+               return;
+       }
+       dns_resolver_prime(res);
+       dns_resolver_detach(&res);
+}
+
 void
 dns_view_create(isc_mem_t *mctx, dns_dispatchmgr_t *dispatchmgr,
                dns_rdataclass_t rdclass, const char *name,
@@ -246,8 +271,8 @@ destroy(dns_view_t *view) {
                ISC_LIST_UNLINK(view->dlz_unsearched, dlzdb, link);
                dns_dlzdestroy(&dlzdb);
        }
-       if (view->hints != NULL) {
-               dns_db_detach(&view->hints);
+       if (view->rootdb != NULL) {
+               dns_db_detach(&view->rootdb);
        }
        if (view->cachedb != NULL) {
                dns_db_detach(&view->cachedb);
@@ -593,13 +618,13 @@ dns_view_iscacheshared(dns_view_t *view) {
 }
 
 void
-dns_view_sethints(dns_view_t *view, dns_db_t *hints) {
+dns_view_setrootdb(dns_view_t *view, dns_db_t *rootdb) {
        REQUIRE(DNS_VIEW_VALID(view));
        REQUIRE(!view->frozen);
-       REQUIRE(view->hints == NULL);
-       REQUIRE(dns_db_iszone(hints));
+       REQUIRE(view->rootdb == NULL);
+       REQUIRE(dns_db_iszone(rootdb));
 
-       dns_db_attach(hints, &view->hints);
+       dns_db_attach(rootdb, &view->rootdb);
 }
 
 void
@@ -878,7 +903,7 @@ db_find:
        }
 
        if (result == ISC_R_NOTFOUND && !is_staticstub_zone && use_hints &&
-           view->hints != NULL)
+           view->rootdb != NULL)
        {
                dns_rdataset_cleanup(rdataset);
                dns_rdataset_cleanup(sigrdataset);
@@ -888,31 +913,29 @@ db_find:
                        }
                        dns_db_detach(&db);
                }
-               result = dns_db_find(view->hints, name, NULL, type, options,
+               result = dns_db_find(view->rootdb, name, NULL, type, options,
                                     now, &node, foundname, rdataset,
                                     sigrdataset);
                if (result == ISC_R_SUCCESS || result == DNS_R_GLUE) {
                        /*
-                        * We just used a hint.  Let the resolver know it
-                        * should consider priming.
+                        * Lazily rearm priming if the rootdb's
+                        * stored TTL has elapsed.  The stale
+                        * record is still returned; it is better
+                        * than nothing until the fresh priming
+                        * fetch completes.
                         */
-                       dns_resolver_t *res = NULL;
-                       result = dns_view_getresolver(view, &res);
-                       if (result == ISC_R_SUCCESS) {
-                               dns_resolver_prime(res);
-                               dns_db_attach(view->hints, &db);
-                               dns_resolver_detach(&res);
-                               result = DNS_R_HINT;
-                       }
+                       maybe_prime(view);
+                       dns_db_attach(view->rootdb, &db);
+                       result = DNS_R_HINT;
                } else if (result == DNS_R_NXRRSET) {
-                       dns_db_attach(view->hints, &db);
+                       dns_db_attach(view->rootdb, &db);
                        result = DNS_R_HINTNXRRSET;
                } else if (result == DNS_R_NXDOMAIN) {
                        result = ISC_R_NOTFOUND;
                }
 
                /*
-                * Cleanup if non-standard hints are used.
+                * Cleanup if the rootdb lookup failed.
                 */
                if (db == NULL && node != NULL) {
                        dns_db_detachnode(&node);
@@ -1116,21 +1139,29 @@ bestzonecut_zoneorcache(dns_view_t *view, const dns_name_t *name,
 }
 
 static isc_result_t
-bestzonecut_hints(dns_view_t *view, dns_name_t *fname, dns_name_t *dcname,
-                 isc_stdtime_t now, dns_rdataset_t *rdataset) {
-       isc_result_t result = ISC_R_NOTFOUND;
-
-       if (view->hints == NULL) {
-               return result;
+bestzonecut_rootdb(dns_view_t *view, dns_name_t *fname, dns_name_t *dcname,
+                  isc_stdtime_t now, dns_rdataset_t *rdataset) {
+       if (view->rootdb == NULL) {
+               return ISC_R_NOTFOUND;
        }
 
-       result = dns_db_find(view->hints, dns_rootname, NULL, dns_rdatatype_ns,
-                            0, now, NULL, fname, rdataset, NULL);
+       isc_result_t result = dns_db_find(view->rootdb, dns_rootname, NULL,
+                                         dns_rdatatype_ns, 0, now, NULL, fname,
+                                         rdataset, NULL);
        if (result != ISC_R_SUCCESS) {
                dns_rdataset_cleanup(rdataset);
-       } else if (dcname != NULL) {
+               return result;
+       }
+
+       if (dcname != NULL) {
                dns_name_copy(fname, dcname);
        }
+       /*
+        * Returned record may be stale by TTL; that's fine — it
+        * is better than nothing until the next priming fetch.
+        * Kick off priming if the stored expiry has elapsed.
+        */
+       maybe_prime(view);
 
        return result;
 }
@@ -1167,10 +1198,11 @@ dns_view_bestzonecut(dns_view_t *view, const dns_name_t *name,
        }
 
        /*
-        * No local zone nor cache match. Last attempt with the hints.
+        * No local zone nor cache match. Last attempt with the rootdb.
         */
        if (result == DNS_R_NXDOMAIN && usehints) {
-               result = bestzonecut_hints(view, fname, dcname, now, &rdataset);
+               result = bestzonecut_rootdb(view, fname, dcname, now,
+                                           &rdataset);
        }
 
        if (result != ISC_R_SUCCESS) {
index b41e8c980d8b39cc5f6c732844433f67934e9cd2..da187369c68eeb6a56655f3721afbd321c22f19d 100644 (file)
@@ -8286,7 +8286,7 @@ query_filter64(query_ctx_t *qctx) {
  */
 static isc_result_t
 query_notfound(query_ctx_t *qctx) {
-       isc_result_t result = ISC_R_UNSET;
+       isc_result_t result = ISC_R_FAILURE;
 
        CCTRACE(ISC_LOG_DEBUG(3), "query_notfound");
 
@@ -8299,24 +8299,21 @@ query_notfound(query_ctx_t *qctx) {
        }
 
        /*
-        * If the cache doesn't even have the root NS,
-        * try to get that from the hints DB.
+        * If the cache doesn't even have the root NS, try to get that from
+        * the rootdb (root hints, refreshed by priming).
         */
-       if (qctx->view->hints != NULL) {
+       if (qctx->view->rootdb != NULL) {
                dns_clientinfomethods_t cm;
                dns_clientinfo_t ci;
 
                dns_clientinfomethods_init(&cm, ns_client_sourceip);
                dns_clientinfo_init(&ci, qctx->client, NULL);
 
-               dns_db_attach(qctx->view->hints, &qctx->db);
+               dns_db_attach(qctx->view->rootdb, &qctx->db);
                result = dns_db_findext(
                        qctx->db, dns_rootname, NULL, dns_rdatatype_ns, 0,
                        qctx->client->inner.now, &qctx->node, qctx->fname, &cm,
                        &ci, qctx->rdataset, qctx->sigrdataset);
-       } else {
-               /* We have no hints. */
-               result = ISC_R_FAILURE;
        }
        if (result != ISC_R_SUCCESS) {
                /*