Home | History | Annotate | Line # | Download | only in dns
      1 /*	$NetBSD: rootns.c,v 1.9 2025/01/26 16:25:25 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  *
      6  * SPDX-License-Identifier: MPL-2.0
      7  *
      8  * This Source Code Form is subject to the terms of the Mozilla Public
      9  * License, v. 2.0. If a copy of the MPL was not distributed with this
     10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
     11  *
     12  * See the COPYRIGHT file distributed with this work for additional
     13  * information regarding copyright ownership.
     14  */
     15 
     16 /*! \file */
     17 
     18 #include <stdbool.h>
     19 
     20 #include <isc/buffer.h>
     21 #include <isc/result.h>
     22 #include <isc/string.h>
     23 #include <isc/util.h>
     24 
     25 #include <dns/callbacks.h>
     26 #include <dns/db.h>
     27 #include <dns/dbiterator.h>
     28 #include <dns/fixedname.h>
     29 #include <dns/log.h>
     30 #include <dns/master.h>
     31 #include <dns/rdata.h>
     32 #include <dns/rdataset.h>
     33 #include <dns/rdatasetiter.h>
     34 #include <dns/rdatastruct.h>
     35 #include <dns/rdatatype.h>
     36 #include <dns/rootns.h>
     37 #include <dns/view.h>
     38 
     39 /*
     40  * Also update 'upcoming' when updating 'root_ns'.
     41  */
     42 static char root_ns[] =
     43 	";\n"
     44 	"; Internet Root Nameservers\n"
     45 	";\n"
     46 	"$TTL 518400\n"
     47 	".                       518400  IN      NS      A.ROOT-SERVERS.NET.\n"
     48 	".                       518400  IN      NS      B.ROOT-SERVERS.NET.\n"
     49 	".                       518400  IN      NS      C.ROOT-SERVERS.NET.\n"
     50 	".                       518400  IN      NS      D.ROOT-SERVERS.NET.\n"
     51 	".                       518400  IN      NS      E.ROOT-SERVERS.NET.\n"
     52 	".                       518400  IN      NS      F.ROOT-SERVERS.NET.\n"
     53 	".                       518400  IN      NS      G.ROOT-SERVERS.NET.\n"
     54 	".                       518400  IN      NS      H.ROOT-SERVERS.NET.\n"
     55 	".                       518400  IN      NS      I.ROOT-SERVERS.NET.\n"
     56 	".                       518400  IN      NS      J.ROOT-SERVERS.NET.\n"
     57 	".                       518400  IN      NS      K.ROOT-SERVERS.NET.\n"
     58 	".                       518400  IN      NS      L.ROOT-SERVERS.NET.\n"
     59 	".                       518400  IN      NS      M.ROOT-SERVERS.NET.\n"
     60 	"A.ROOT-SERVERS.NET.     3600000 IN      A       198.41.0.4\n"
     61 	"A.ROOT-SERVERS.NET.     3600000 IN      AAAA    2001:503:BA3E::2:30\n"
     62 	"B.ROOT-SERVERS.NET.     3600000 IN      A       170.247.170.2\n"
     63 	"B.ROOT-SERVERS.NET.     3600000 IN      AAAA    2801:1b8:10::b\n"
     64 	"C.ROOT-SERVERS.NET.     3600000 IN      A       192.33.4.12\n"
     65 	"C.ROOT-SERVERS.NET.     3600000 IN      AAAA    2001:500:2::c\n"
     66 	"D.ROOT-SERVERS.NET.     3600000 IN      A       199.7.91.13\n"
     67 	"D.ROOT-SERVERS.NET.     3600000 IN      AAAA    2001:500:2d::d\n"
     68 	"E.ROOT-SERVERS.NET.     3600000 IN      A       192.203.230.10\n"
     69 	"E.ROOT-SERVERS.NET.     3600000 IN      AAAA    2001:500:a8::e\n"
     70 	"F.ROOT-SERVERS.NET.     3600000 IN      A       192.5.5.241\n"
     71 	"F.ROOT-SERVERS.NET.     3600000 IN      AAAA    2001:500:2F::F\n"
     72 	"G.ROOT-SERVERS.NET.     3600000 IN      A       192.112.36.4\n"
     73 	"G.ROOT-SERVERS.NET.     3600000 IN      AAAA    2001:500:12::d0d\n"
     74 	"H.ROOT-SERVERS.NET.     3600000 IN      A       198.97.190.53\n"
     75 	"H.ROOT-SERVERS.NET.     3600000 IN      AAAA    2001:500:1::53\n"
     76 	"I.ROOT-SERVERS.NET.     3600000 IN      A       192.36.148.17\n"
     77 	"I.ROOT-SERVERS.NET.     3600000 IN      AAAA    2001:7fe::53\n"
     78 	"J.ROOT-SERVERS.NET.     3600000 IN      A       192.58.128.30\n"
     79 	"J.ROOT-SERVERS.NET.     3600000 IN      AAAA    2001:503:C27::2:30\n"
     80 	"K.ROOT-SERVERS.NET.     3600000 IN      A       193.0.14.129\n"
     81 	"K.ROOT-SERVERS.NET.     3600000 IN      AAAA    2001:7FD::1\n"
     82 	"L.ROOT-SERVERS.NET.     3600000 IN      A       199.7.83.42\n"
     83 	"L.ROOT-SERVERS.NET.     3600000 IN      AAAA    2001:500:9f::42\n"
     84 	"M.ROOT-SERVERS.NET.     3600000 IN      A       202.12.27.33\n"
     85 	"M.ROOT-SERVERS.NET.     3600000 IN      AAAA    2001:DC3::35\n";
     86 
     87 static unsigned char b_data[] = "\001b\014root-servers\003net";
     88 static unsigned char b_offsets[] = { 0, 2, 15, 19 };
     89 
     90 static struct upcoming {
     91 	const dns_name_t name;
     92 	dns_rdatatype_t type;
     93 	isc_stdtime_t time;
     94 } upcoming[] = { {
     95 			 .name = DNS_NAME_INITABSOLUTE(b_data, b_offsets),
     96 			 .type = dns_rdatatype_a,
     97 			 .time = 1701086400 /* November 27 2023, 12:00 UTC */
     98 		 },
     99 		 {
    100 			 .name = DNS_NAME_INITABSOLUTE(b_data, b_offsets),
    101 			 .type = dns_rdatatype_aaaa,
    102 			 .time = 1701086400 /* November 27 2023, 12:00 UTC */
    103 		 } };
    104 
    105 static isc_result_t
    106 in_rootns(dns_rdataset_t *rootns, dns_name_t *name) {
    107 	isc_result_t result;
    108 	dns_rdata_t rdata = DNS_RDATA_INIT;
    109 	dns_rdata_ns_t ns;
    110 
    111 	if (!dns_rdataset_isassociated(rootns)) {
    112 		return ISC_R_NOTFOUND;
    113 	}
    114 
    115 	result = dns_rdataset_first(rootns);
    116 	while (result == ISC_R_SUCCESS) {
    117 		dns_rdataset_current(rootns, &rdata);
    118 		result = dns_rdata_tostruct(&rdata, &ns, NULL);
    119 		if (result != ISC_R_SUCCESS) {
    120 			return result;
    121 		}
    122 		if (dns_name_compare(name, &ns.name) == 0) {
    123 			return ISC_R_SUCCESS;
    124 		}
    125 		result = dns_rdataset_next(rootns);
    126 		dns_rdata_reset(&rdata);
    127 	}
    128 	if (result == ISC_R_NOMORE) {
    129 		result = ISC_R_NOTFOUND;
    130 	}
    131 	return result;
    132 }
    133 
    134 static isc_result_t
    135 check_node(dns_rdataset_t *rootns, dns_name_t *name,
    136 	   dns_rdatasetiter_t *rdsiter) {
    137 	isc_result_t result;
    138 	dns_rdataset_t rdataset;
    139 
    140 	dns_rdataset_init(&rdataset);
    141 	result = dns_rdatasetiter_first(rdsiter);
    142 	while (result == ISC_R_SUCCESS) {
    143 		dns_rdatasetiter_current(rdsiter, &rdataset);
    144 		switch (rdataset.type) {
    145 		case dns_rdatatype_a:
    146 		case dns_rdatatype_aaaa:
    147 			result = in_rootns(rootns, name);
    148 			if (result != ISC_R_SUCCESS) {
    149 				goto cleanup;
    150 			}
    151 			break;
    152 		case dns_rdatatype_ns:
    153 			if (dns_name_compare(name, dns_rootname) == 0) {
    154 				break;
    155 			}
    156 			FALLTHROUGH;
    157 		default:
    158 			result = ISC_R_FAILURE;
    159 			goto cleanup;
    160 		}
    161 		dns_rdataset_disassociate(&rdataset);
    162 		result = dns_rdatasetiter_next(rdsiter);
    163 	}
    164 	if (result == ISC_R_NOMORE) {
    165 		result = ISC_R_SUCCESS;
    166 	}
    167 cleanup:
    168 	if (dns_rdataset_isassociated(&rdataset)) {
    169 		dns_rdataset_disassociate(&rdataset);
    170 	}
    171 	return result;
    172 }
    173 
    174 static isc_result_t
    175 check_hints(dns_db_t *db) {
    176 	isc_result_t result;
    177 	dns_rdataset_t rootns;
    178 	dns_dbiterator_t *dbiter = NULL;
    179 	dns_dbnode_t *node = NULL;
    180 	isc_stdtime_t now = isc_stdtime_now();
    181 	dns_fixedname_t fixname;
    182 	dns_name_t *name;
    183 	dns_rdatasetiter_t *rdsiter = NULL;
    184 
    185 	name = dns_fixedname_initname(&fixname);
    186 
    187 	dns_rdataset_init(&rootns);
    188 	(void)dns_db_find(db, dns_rootname, NULL, dns_rdatatype_ns, 0, now,
    189 			  NULL, name, &rootns, NULL);
    190 	result = dns_db_createiterator(db, 0, &dbiter);
    191 	if (result != ISC_R_SUCCESS) {
    192 		goto cleanup;
    193 	}
    194 	result = dns_dbiterator_first(dbiter);
    195 	while (result == ISC_R_SUCCESS) {
    196 		result = dns_dbiterator_current(dbiter, &node, name);
    197 		if (result != ISC_R_SUCCESS) {
    198 			goto cleanup;
    199 		}
    200 		result = dns_db_allrdatasets(db, node, NULL, 0, now, &rdsiter);
    201 		if (result != ISC_R_SUCCESS) {
    202 			goto cleanup;
    203 		}
    204 		result = check_node(&rootns, name, rdsiter);
    205 		if (result != ISC_R_SUCCESS) {
    206 			goto cleanup;
    207 		}
    208 		dns_rdatasetiter_destroy(&rdsiter);
    209 		dns_db_detachnode(db, &node);
    210 		result = dns_dbiterator_next(dbiter);
    211 	}
    212 	if (result == ISC_R_NOMORE) {
    213 		result = ISC_R_SUCCESS;
    214 	}
    215 
    216 cleanup:
    217 	if (dns_rdataset_isassociated(&rootns)) {
    218 		dns_rdataset_disassociate(&rootns);
    219 	}
    220 	if (rdsiter != NULL) {
    221 		dns_rdatasetiter_destroy(&rdsiter);
    222 	}
    223 	if (node != NULL) {
    224 		dns_db_detachnode(db, &node);
    225 	}
    226 	if (dbiter != NULL) {
    227 		dns_dbiterator_destroy(&dbiter);
    228 	}
    229 	return result;
    230 }
    231 
    232 isc_result_t
    233 dns_rootns_create(isc_mem_t *mctx, dns_rdataclass_t rdclass,
    234 		  const char *filename, dns_db_t **target) {
    235 	isc_result_t result, eresult;
    236 	isc_buffer_t source;
    237 	unsigned int len;
    238 	dns_rdatacallbacks_t callbacks;
    239 	dns_db_t *db = NULL;
    240 
    241 	REQUIRE(target != NULL && *target == NULL);
    242 
    243 	result = dns_db_create(mctx, ZONEDB_DEFAULT, dns_rootname,
    244 			       dns_dbtype_zone, rdclass, 0, NULL, &db);
    245 	if (result != ISC_R_SUCCESS) {
    246 		goto failure;
    247 	}
    248 
    249 	len = strlen(root_ns);
    250 	isc_buffer_init(&source, root_ns, len);
    251 	isc_buffer_add(&source, len);
    252 
    253 	dns_rdatacallbacks_init(&callbacks);
    254 	result = dns_db_beginload(db, &callbacks);
    255 	if (result != ISC_R_SUCCESS) {
    256 		goto failure;
    257 	}
    258 	if (filename != NULL) {
    259 		/*
    260 		 * Load the hints from the specified filename.
    261 		 */
    262 		result = dns_master_loadfile(filename, &db->origin, &db->origin,
    263 					     db->rdclass, DNS_MASTER_HINT, 0,
    264 					     &callbacks, NULL, NULL, db->mctx,
    265 					     dns_masterformat_text, 0);
    266 	} else if (rdclass == dns_rdataclass_in) {
    267 		/*
    268 		 * Default to using the Internet root servers.
    269 		 */
    270 		result = dns_master_loadbuffer(
    271 			&source, &db->origin, &db->origin, db->rdclass,
    272 			DNS_MASTER_HINT, &callbacks, db->mctx);
    273 	} else {
    274 		result = ISC_R_NOTFOUND;
    275 	}
    276 	eresult = dns_db_endload(db, &callbacks);
    277 	if (result == ISC_R_SUCCESS || result == DNS_R_SEENINCLUDE) {
    278 		result = eresult;
    279 	}
    280 	if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE) {
    281 		goto failure;
    282 	}
    283 	if (check_hints(db) != ISC_R_SUCCESS) {
    284 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
    285 			      DNS_LOGMODULE_HINTS, ISC_LOG_WARNING,
    286 			      "extra data in root hints '%s'",
    287 			      (filename != NULL) ? filename : "<BUILT-IN>");
    288 	}
    289 	*target = db;
    290 	return ISC_R_SUCCESS;
    291 
    292 failure:
    293 	isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_HINTS,
    294 		      ISC_LOG_ERROR,
    295 		      "could not configure root hints from "
    296 		      "'%s': %s",
    297 		      (filename != NULL) ? filename : "<BUILT-IN>",
    298 		      isc_result_totext(result));
    299 
    300 	if (db != NULL) {
    301 		dns_db_detach(&db);
    302 	}
    303 
    304 	return result;
    305 }
    306 
    307 static void
    308 report(dns_view_t *view, dns_name_t *name, bool missing, dns_rdata_t *rdata) {
    309 	const char *viewname = "", *sep = "";
    310 	char namebuf[DNS_NAME_FORMATSIZE];
    311 	char typebuf[DNS_RDATATYPE_FORMATSIZE];
    312 	char databuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:123.123.123.123")];
    313 	isc_buffer_t buffer;
    314 	isc_result_t result;
    315 
    316 	if (strcmp(view->name, "_bind") != 0 &&
    317 	    strcmp(view->name, "_default") != 0)
    318 	{
    319 		viewname = view->name;
    320 		sep = ": view ";
    321 	}
    322 
    323 	dns_name_format(name, namebuf, sizeof(namebuf));
    324 	dns_rdatatype_format(rdata->type, typebuf, sizeof(typebuf));
    325 	isc_buffer_init(&buffer, databuf, sizeof(databuf) - 1);
    326 	result = dns_rdata_totext(rdata, NULL, &buffer);
    327 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
    328 	databuf[isc_buffer_usedlength(&buffer)] = '\0';
    329 
    330 	if (missing) {
    331 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
    332 			      DNS_LOGMODULE_HINTS, ISC_LOG_WARNING,
    333 			      "checkhints%s%s: %s/%s (%s) missing from hints",
    334 			      sep, viewname, namebuf, typebuf, databuf);
    335 	} else {
    336 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
    337 			      DNS_LOGMODULE_HINTS, ISC_LOG_WARNING,
    338 			      "checkhints%s%s: %s/%s (%s) extra record "
    339 			      "in hints",
    340 			      sep, viewname, namebuf, typebuf, databuf);
    341 	}
    342 }
    343 
    344 static bool
    345 inrrset(dns_rdataset_t *rrset, dns_rdata_t *rdata) {
    346 	isc_result_t result;
    347 	dns_rdata_t current = DNS_RDATA_INIT;
    348 
    349 	result = dns_rdataset_first(rrset);
    350 	while (result == ISC_R_SUCCESS) {
    351 		dns_rdataset_current(rrset, &current);
    352 		if (dns_rdata_compare(rdata, &current) == 0) {
    353 			return true;
    354 		}
    355 		dns_rdata_reset(&current);
    356 		result = dns_rdataset_next(rrset);
    357 	}
    358 	return false;
    359 }
    360 
    361 static bool
    362 changing(const dns_name_t *name, dns_rdatatype_t type, isc_stdtime_t now) {
    363 	for (size_t i = 0; i < ARRAY_SIZE(upcoming); i++) {
    364 		if (upcoming[i].time > now && upcoming[i].type == type &&
    365 		    dns_name_equal(&upcoming[i].name, name))
    366 		{
    367 			return true;
    368 		}
    369 	}
    370 	return false;
    371 }
    372 
    373 /*
    374  * Check that the address RRsets match.
    375  *
    376  * Note we don't complain about missing glue records.
    377  */
    378 
    379 static void
    380 check_address_records(dns_view_t *view, dns_db_t *hints, dns_db_t *db,
    381 		      dns_name_t *name, isc_stdtime_t now) {
    382 	isc_result_t hresult, rresult, result;
    383 	dns_rdataset_t hintrrset, rootrrset;
    384 	dns_rdata_t rdata = DNS_RDATA_INIT;
    385 	dns_name_t *foundname;
    386 	dns_fixedname_t fixed;
    387 
    388 	dns_rdataset_init(&hintrrset);
    389 	dns_rdataset_init(&rootrrset);
    390 	foundname = dns_fixedname_initname(&fixed);
    391 
    392 	hresult = dns_db_find(hints, name, NULL, dns_rdatatype_a, 0, now, NULL,
    393 			      foundname, &hintrrset, NULL);
    394 	rresult = dns_db_find(db, name, NULL, dns_rdatatype_a,
    395 			      DNS_DBFIND_GLUEOK, now, NULL, foundname,
    396 			      &rootrrset, NULL);
    397 	if (hresult == ISC_R_SUCCESS &&
    398 	    (rresult == ISC_R_SUCCESS || rresult == DNS_R_GLUE))
    399 	{
    400 		result = dns_rdataset_first(&rootrrset);
    401 		while (result == ISC_R_SUCCESS) {
    402 			dns_rdata_reset(&rdata);
    403 			dns_rdataset_current(&rootrrset, &rdata);
    404 			if (!inrrset(&hintrrset, &rdata) &&
    405 			    !changing(name, dns_rdatatype_a, now))
    406 			{
    407 				report(view, name, true, &rdata);
    408 			}
    409 			result = dns_rdataset_next(&rootrrset);
    410 		}
    411 		result = dns_rdataset_first(&hintrrset);
    412 		while (result == ISC_R_SUCCESS) {
    413 			dns_rdata_reset(&rdata);
    414 			dns_rdataset_current(&hintrrset, &rdata);
    415 			if (!inrrset(&rootrrset, &rdata) &&
    416 			    !changing(name, dns_rdatatype_a, now))
    417 			{
    418 				report(view, name, false, &rdata);
    419 			}
    420 			result = dns_rdataset_next(&hintrrset);
    421 		}
    422 	}
    423 	if (hresult == ISC_R_NOTFOUND &&
    424 	    (rresult == ISC_R_SUCCESS || rresult == DNS_R_GLUE))
    425 	{
    426 		result = dns_rdataset_first(&rootrrset);
    427 		while (result == ISC_R_SUCCESS) {
    428 			dns_rdata_reset(&rdata);
    429 			dns_rdataset_current(&rootrrset, &rdata);
    430 			report(view, name, true, &rdata);
    431 			result = dns_rdataset_next(&rootrrset);
    432 		}
    433 	}
    434 	if (dns_rdataset_isassociated(&rootrrset)) {
    435 		dns_rdataset_disassociate(&rootrrset);
    436 	}
    437 	if (dns_rdataset_isassociated(&hintrrset)) {
    438 		dns_rdataset_disassociate(&hintrrset);
    439 	}
    440 
    441 	/*
    442 	 * Check AAAA records.
    443 	 */
    444 	hresult = dns_db_find(hints, name, NULL, dns_rdatatype_aaaa, 0, now,
    445 			      NULL, foundname, &hintrrset, NULL);
    446 	rresult = dns_db_find(db, name, NULL, dns_rdatatype_aaaa,
    447 			      DNS_DBFIND_GLUEOK, now, NULL, foundname,
    448 			      &rootrrset, NULL);
    449 	if (hresult == ISC_R_SUCCESS &&
    450 	    (rresult == ISC_R_SUCCESS || rresult == DNS_R_GLUE))
    451 	{
    452 		result = dns_rdataset_first(&rootrrset);
    453 		while (result == ISC_R_SUCCESS) {
    454 			dns_rdata_reset(&rdata);
    455 			dns_rdataset_current(&rootrrset, &rdata);
    456 			if (!inrrset(&hintrrset, &rdata) &&
    457 			    !changing(name, dns_rdatatype_aaaa, now))
    458 			{
    459 				report(view, name, true, &rdata);
    460 			}
    461 			dns_rdata_reset(&rdata);
    462 			result = dns_rdataset_next(&rootrrset);
    463 		}
    464 		result = dns_rdataset_first(&hintrrset);
    465 		while (result == ISC_R_SUCCESS) {
    466 			dns_rdata_reset(&rdata);
    467 			dns_rdataset_current(&hintrrset, &rdata);
    468 			if (!inrrset(&rootrrset, &rdata) &&
    469 			    !changing(name, dns_rdatatype_aaaa, now))
    470 			{
    471 				report(view, name, false, &rdata);
    472 			}
    473 			dns_rdata_reset(&rdata);
    474 			result = dns_rdataset_next(&hintrrset);
    475 		}
    476 	}
    477 	if (hresult == ISC_R_NOTFOUND &&
    478 	    (rresult == ISC_R_SUCCESS || rresult == DNS_R_GLUE))
    479 	{
    480 		result = dns_rdataset_first(&rootrrset);
    481 		while (result == ISC_R_SUCCESS) {
    482 			dns_rdata_reset(&rdata);
    483 			dns_rdataset_current(&rootrrset, &rdata);
    484 			report(view, name, true, &rdata);
    485 			dns_rdata_reset(&rdata);
    486 			result = dns_rdataset_next(&rootrrset);
    487 		}
    488 	}
    489 	if (dns_rdataset_isassociated(&rootrrset)) {
    490 		dns_rdataset_disassociate(&rootrrset);
    491 	}
    492 	if (dns_rdataset_isassociated(&hintrrset)) {
    493 		dns_rdataset_disassociate(&hintrrset);
    494 	}
    495 }
    496 
    497 void
    498 dns_root_checkhints(dns_view_t *view, dns_db_t *hints, dns_db_t *db) {
    499 	isc_result_t result;
    500 	dns_rdata_t rdata = DNS_RDATA_INIT;
    501 	dns_rdata_ns_t ns;
    502 	dns_rdataset_t hintns, rootns;
    503 	const char *viewname = "", *sep = "";
    504 	isc_stdtime_t now = isc_stdtime_now();
    505 	dns_name_t *name;
    506 	dns_fixedname_t fixed;
    507 
    508 	REQUIRE(hints != NULL);
    509 	REQUIRE(db != NULL);
    510 	REQUIRE(view != NULL);
    511 
    512 	if (strcmp(view->name, "_bind") != 0 &&
    513 	    strcmp(view->name, "_default") != 0)
    514 	{
    515 		viewname = view->name;
    516 		sep = ": view ";
    517 	}
    518 
    519 	dns_rdataset_init(&hintns);
    520 	dns_rdataset_init(&rootns);
    521 	name = dns_fixedname_initname(&fixed);
    522 
    523 	result = dns_db_find(hints, dns_rootname, NULL, dns_rdatatype_ns, 0,
    524 			     now, NULL, name, &hintns, NULL);
    525 	if (result != ISC_R_SUCCESS) {
    526 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
    527 			      DNS_LOGMODULE_HINTS, ISC_LOG_WARNING,
    528 			      "checkhints%s%s: unable to get root NS rrset "
    529 			      "from hints: %s",
    530 			      sep, viewname, isc_result_totext(result));
    531 		goto cleanup;
    532 	}
    533 
    534 	result = dns_db_find(db, dns_rootname, NULL, dns_rdatatype_ns, 0, now,
    535 			     NULL, name, &rootns, NULL);
    536 	if (result != ISC_R_SUCCESS) {
    537 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
    538 			      DNS_LOGMODULE_HINTS, ISC_LOG_WARNING,
    539 			      "checkhints%s%s: unable to get root NS rrset "
    540 			      "from cache: %s",
    541 			      sep, viewname, isc_result_totext(result));
    542 		goto cleanup;
    543 	}
    544 
    545 	/*
    546 	 * Look for missing root NS names.
    547 	 */
    548 	result = dns_rdataset_first(&rootns);
    549 	while (result == ISC_R_SUCCESS) {
    550 		dns_rdataset_current(&rootns, &rdata);
    551 		result = dns_rdata_tostruct(&rdata, &ns, NULL);
    552 		RUNTIME_CHECK(result == ISC_R_SUCCESS);
    553 		result = in_rootns(&hintns, &ns.name);
    554 		if (result != ISC_R_SUCCESS) {
    555 			char namebuf[DNS_NAME_FORMATSIZE];
    556 			/* missing from hints */
    557 			dns_name_format(&ns.name, namebuf, sizeof(namebuf));
    558 			isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
    559 				      DNS_LOGMODULE_HINTS, ISC_LOG_WARNING,
    560 				      "checkhints%s%s: unable to find root "
    561 				      "NS '%s' in hints",
    562 				      sep, viewname, namebuf);
    563 		} else {
    564 			check_address_records(view, hints, db, &ns.name, now);
    565 		}
    566 		dns_rdata_reset(&rdata);
    567 		result = dns_rdataset_next(&rootns);
    568 	}
    569 	if (result != ISC_R_NOMORE) {
    570 		goto cleanup;
    571 	}
    572 
    573 	/*
    574 	 * Look for extra root NS names.
    575 	 */
    576 	result = dns_rdataset_first(&hintns);
    577 	while (result == ISC_R_SUCCESS) {
    578 		dns_rdataset_current(&hintns, &rdata);
    579 		result = dns_rdata_tostruct(&rdata, &ns, NULL);
    580 		RUNTIME_CHECK(result == ISC_R_SUCCESS);
    581 		result = in_rootns(&rootns, &ns.name);
    582 		if (result != ISC_R_SUCCESS) {
    583 			char namebuf[DNS_NAME_FORMATSIZE];
    584 			/* extra entry in hints */
    585 			dns_name_format(&ns.name, namebuf, sizeof(namebuf));
    586 			isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
    587 				      DNS_LOGMODULE_HINTS, ISC_LOG_WARNING,
    588 				      "checkhints%s%s: extra NS '%s' in hints",
    589 				      sep, viewname, namebuf);
    590 		}
    591 		dns_rdata_reset(&rdata);
    592 		result = dns_rdataset_next(&hintns);
    593 	}
    594 	if (result != ISC_R_NOMORE) {
    595 		goto cleanup;
    596 	}
    597 
    598 cleanup:
    599 	if (dns_rdataset_isassociated(&rootns)) {
    600 		dns_rdataset_disassociate(&rootns);
    601 	}
    602 	if (dns_rdataset_isassociated(&hintns)) {
    603 		dns_rdataset_disassociate(&hintns);
    604 	}
    605 }
    606