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