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