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;
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;
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,
}
/*
- * 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);
}
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);
}
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,
named_geoip_shutdown();
#endif /* HAVE_GEOIP2 */
- dns_db_detach(&server->in_roothints);
-
isc_loopmgr_resume();
}
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);
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))
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))
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))
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))
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))
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"
* 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,
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.
- */
#include <stdbool.h>
#include <stdio.h>
+#include <isc/atomic.h>
#include <isc/magic.h>
#include <isc/mutex.h>
#include <isc/net.h>
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.
*/
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
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);
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);
+ }
}
/*
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));
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);
}
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) {
#include <dns/rootns.h>
#include <dns/view.h>
-/*
- * Also update 'upcoming' when updating 'root_ns'.
- */
static char root_ns[] =
";\n"
"; Internet Root Nameservers\n"
"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;
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, ¤t);
- if (dns_rdata_compare(rdata, ¤t) == 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);
-}
#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,
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);
}
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
}
if (result == ISC_R_NOTFOUND && !is_staticstub_zone && use_hints &&
- view->hints != NULL)
+ view->rootdb != NULL)
{
dns_rdataset_cleanup(rdataset);
dns_rdataset_cleanup(sigrdataset);
}
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);
}
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;
}
}
/*
- * 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) {
*/
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");
}
/*
- * 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) {
/*