Home | History | Annotate | Line # | Download | only in dns
rootns.c revision 1.1.1.7
      1 /*	$NetBSD: rootns.c,v 1.1.1.7 2024/02/21 21:54:51 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> /* Required for HP/UX (and others?) */
     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;
    181 	dns_fixedname_t fixname;
    182 	dns_name_t *name;
    183 	dns_rdatasetiter_t *rdsiter = NULL;
    184 
    185 	isc_stdtime_get(&now);
    186 
    187 	name = dns_fixedname_initname(&fixname);
    188 
    189 	dns_rdataset_init(&rootns);
    190 	(void)dns_db_find(db, dns_rootname, NULL, dns_rdatatype_ns, 0, now,
    191 			  NULL, name, &rootns, NULL);
    192 	result = dns_db_createiterator(db, 0, &dbiter);
    193 	if (result != ISC_R_SUCCESS) {
    194 		goto cleanup;
    195 	}
    196 	result = dns_dbiterator_first(dbiter);
    197 	while (result == ISC_R_SUCCESS) {
    198 		result = dns_dbiterator_current(dbiter, &node, name);
    199 		if (result != ISC_R_SUCCESS) {
    200 			goto cleanup;
    201 		}
    202 		result = dns_db_allrdatasets(db, node, NULL, 0, now, &rdsiter);
    203 		if (result != ISC_R_SUCCESS) {
    204 			goto cleanup;
    205 		}
    206 		result = check_node(&rootns, name, rdsiter);
    207 		if (result != ISC_R_SUCCESS) {
    208 			goto cleanup;
    209 		}
    210 		dns_rdatasetiter_destroy(&rdsiter);
    211 		dns_db_detachnode(db, &node);
    212 		result = dns_dbiterator_next(dbiter);
    213 	}
    214 	if (result == ISC_R_NOMORE) {
    215 		result = ISC_R_SUCCESS;
    216 	}
    217 
    218 cleanup:
    219 	if (dns_rdataset_isassociated(&rootns)) {
    220 		dns_rdataset_disassociate(&rootns);
    221 	}
    222 	if (rdsiter != NULL) {
    223 		dns_rdatasetiter_destroy(&rdsiter);
    224 	}
    225 	if (node != NULL) {
    226 		dns_db_detachnode(db, &node);
    227 	}
    228 	if (dbiter != NULL) {
    229 		dns_dbiterator_destroy(&dbiter);
    230 	}
    231 	return (result);
    232 }
    233 
    234 isc_result_t
    235 dns_rootns_create(isc_mem_t *mctx, dns_rdataclass_t rdclass,
    236 		  const char *filename, dns_db_t **target) {
    237 	isc_result_t result, eresult;
    238 	isc_buffer_t source;
    239 	unsigned int len;
    240 	dns_rdatacallbacks_t callbacks;
    241 	dns_db_t *db = NULL;
    242 
    243 	REQUIRE(target != NULL && *target == NULL);
    244 
    245 	result = dns_db_create(mctx, "rbt", dns_rootname, dns_dbtype_zone,
    246 			       rdclass, 0, NULL, &db);
    247 	if (result != ISC_R_SUCCESS) {
    248 		goto failure;
    249 	}
    250 
    251 	len = strlen(root_ns);
    252 	isc_buffer_init(&source, root_ns, len);
    253 	isc_buffer_add(&source, len);
    254 
    255 	dns_rdatacallbacks_init(&callbacks);
    256 	result = dns_db_beginload(db, &callbacks);
    257 	if (result != ISC_R_SUCCESS) {
    258 		goto failure;
    259 	}
    260 	if (filename != NULL) {
    261 		/*
    262 		 * Load the hints from the specified filename.
    263 		 */
    264 		result = dns_master_loadfile(filename, &db->origin, &db->origin,
    265 					     db->rdclass, DNS_MASTER_HINT, 0,
    266 					     &callbacks, NULL, NULL, db->mctx,
    267 					     dns_masterformat_text, 0);
    268 	} else if (rdclass == dns_rdataclass_in) {
    269 		/*
    270 		 * Default to using the Internet root servers.
    271 		 */
    272 		result = dns_master_loadbuffer(
    273 			&source, &db->origin, &db->origin, db->rdclass,
    274 			DNS_MASTER_HINT, &callbacks, db->mctx);
    275 	} else {
    276 		result = ISC_R_NOTFOUND;
    277 	}
    278 	eresult = dns_db_endload(db, &callbacks);
    279 	if (result == ISC_R_SUCCESS || result == DNS_R_SEENINCLUDE) {
    280 		result = eresult;
    281 	}
    282 	if (result != ISC_R_SUCCESS && result != DNS_R_SEENINCLUDE) {
    283 		goto failure;
    284 	}
    285 	if (check_hints(db) != ISC_R_SUCCESS) {
    286 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
    287 			      DNS_LOGMODULE_HINTS, ISC_LOG_WARNING,
    288 			      "extra data in root hints '%s'",
    289 			      (filename != NULL) ? filename : "<BUILT-IN>");
    290 	}
    291 	*target = db;
    292 	return (ISC_R_SUCCESS);
    293 
    294 failure:
    295 	isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL, DNS_LOGMODULE_HINTS,
    296 		      ISC_LOG_ERROR,
    297 		      "could not configure root hints from "
    298 		      "'%s': %s",
    299 		      (filename != NULL) ? filename : "<BUILT-IN>",
    300 		      isc_result_totext(result));
    301 
    302 	if (db != NULL) {
    303 		dns_db_detach(&db);
    304 	}
    305 
    306 	return (result);
    307 }
    308 
    309 static void
    310 report(dns_view_t *view, dns_name_t *name, bool missing, dns_rdata_t *rdata) {
    311 	const char *viewname = "", *sep = "";
    312 	char namebuf[DNS_NAME_FORMATSIZE];
    313 	char typebuf[DNS_RDATATYPE_FORMATSIZE];
    314 	char databuf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:123.123.123.123")];
    315 	isc_buffer_t buffer;
    316 	isc_result_t result;
    317 
    318 	if (strcmp(view->name, "_bind") != 0 &&
    319 	    strcmp(view->name, "_default") != 0)
    320 	{
    321 		viewname = view->name;
    322 		sep = ": view ";
    323 	}
    324 
    325 	dns_name_format(name, namebuf, sizeof(namebuf));
    326 	dns_rdatatype_format(rdata->type, typebuf, sizeof(typebuf));
    327 	isc_buffer_init(&buffer, databuf, sizeof(databuf) - 1);
    328 	result = dns_rdata_totext(rdata, NULL, &buffer);
    329 	RUNTIME_CHECK(result == ISC_R_SUCCESS);
    330 	databuf[isc_buffer_usedlength(&buffer)] = '\0';
    331 
    332 	if (missing) {
    333 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
    334 			      DNS_LOGMODULE_HINTS, ISC_LOG_WARNING,
    335 			      "checkhints%s%s: %s/%s (%s) missing from hints",
    336 			      sep, viewname, namebuf, typebuf, databuf);
    337 	} else {
    338 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
    339 			      DNS_LOGMODULE_HINTS, ISC_LOG_WARNING,
    340 			      "checkhints%s%s: %s/%s (%s) extra record "
    341 			      "in hints",
    342 			      sep, viewname, namebuf, typebuf, databuf);
    343 	}
    344 }
    345 
    346 static bool
    347 inrrset(dns_rdataset_t *rrset, dns_rdata_t *rdata) {
    348 	isc_result_t result;
    349 	dns_rdata_t current = DNS_RDATA_INIT;
    350 
    351 	result = dns_rdataset_first(rrset);
    352 	while (result == ISC_R_SUCCESS) {
    353 		dns_rdataset_current(rrset, &current);
    354 		if (dns_rdata_compare(rdata, &current) == 0) {
    355 			return (true);
    356 		}
    357 		dns_rdata_reset(&current);
    358 		result = dns_rdataset_next(rrset);
    359 	}
    360 	return (false);
    361 }
    362 
    363 static bool
    364 changing(const dns_name_t *name, dns_rdatatype_t type, isc_stdtime_t now) {
    365 	for (size_t i = 0; i < ARRAY_SIZE(upcoming); i++) {
    366 		if (upcoming[i].time > now && upcoming[i].type == type &&
    367 		    dns_name_equal(&upcoming[i].name, name))
    368 		{
    369 			return (true);
    370 		}
    371 	}
    372 	return (false);
    373 }
    374 
    375 /*
    376  * Check that the address RRsets match.
    377  *
    378  * Note we don't complain about missing glue records.
    379  */
    380 
    381 static void
    382 check_address_records(dns_view_t *view, dns_db_t *hints, dns_db_t *db,
    383 		      dns_name_t *name, isc_stdtime_t now) {
    384 	isc_result_t hresult, rresult, result;
    385 	dns_rdataset_t hintrrset, rootrrset;
    386 	dns_rdata_t rdata = DNS_RDATA_INIT;
    387 	dns_name_t *foundname;
    388 	dns_fixedname_t fixed;
    389 
    390 	dns_rdataset_init(&hintrrset);
    391 	dns_rdataset_init(&rootrrset);
    392 	foundname = dns_fixedname_initname(&fixed);
    393 
    394 	hresult = dns_db_find(hints, name, NULL, dns_rdatatype_a, 0, now, NULL,
    395 			      foundname, &hintrrset, NULL);
    396 	rresult = dns_db_find(db, name, NULL, dns_rdatatype_a,
    397 			      DNS_DBFIND_GLUEOK, now, NULL, foundname,
    398 			      &rootrrset, NULL);
    399 	if (hresult == ISC_R_SUCCESS &&
    400 	    (rresult == ISC_R_SUCCESS || rresult == DNS_R_GLUE))
    401 	{
    402 		result = dns_rdataset_first(&rootrrset);
    403 		while (result == ISC_R_SUCCESS) {
    404 			dns_rdata_reset(&rdata);
    405 			dns_rdataset_current(&rootrrset, &rdata);
    406 			if (!inrrset(&hintrrset, &rdata) &&
    407 			    !changing(name, dns_rdatatype_a, now))
    408 			{
    409 				report(view, name, true, &rdata);
    410 			}
    411 			result = dns_rdataset_next(&rootrrset);
    412 		}
    413 		result = dns_rdataset_first(&hintrrset);
    414 		while (result == ISC_R_SUCCESS) {
    415 			dns_rdata_reset(&rdata);
    416 			dns_rdataset_current(&hintrrset, &rdata);
    417 			if (!inrrset(&rootrrset, &rdata) &&
    418 			    !changing(name, dns_rdatatype_a, now))
    419 			{
    420 				report(view, name, false, &rdata);
    421 			}
    422 			result = dns_rdataset_next(&hintrrset);
    423 		}
    424 	}
    425 	if (hresult == ISC_R_NOTFOUND &&
    426 	    (rresult == ISC_R_SUCCESS || rresult == DNS_R_GLUE))
    427 	{
    428 		result = dns_rdataset_first(&rootrrset);
    429 		while (result == ISC_R_SUCCESS) {
    430 			dns_rdata_reset(&rdata);
    431 			dns_rdataset_current(&rootrrset, &rdata);
    432 			report(view, name, true, &rdata);
    433 			result = dns_rdataset_next(&rootrrset);
    434 		}
    435 	}
    436 	if (dns_rdataset_isassociated(&rootrrset)) {
    437 		dns_rdataset_disassociate(&rootrrset);
    438 	}
    439 	if (dns_rdataset_isassociated(&hintrrset)) {
    440 		dns_rdataset_disassociate(&hintrrset);
    441 	}
    442 
    443 	/*
    444 	 * Check AAAA records.
    445 	 */
    446 	hresult = dns_db_find(hints, name, NULL, dns_rdatatype_aaaa, 0, now,
    447 			      NULL, foundname, &hintrrset, NULL);
    448 	rresult = dns_db_find(db, name, NULL, dns_rdatatype_aaaa,
    449 			      DNS_DBFIND_GLUEOK, now, NULL, foundname,
    450 			      &rootrrset, NULL);
    451 	if (hresult == ISC_R_SUCCESS &&
    452 	    (rresult == ISC_R_SUCCESS || rresult == DNS_R_GLUE))
    453 	{
    454 		result = dns_rdataset_first(&rootrrset);
    455 		while (result == ISC_R_SUCCESS) {
    456 			dns_rdata_reset(&rdata);
    457 			dns_rdataset_current(&rootrrset, &rdata);
    458 			if (!inrrset(&hintrrset, &rdata) &&
    459 			    !changing(name, dns_rdatatype_aaaa, now))
    460 			{
    461 				report(view, name, true, &rdata);
    462 			}
    463 			dns_rdata_reset(&rdata);
    464 			result = dns_rdataset_next(&rootrrset);
    465 		}
    466 		result = dns_rdataset_first(&hintrrset);
    467 		while (result == ISC_R_SUCCESS) {
    468 			dns_rdata_reset(&rdata);
    469 			dns_rdataset_current(&hintrrset, &rdata);
    470 			if (!inrrset(&rootrrset, &rdata) &&
    471 			    !changing(name, dns_rdatatype_aaaa, now))
    472 			{
    473 				report(view, name, false, &rdata);
    474 			}
    475 			dns_rdata_reset(&rdata);
    476 			result = dns_rdataset_next(&hintrrset);
    477 		}
    478 	}
    479 	if (hresult == ISC_R_NOTFOUND &&
    480 	    (rresult == ISC_R_SUCCESS || rresult == DNS_R_GLUE))
    481 	{
    482 		result = dns_rdataset_first(&rootrrset);
    483 		while (result == ISC_R_SUCCESS) {
    484 			dns_rdata_reset(&rdata);
    485 			dns_rdataset_current(&rootrrset, &rdata);
    486 			report(view, name, true, &rdata);
    487 			dns_rdata_reset(&rdata);
    488 			result = dns_rdataset_next(&rootrrset);
    489 		}
    490 	}
    491 	if (dns_rdataset_isassociated(&rootrrset)) {
    492 		dns_rdataset_disassociate(&rootrrset);
    493 	}
    494 	if (dns_rdataset_isassociated(&hintrrset)) {
    495 		dns_rdataset_disassociate(&hintrrset);
    496 	}
    497 }
    498 
    499 void
    500 dns_root_checkhints(dns_view_t *view, dns_db_t *hints, dns_db_t *db) {
    501 	isc_result_t result;
    502 	dns_rdata_t rdata = DNS_RDATA_INIT;
    503 	dns_rdata_ns_t ns;
    504 	dns_rdataset_t hintns, rootns;
    505 	const char *viewname = "", *sep = "";
    506 	isc_stdtime_t now;
    507 	dns_name_t *name;
    508 	dns_fixedname_t fixed;
    509 
    510 	REQUIRE(hints != NULL);
    511 	REQUIRE(db != NULL);
    512 	REQUIRE(view != NULL);
    513 
    514 	isc_stdtime_get(&now);
    515 
    516 	if (strcmp(view->name, "_bind") != 0 &&
    517 	    strcmp(view->name, "_default") != 0)
    518 	{
    519 		viewname = view->name;
    520 		sep = ": view ";
    521 	}
    522 
    523 	dns_rdataset_init(&hintns);
    524 	dns_rdataset_init(&rootns);
    525 	name = dns_fixedname_initname(&fixed);
    526 
    527 	result = dns_db_find(hints, dns_rootname, NULL, dns_rdatatype_ns, 0,
    528 			     now, NULL, name, &hintns, NULL);
    529 	if (result != ISC_R_SUCCESS) {
    530 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
    531 			      DNS_LOGMODULE_HINTS, ISC_LOG_WARNING,
    532 			      "checkhints%s%s: unable to get root NS rrset "
    533 			      "from hints: %s",
    534 			      sep, viewname, isc_result_totext(result));
    535 		goto cleanup;
    536 	}
    537 
    538 	result = dns_db_find(db, dns_rootname, NULL, dns_rdatatype_ns, 0, now,
    539 			     NULL, name, &rootns, NULL);
    540 	if (result != ISC_R_SUCCESS) {
    541 		isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
    542 			      DNS_LOGMODULE_HINTS, ISC_LOG_WARNING,
    543 			      "checkhints%s%s: unable to get root NS rrset "
    544 			      "from cache: %s",
    545 			      sep, viewname, isc_result_totext(result));
    546 		goto cleanup;
    547 	}
    548 
    549 	/*
    550 	 * Look for missing root NS names.
    551 	 */
    552 	result = dns_rdataset_first(&rootns);
    553 	while (result == ISC_R_SUCCESS) {
    554 		dns_rdataset_current(&rootns, &rdata);
    555 		result = dns_rdata_tostruct(&rdata, &ns, NULL);
    556 		RUNTIME_CHECK(result == ISC_R_SUCCESS);
    557 		result = in_rootns(&hintns, &ns.name);
    558 		if (result != ISC_R_SUCCESS) {
    559 			char namebuf[DNS_NAME_FORMATSIZE];
    560 			/* missing from hints */
    561 			dns_name_format(&ns.name, namebuf, sizeof(namebuf));
    562 			isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
    563 				      DNS_LOGMODULE_HINTS, ISC_LOG_WARNING,
    564 				      "checkhints%s%s: unable to find root "
    565 				      "NS '%s' in hints",
    566 				      sep, viewname, namebuf);
    567 		} else {
    568 			check_address_records(view, hints, db, &ns.name, now);
    569 		}
    570 		dns_rdata_reset(&rdata);
    571 		result = dns_rdataset_next(&rootns);
    572 	}
    573 	if (result != ISC_R_NOMORE) {
    574 		goto cleanup;
    575 	}
    576 
    577 	/*
    578 	 * Look for extra root NS names.
    579 	 */
    580 	result = dns_rdataset_first(&hintns);
    581 	while (result == ISC_R_SUCCESS) {
    582 		dns_rdataset_current(&hintns, &rdata);
    583 		result = dns_rdata_tostruct(&rdata, &ns, NULL);
    584 		RUNTIME_CHECK(result == ISC_R_SUCCESS);
    585 		result = in_rootns(&rootns, &ns.name);
    586 		if (result != ISC_R_SUCCESS) {
    587 			char namebuf[DNS_NAME_FORMATSIZE];
    588 			/* extra entry in hints */
    589 			dns_name_format(&ns.name, namebuf, sizeof(namebuf));
    590 			isc_log_write(dns_lctx, DNS_LOGCATEGORY_GENERAL,
    591 				      DNS_LOGMODULE_HINTS, ISC_LOG_WARNING,
    592 				      "checkhints%s%s: extra NS '%s' in hints",
    593 				      sep, viewname, namebuf);
    594 		}
    595 		dns_rdata_reset(&rdata);
    596 		result = dns_rdataset_next(&hintns);
    597 	}
    598 	if (result != ISC_R_NOMORE) {
    599 		goto cleanup;
    600 	}
    601 
    602 cleanup:
    603 	if (dns_rdataset_isassociated(&rootns)) {
    604 		dns_rdataset_disassociate(&rootns);
    605 	}
    606 	if (dns_rdataset_isassociated(&hintns)) {
    607 		dns_rdataset_disassociate(&hintns);
    608 	}
    609 }
    610