Home | History | Annotate | Line # | Download | only in dns
      1 /*	$NetBSD: rootns.c,v 1.1 2024/02/18 20:57:33 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, 0, 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 	{
    300 		viewname = view->name;
    301 		sep = ": view ";
    302 	}
    303 
    304 	dns_name_format(name, namebuf, sizeof(namebuf));
    305 	dns_rdatatype_format(rdata->type, typebuf, sizeof(typebuf));
    306 	isc_buffer_init(&buffer, databuf, sizeof(databuf) - 1);
    307 	result = dns_rdata_totext(rdata, NULL, &buffer);
    308 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
    309 	databuf[isc_buffer_usedlength(&buffer)] = '\0';
    310 
    311 	if (missing) {
    312 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
    313 			      DNS_LOGMODULE_HINTS, ISC_LOG_WARNING,
    314 			      "checkhints%s%s: %s/%s (%s) missing from hints",
    315 			      sep, viewname, namebuf, typebuf, databuf);
    316 	} else {
    317 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
    318 			      DNS_LOGMODULE_HINTS, ISC_LOG_WARNING,
    319 			      "checkhints%s%s: %s/%s (%s) extra record "
    320 			      "in hints",
    321 			      sep, viewname, namebuf, typebuf, databuf);
    322 	}
    323 }
    324 
    325 static bool
    326 inrrset(dns_rdataset_t *rrset, dns_rdata_t *rdata) {
    327 	isc_result_t result;
    328 	dns_rdata_t current = DNS_RDATA_INIT;
    329 
    330 	result = dns_rdataset_first(rrset);
    331 	while (result == ISC_R_SUCCESS) {
    332 		dns_rdataset_current(rrset, &current);
    333 		if (dns_rdata_compare(rdata, &current) == 0) {
    334 			return (true);
    335 		}
    336 		dns_rdata_reset(&current);
    337 		result = dns_rdataset_next(rrset);
    338 	}
    339 	return (false);
    340 }
    341 
    342 /*
    343  * Check that the address RRsets match.
    344  *
    345  * Note we don't complain about missing glue records.
    346  */
    347 
    348 static void
    349 check_address_records(dns_view_t *view, dns_db_t *hints, dns_db_t *db,
    350 		      dns_name_t *name, isc_stdtime_t now) {
    351 	isc_result_t hresult, rresult, result;
    352 	dns_rdataset_t hintrrset, rootrrset;
    353 	dns_rdata_t rdata = DNS_RDATA_INIT;
    354 	dns_name_t *foundname;
    355 	dns_fixedname_t fixed;
    356 
    357 	dns_rdataset_init(&hintrrset);
    358 	dns_rdataset_init(&rootrrset);
    359 	foundname = dns_fixedname_initname(&fixed);
    360 
    361 	hresult = dns_db_find(hints, name, NULL, dns_rdatatype_a, 0, now, NULL,
    362 			      foundname, &hintrrset, NULL);
    363 	rresult = dns_db_find(db, name, NULL, dns_rdatatype_a,
    364 			      DNS_DBFIND_GLUEOK, now, NULL, foundname,
    365 			      &rootrrset, NULL);
    366 	if (hresult == ISC_R_SUCCESS &&
    367 	    (rresult == ISC_R_SUCCESS || rresult == DNS_R_GLUE))
    368 	{
    369 		result = dns_rdataset_first(&rootrrset);
    370 		while (result == ISC_R_SUCCESS) {
    371 			dns_rdata_reset(&rdata);
    372 			dns_rdataset_current(&rootrrset, &rdata);
    373 			if (!inrrset(&hintrrset, &rdata)) {
    374 				report(view, name, true, &rdata);
    375 			}
    376 			result = dns_rdataset_next(&rootrrset);
    377 		}
    378 		result = dns_rdataset_first(&hintrrset);
    379 		while (result == ISC_R_SUCCESS) {
    380 			dns_rdata_reset(&rdata);
    381 			dns_rdataset_current(&hintrrset, &rdata);
    382 			if (!inrrset(&rootrrset, &rdata)) {
    383 				report(view, name, false, &rdata);
    384 			}
    385 			result = dns_rdataset_next(&hintrrset);
    386 		}
    387 	}
    388 	if (hresult == ISC_R_NOTFOUND &&
    389 	    (rresult == ISC_R_SUCCESS || rresult == DNS_R_GLUE))
    390 	{
    391 		result = dns_rdataset_first(&rootrrset);
    392 		while (result == ISC_R_SUCCESS) {
    393 			dns_rdata_reset(&rdata);
    394 			dns_rdataset_current(&rootrrset, &rdata);
    395 			report(view, name, true, &rdata);
    396 			result = dns_rdataset_next(&rootrrset);
    397 		}
    398 	}
    399 	if (dns_rdataset_isassociated(&rootrrset)) {
    400 		dns_rdataset_disassociate(&rootrrset);
    401 	}
    402 	if (dns_rdataset_isassociated(&hintrrset)) {
    403 		dns_rdataset_disassociate(&hintrrset);
    404 	}
    405 
    406 	/*
    407 	 * Check AAAA records.
    408 	 */
    409 	hresult = dns_db_find(hints, name, NULL, dns_rdatatype_aaaa, 0, now,
    410 			      NULL, foundname, &hintrrset, NULL);
    411 	rresult = dns_db_find(db, name, NULL, dns_rdatatype_aaaa,
    412 			      DNS_DBFIND_GLUEOK, now, NULL, foundname,
    413 			      &rootrrset, NULL);
    414 	if (hresult == ISC_R_SUCCESS &&
    415 	    (rresult == ISC_R_SUCCESS || rresult == DNS_R_GLUE))
    416 	{
    417 		result = dns_rdataset_first(&rootrrset);
    418 		while (result == ISC_R_SUCCESS) {
    419 			dns_rdata_reset(&rdata);
    420 			dns_rdataset_current(&rootrrset, &rdata);
    421 			if (!inrrset(&hintrrset, &rdata)) {
    422 				report(view, name, true, &rdata);
    423 			}
    424 			dns_rdata_reset(&rdata);
    425 			result = dns_rdataset_next(&rootrrset);
    426 		}
    427 		result = dns_rdataset_first(&hintrrset);
    428 		while (result == ISC_R_SUCCESS) {
    429 			dns_rdata_reset(&rdata);
    430 			dns_rdataset_current(&hintrrset, &rdata);
    431 			if (!inrrset(&rootrrset, &rdata)) {
    432 				report(view, name, false, &rdata);
    433 			}
    434 			dns_rdata_reset(&rdata);
    435 			result = dns_rdataset_next(&hintrrset);
    436 		}
    437 	}
    438 	if (hresult == ISC_R_NOTFOUND &&
    439 	    (rresult == ISC_R_SUCCESS || rresult == DNS_R_GLUE))
    440 	{
    441 		result = dns_rdataset_first(&rootrrset);
    442 		while (result == ISC_R_SUCCESS) {
    443 			dns_rdata_reset(&rdata);
    444 			dns_rdataset_current(&rootrrset, &rdata);
    445 			report(view, name, true, &rdata);
    446 			dns_rdata_reset(&rdata);
    447 			result = dns_rdataset_next(&rootrrset);
    448 		}
    449 	}
    450 	if (dns_rdataset_isassociated(&rootrrset)) {
    451 		dns_rdataset_disassociate(&rootrrset);
    452 	}
    453 	if (dns_rdataset_isassociated(&hintrrset)) {
    454 		dns_rdataset_disassociate(&hintrrset);
    455 	}
    456 }
    457 
    458 void
    459 dns_root_checkhints(dns_view_t *view, dns_db_t *hints, dns_db_t *db) {
    460 	isc_result_t result;
    461 	dns_rdata_t rdata = DNS_RDATA_INIT;
    462 	dns_rdata_ns_t ns;
    463 	dns_rdataset_t hintns, rootns;
    464 	const char *viewname = "", *sep = "";
    465 	isc_stdtime_t now;
    466 	dns_name_t *name;
    467 	dns_fixedname_t fixed;
    468 
    469 	REQUIRE(hints != NULL);
    470 	REQUIRE(db != NULL);
    471 	REQUIRE(view != NULL);
    472 
    473 	isc_stdtime_get(&now);
    474 
    475 	if (strcmp(view->name, "_bind") != 0 &&
    476 	    strcmp(view->name, "_default") != 0)
    477 	{
    478 		viewname = view->name;
    479 		sep = ": view ";
    480 	}
    481 
    482 	dns_rdataset_init(&hintns);
    483 	dns_rdataset_init(&rootns);
    484 	name = dns_fixedname_initname(&fixed);
    485 
    486 	result = dns_db_find(hints, dns_rootname, NULL, dns_rdatatype_ns, 0,
    487 			     now, NULL, name, &hintns, NULL);
    488 	if (result != ISC_R_SUCCESS) {
    489 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
    490 			      DNS_LOGMODULE_HINTS, ISC_LOG_WARNING,
    491 			      "checkhints%s%s: unable to get root NS rrset "
    492 			      "from hints: %s",
    493 			      sep, viewname, dns_result_totext(result));
    494 		goto cleanup;
    495 	}
    496 
    497 	result = dns_db_find(db, dns_rootname, NULL, dns_rdatatype_ns, 0, now,
    498 			     NULL, name, &rootns, NULL);
    499 	if (result != ISC_R_SUCCESS) {
    500 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
    501 			      DNS_LOGMODULE_HINTS, ISC_LOG_WARNING,
    502 			      "checkhints%s%s: unable to get root NS rrset "
    503 			      "from cache: %s",
    504 			      sep, viewname, dns_result_totext(result));
    505 		goto cleanup;
    506 	}
    507 
    508 	/*
    509 	 * Look for missing root NS names.
    510 	 */
    511 	result = dns_rdataset_first(&rootns);
    512 	while (result == ISC_R_SUCCESS) {
    513 		dns_rdataset_current(&rootns, &rdata);
    514 		result = dns_rdata_tostruct(&rdata, &ns, NULL);
    515 		RUNTIME_CHECK(result == ISC_R_SUCCESS);
    516 		result = in_rootns(&hintns, &ns.name);
    517 		if (result != ISC_R_SUCCESS) {
    518 			char namebuf[DNS_NAME_FORMATSIZE];
    519 			/* missing from hints */
    520 			dns_name_format(&ns.name, namebuf, sizeof(namebuf));
    521 			isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
    522 				      DNS_LOGMODULE_HINTS, ISC_LOG_WARNING,
    523 				      "checkhints%s%s: unable to find root "
    524 				      "NS '%s' in hints",
    525 				      sep, viewname, namebuf);
    526 		} else {
    527 			check_address_records(view, hints, db, &ns.name, now);
    528 		}
    529 		dns_rdata_reset(&rdata);
    530 		result = dns_rdataset_next(&rootns);
    531 	}
    532 	if (result != ISC_R_NOMORE) {
    533 		goto cleanup;
    534 	}
    535 
    536 	/*
    537 	 * Look for extra root NS names.
    538 	 */
    539 	result = dns_rdataset_first(&hintns);
    540 	while (result == ISC_R_SUCCESS) {
    541 		dns_rdataset_current(&hintns, &rdata);
    542 		result = dns_rdata_tostruct(&rdata, &ns, NULL);
    543 		RUNTIME_CHECK(result == ISC_R_SUCCESS);
    544 		result = in_rootns(&rootns, &ns.name);
    545 		if (result != ISC_R_SUCCESS) {
    546 			char namebuf[DNS_NAME_FORMATSIZE];
    547 			/* extra entry in hints */
    548 			dns_name_format(&ns.name, namebuf, sizeof(namebuf));
    549 			isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
    550 				      DNS_LOGMODULE_HINTS, ISC_LOG_WARNING,
    551 				      "checkhints%s%s: extra NS '%s' in hints",
    552 				      sep, viewname, namebuf);
    553 		}
    554 		dns_rdata_reset(&rdata);
    555 		result = dns_rdataset_next(&hintns);
    556 	}
    557 	if (result != ISC_R_NOMORE) {
    558 		goto cleanup;
    559 	}
    560 
    561 cleanup:
    562 	if (dns_rdataset_isassociated(&rootns)) {
    563 		dns_rdataset_disassociate(&rootns);
    564 	}
    565 	if (dns_rdataset_isassociated(&hintns)) {
    566 		dns_rdataset_disassociate(&hintns);
    567 	}
    568 }
    569