Home | History | Annotate | Line # | Download | only in iterator
      1 /*
      2  * iterator/iter_hints.c - iterative resolver module stub and root hints.
      3  *
      4  * Copyright (c) 2007, NLnet Labs. All rights reserved.
      5  *
      6  * This software is open source.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  *
     12  * Redistributions of source code must retain the above copyright notice,
     13  * this list of conditions and the following disclaimer.
     14  *
     15  * Redistributions in binary form must reproduce the above copyright notice,
     16  * this list of conditions and the following disclaimer in the documentation
     17  * and/or other materials provided with the distribution.
     18  *
     19  * Neither the name of the NLNET LABS nor the names of its contributors may
     20  * be used to endorse or promote products derived from this software without
     21  * specific prior written permission.
     22  *
     23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     24  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     26  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     27  * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     28  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
     29  * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     30  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
     31  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
     32  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     33  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     34  */
     35 
     36 /**
     37  * \file
     38  *
     39  * This file contains functions to assist the iterator module.
     40  * Keep track of stub and root hints, and read those from config.
     41  */
     42 #include "config.h"
     43 #include "iterator/iter_hints.h"
     44 #include "iterator/iter_delegpt.h"
     45 #include "util/log.h"
     46 #include "util/config_file.h"
     47 #include "util/net_help.h"
     48 #include "util/data/dname.h"
     49 #include "sldns/rrdef.h"
     50 #include "sldns/str2wire.h"
     51 #include "sldns/wire2str.h"
     52 
     53 struct iter_hints*
     54 hints_create(void)
     55 {
     56 	struct iter_hints* hints = (struct iter_hints*)calloc(1,
     57 		sizeof(struct iter_hints));
     58 	if(!hints)
     59 		return NULL;
     60 	lock_rw_init(&hints->lock);
     61 	lock_protect(&hints->lock, &hints->tree, sizeof(hints->tree));
     62 	return hints;
     63 }
     64 
     65 static void hints_stub_free(struct iter_hints_stub* s)
     66 {
     67 	if(!s) return;
     68 	delegpt_free_mlc(s->dp);
     69 	free(s);
     70 }
     71 
     72 static void delhintnode(rbnode_type* n, void* ATTR_UNUSED(arg))
     73 {
     74 	struct iter_hints_stub* node = (struct iter_hints_stub*)n;
     75 	hints_stub_free(node);
     76 }
     77 
     78 static void hints_del_tree(struct iter_hints* hints)
     79 {
     80 	traverse_postorder(&hints->tree, &delhintnode, NULL);
     81 }
     82 
     83 void
     84 hints_delete(struct iter_hints* hints)
     85 {
     86 	if(!hints)
     87 		return;
     88 	lock_rw_destroy(&hints->lock);
     89 	hints_del_tree(hints);
     90 	free(hints);
     91 }
     92 
     93 /** add hint to delegation hints */
     94 static int
     95 ah(struct delegpt* dp, const char* sv, const char* ip)
     96 {
     97 	struct sockaddr_storage addr;
     98 	socklen_t addrlen;
     99 	size_t dname_len;
    100 	uint8_t* dname = sldns_str2wire_dname(sv, &dname_len);
    101 	if(!dname) {
    102 		log_err("could not parse %s", sv);
    103 		return 0;
    104 	}
    105 	if(!delegpt_add_ns_mlc(dp, dname, 0, NULL, UNBOUND_DNS_PORT) ||
    106 	   !extstrtoaddr(ip, &addr, &addrlen, UNBOUND_DNS_PORT) ||
    107 	   !delegpt_add_target_mlc(dp, dname, dname_len,
    108 		&addr, addrlen, 0, 0)) {
    109 		free(dname);
    110 		return 0;
    111 	}
    112 	free(dname);
    113 	return 1;
    114 }
    115 
    116 /** obtain compiletime provided root hints */
    117 static struct delegpt*
    118 compile_time_root_prime(int do_ip4, int do_ip6)
    119 {
    120 	/* from:
    121 	 ;       This file is made available by InterNIC
    122 	 ;       under anonymous FTP as
    123 	 ;           file                /domain/named.cache
    124 	 ;           on server           FTP.INTERNIC.NET
    125 	 ;       -OR-                    RS.INTERNIC.NET
    126 	 ;
    127 	 ;       related version of root zone:   changes-on-20120103
    128 	 */
    129 	struct delegpt* dp = delegpt_create_mlc((uint8_t*)"\000");
    130 	if(!dp)
    131 		return NULL;
    132 	dp->has_parent_side_NS = 1;
    133       if(do_ip4) {
    134 	if(!ah(dp, "A.ROOT-SERVERS.NET.", "198.41.0.4"))	goto failed;
    135 	if(!ah(dp, "B.ROOT-SERVERS.NET.", "170.247.170.2"))	goto failed;
    136 	if(!ah(dp, "C.ROOT-SERVERS.NET.", "192.33.4.12"))	goto failed;
    137 	if(!ah(dp, "D.ROOT-SERVERS.NET.", "199.7.91.13"))	goto failed;
    138 	if(!ah(dp, "E.ROOT-SERVERS.NET.", "192.203.230.10")) goto failed;
    139 	if(!ah(dp, "F.ROOT-SERVERS.NET.", "192.5.5.241"))	goto failed;
    140 	if(!ah(dp, "G.ROOT-SERVERS.NET.", "192.112.36.4"))	goto failed;
    141 	if(!ah(dp, "H.ROOT-SERVERS.NET.", "198.97.190.53"))	goto failed;
    142 	if(!ah(dp, "I.ROOT-SERVERS.NET.", "192.36.148.17"))	goto failed;
    143 	if(!ah(dp, "J.ROOT-SERVERS.NET.", "192.58.128.30"))	goto failed;
    144 	if(!ah(dp, "K.ROOT-SERVERS.NET.", "193.0.14.129"))	goto failed;
    145 	if(!ah(dp, "L.ROOT-SERVERS.NET.", "199.7.83.42"))	goto failed;
    146 	if(!ah(dp, "M.ROOT-SERVERS.NET.", "202.12.27.33"))	goto failed;
    147       }
    148       if(do_ip6) {
    149 	if(!ah(dp, "A.ROOT-SERVERS.NET.", "2001:503:ba3e::2:30")) goto failed;
    150 	if(!ah(dp, "B.ROOT-SERVERS.NET.", "2801:1b8:10::b")) goto failed;
    151 	if(!ah(dp, "C.ROOT-SERVERS.NET.", "2001:500:2::c")) goto failed;
    152 	if(!ah(dp, "D.ROOT-SERVERS.NET.", "2001:500:2d::d")) goto failed;
    153 	if(!ah(dp, "E.ROOT-SERVERS.NET.", "2001:500:a8::e")) goto failed;
    154 	if(!ah(dp, "F.ROOT-SERVERS.NET.", "2001:500:2f::f")) goto failed;
    155 	if(!ah(dp, "G.ROOT-SERVERS.NET.", "2001:500:12::d0d")) goto failed;
    156 	if(!ah(dp, "H.ROOT-SERVERS.NET.", "2001:500:1::53")) goto failed;
    157 	if(!ah(dp, "I.ROOT-SERVERS.NET.", "2001:7fe::53")) goto failed;
    158 	if(!ah(dp, "J.ROOT-SERVERS.NET.", "2001:503:c27::2:30")) goto failed;
    159 	if(!ah(dp, "K.ROOT-SERVERS.NET.", "2001:7fd::1")) goto failed;
    160 	if(!ah(dp, "L.ROOT-SERVERS.NET.", "2001:500:9f::42")) goto failed;
    161 	if(!ah(dp, "M.ROOT-SERVERS.NET.", "2001:dc3::35")) goto failed;
    162       }
    163 	return dp;
    164 failed:
    165 	delegpt_free_mlc(dp);
    166 	return 0;
    167 }
    168 
    169 /** insert new hint info into hint structure */
    170 static int
    171 hints_insert(struct iter_hints* hints, uint16_t c, struct delegpt* dp,
    172 	int noprime)
    173 {
    174 	struct iter_hints_stub* node = (struct iter_hints_stub*)malloc(
    175 		sizeof(struct iter_hints_stub));
    176 	if(!node) {
    177 		delegpt_free_mlc(dp);
    178 		return 0;
    179 	}
    180 	node->dp = dp;
    181 	node->noprime = (uint8_t)noprime;
    182 	if(!name_tree_insert(&hints->tree, &node->node, dp->name, dp->namelen,
    183 		dp->namelabs, c)) {
    184 		char buf[LDNS_MAX_DOMAINLEN];
    185 		dname_str(dp->name, buf);
    186 		log_err("second hints for zone %s ignored.", buf);
    187 		delegpt_free_mlc(dp);
    188 		free(node);
    189 	}
    190 	return 1;
    191 }
    192 
    193 /** set stub name */
    194 static struct delegpt*
    195 read_stubs_name(struct config_stub* s)
    196 {
    197 	struct delegpt* dp;
    198 	size_t dname_len;
    199 	uint8_t* dname;
    200 	if(!s->name) {
    201 		log_err("stub zone without a name");
    202 		return NULL;
    203 	}
    204 	dname = sldns_str2wire_dname(s->name, &dname_len);
    205 	if(!dname) {
    206 		log_err("cannot parse stub zone name %s", s->name);
    207 		return NULL;
    208 	}
    209 	if(!(dp=delegpt_create_mlc(dname))) {
    210 		free(dname);
    211 		log_err("out of memory");
    212 		return NULL;
    213 	}
    214 	free(dname);
    215 	return dp;
    216 }
    217 
    218 /** set stub host names */
    219 static int
    220 read_stubs_host(struct config_stub* s, struct delegpt* dp)
    221 {
    222 	struct config_strlist* p;
    223 	uint8_t* dname;
    224 	char* tls_auth_name;
    225 	int port;
    226 	for(p = s->hosts; p; p = p->next) {
    227 		log_assert(p->str);
    228 		dname = authextstrtodname(p->str, &port, &tls_auth_name);
    229 		if(!dname) {
    230 			log_err("cannot parse stub %s nameserver name: '%s'",
    231 				s->name, p->str);
    232 			return 0;
    233 		}
    234 		if(dname_subdomain_c(dname, dp->name)) {
    235 			log_warn("stub-host '%s' may have a circular "
    236 				"dependency on stub-zone '%s'",
    237 				p->str, s->name);
    238 		}
    239 #if ! defined(HAVE_SSL_SET1_HOST) && ! defined(HAVE_X509_VERIFY_PARAM_SET1_HOST)
    240 		if(tls_auth_name)
    241 			log_err("no name verification functionality in "
    242 				"ssl library, ignored name for %s", p->str);
    243 #endif
    244 		if(!delegpt_add_ns_mlc(dp, dname, 0, tls_auth_name, port)) {
    245 			free(dname);
    246 			log_err("out of memory");
    247 			return 0;
    248 		}
    249 		free(dname);
    250 	}
    251 	return 1;
    252 }
    253 
    254 /** set stub server addresses */
    255 static int
    256 read_stubs_addr(struct config_stub* s, struct delegpt* dp)
    257 {
    258 	struct config_strlist* p;
    259 	struct sockaddr_storage addr;
    260 	socklen_t addrlen;
    261 	char* auth_name;
    262 	for(p = s->addrs; p; p = p->next) {
    263 		log_assert(p->str);
    264 		if(!authextstrtoaddr(p->str, &addr, &addrlen, &auth_name)) {
    265 			log_err("cannot parse stub %s ip address: '%s'",
    266 				s->name, p->str);
    267 			return 0;
    268 		}
    269 #if ! defined(HAVE_SSL_SET1_HOST) && ! defined(HAVE_X509_VERIFY_PARAM_SET1_HOST)
    270 		if(auth_name)
    271 			log_err("no name verification functionality in "
    272 				"ssl library, ignored name for %s", p->str);
    273 #endif
    274 		if(!delegpt_add_addr_mlc(dp, &addr, addrlen, 0, 0,
    275 			auth_name, -1)) {
    276 			log_err("out of memory");
    277 			return 0;
    278 		}
    279 	}
    280 	return 1;
    281 }
    282 
    283 /** read stubs config */
    284 static int
    285 read_stubs(struct iter_hints* hints, struct config_file* cfg)
    286 {
    287 	struct config_stub* s;
    288 	struct delegpt* dp;
    289 	for(s = cfg->stubs; s; s = s->next) {
    290 		if(!(dp=read_stubs_name(s)))
    291 			return 0;
    292 		if(!read_stubs_host(s, dp) || !read_stubs_addr(s, dp)) {
    293 			delegpt_free_mlc(dp);
    294 			return 0;
    295 		}
    296 		/* the flag is turned off for 'stub-first' so that the
    297 		 * last resort will ask for parent-side NS record and thus
    298 		 * fallback to the internet name servers on a failure */
    299 		dp->has_parent_side_NS = (uint8_t)!s->isfirst;
    300 		/* Do not cache if set. */
    301 		dp->no_cache = s->no_cache;
    302 		/* ssl_upstream */
    303 		dp->ssl_upstream = (uint8_t)s->ssl_upstream;
    304 		/* tcp_upstream */
    305 		dp->tcp_upstream = (uint8_t)s->tcp_upstream;
    306 		delegpt_log(VERB_QUERY, dp);
    307 		if(!hints_insert(hints, LDNS_RR_CLASS_IN, dp, !s->isprime))
    308 			return 0;
    309 	}
    310 	return 1;
    311 }
    312 
    313 /** read root hints from file */
    314 static int
    315 read_root_hints(struct iter_hints* hints, char* fname)
    316 {
    317 	struct sldns_file_parse_state pstate;
    318 	struct delegpt* dp;
    319 	uint8_t rr[LDNS_RR_BUF_SIZE];
    320 	size_t rr_len, dname_len;
    321 	int status;
    322 	uint16_t c = LDNS_RR_CLASS_IN;
    323 	FILE* f = fopen(fname, "r");
    324 	if(!f) {
    325 		log_err("could not read root hints %s: %s",
    326 			fname, strerror(errno));
    327 		return 0;
    328 	}
    329 	dp = delegpt_create_mlc(NULL);
    330 	if(!dp) {
    331 		log_err("out of memory reading root hints");
    332 		fclose(f);
    333 		return 0;
    334 	}
    335 	verbose(VERB_QUERY, "Reading root hints from %s", fname);
    336 	memset(&pstate, 0, sizeof(pstate));
    337 	pstate.lineno = 1;
    338 	dp->has_parent_side_NS = 1;
    339 	while(!feof(f)) {
    340 		rr_len = sizeof(rr);
    341 		dname_len = 0;
    342 		status = sldns_fp2wire_rr_buf(f, rr, &rr_len, &dname_len,
    343 			&pstate);
    344 		if(status != 0) {
    345 			log_err("reading root hints %s %d:%d: %s", fname,
    346 				pstate.lineno, LDNS_WIREPARSE_OFFSET(status),
    347 				sldns_get_errorstr_parse(status));
    348 			goto stop_read;
    349 		}
    350 		if(rr_len == 0)
    351 			continue; /* EMPTY line, TTL or ORIGIN */
    352 		if(sldns_wirerr_get_type(rr, rr_len, dname_len)
    353 			== LDNS_RR_TYPE_NS) {
    354 			if(!delegpt_add_ns_mlc(dp, sldns_wirerr_get_rdata(rr,
    355 				rr_len, dname_len), 0, NULL, UNBOUND_DNS_PORT)) {
    356 				log_err("out of memory reading root hints");
    357 				goto stop_read;
    358 			}
    359 			c = sldns_wirerr_get_class(rr, rr_len, dname_len);
    360 			if(!dp->name) {
    361 				if(!delegpt_set_name_mlc(dp, rr)) {
    362 					log_err("out of memory.");
    363 					goto stop_read;
    364 				}
    365 			}
    366 		} else if(sldns_wirerr_get_type(rr, rr_len, dname_len)
    367 			== LDNS_RR_TYPE_A && sldns_wirerr_get_rdatalen(rr,
    368 			rr_len, dname_len) == INET_SIZE) {
    369 			struct sockaddr_in sa;
    370 			socklen_t len = (socklen_t)sizeof(sa);
    371 			memset(&sa, 0, len);
    372 			sa.sin_family = AF_INET;
    373 			sa.sin_port = (in_port_t)htons(UNBOUND_DNS_PORT);
    374 			memmove(&sa.sin_addr,
    375 				sldns_wirerr_get_rdata(rr, rr_len, dname_len),
    376 				INET_SIZE);
    377 			if(!delegpt_add_target_mlc(dp, rr, dname_len,
    378 					(struct sockaddr_storage*)&sa, len,
    379 					0, 0)) {
    380 				log_err("out of memory reading root hints");
    381 				goto stop_read;
    382 			}
    383 		} else if(sldns_wirerr_get_type(rr, rr_len, dname_len)
    384 			== LDNS_RR_TYPE_AAAA && sldns_wirerr_get_rdatalen(rr,
    385 			rr_len, dname_len) == INET6_SIZE) {
    386 			struct sockaddr_in6 sa;
    387 			socklen_t len = (socklen_t)sizeof(sa);
    388 			memset(&sa, 0, len);
    389 			sa.sin6_family = AF_INET6;
    390 			sa.sin6_port = (in_port_t)htons(UNBOUND_DNS_PORT);
    391 			memmove(&sa.sin6_addr,
    392 				sldns_wirerr_get_rdata(rr, rr_len, dname_len),
    393 				INET6_SIZE);
    394 			if(!delegpt_add_target_mlc(dp, rr, dname_len,
    395 					(struct sockaddr_storage*)&sa, len,
    396 					0, 0)) {
    397 				log_err("out of memory reading root hints");
    398 				goto stop_read;
    399 			}
    400 		} else {
    401 			char buf[17];
    402 			sldns_wire2str_type_buf(sldns_wirerr_get_type(rr,
    403 				rr_len, dname_len), buf, sizeof(buf));
    404 			log_warn("root hints %s:%d skipping type %s",
    405 				fname, pstate.lineno, buf);
    406 		}
    407 	}
    408 	fclose(f);
    409 	if(!dp->name) {
    410 		log_warn("root hints %s: no NS content", fname);
    411 		delegpt_free_mlc(dp);
    412 		return 1;
    413 	}
    414 	delegpt_log(VERB_QUERY, dp);
    415 	if(!hints_insert(hints, c, dp, 0)) {
    416 		return 0;
    417 	}
    418 	return 1;
    419 
    420 stop_read:
    421 	delegpt_free_mlc(dp);
    422 	fclose(f);
    423 	return 0;
    424 }
    425 
    426 /** read root hints list */
    427 static int
    428 read_root_hints_list(struct iter_hints* hints, struct config_file* cfg)
    429 {
    430 	struct config_strlist* p;
    431 	for(p = cfg->root_hints; p; p = p->next) {
    432 		log_assert(p->str);
    433 		if(p->str && p->str[0]) {
    434 			char* f = p->str;
    435 			if(cfg->chrootdir && cfg->chrootdir[0] &&
    436 				strncmp(p->str, cfg->chrootdir,
    437 				strlen(cfg->chrootdir)) == 0)
    438 				f += strlen(cfg->chrootdir);
    439 			if(!read_root_hints(hints, f))
    440 				return 0;
    441 		}
    442 	}
    443 	return 1;
    444 }
    445 
    446 int
    447 hints_apply_cfg(struct iter_hints* hints, struct config_file* cfg)
    448 {
    449 	int nolock = 1;
    450 	lock_rw_wrlock(&hints->lock);
    451 	hints_del_tree(hints);
    452 	name_tree_init(&hints->tree);
    453 
    454 	/* read root hints */
    455 	if(!read_root_hints_list(hints, cfg)) {
    456 		lock_rw_unlock(&hints->lock);
    457 		return 0;
    458 	}
    459 
    460 	/* read stub hints */
    461 	if(!read_stubs(hints, cfg)) {
    462 		lock_rw_unlock(&hints->lock);
    463 		return 0;
    464 	}
    465 
    466 	/* use fallback compiletime root hints */
    467 	if(!hints_find_root(hints, LDNS_RR_CLASS_IN, nolock)) {
    468 		struct delegpt* dp = compile_time_root_prime(cfg->do_ip4,
    469 			cfg->do_ip6);
    470 		verbose(VERB_ALGO, "no config, using builtin root hints.");
    471 		if(!dp) {
    472 			lock_rw_unlock(&hints->lock);
    473 			return 0;
    474 		}
    475 		if(!hints_insert(hints, LDNS_RR_CLASS_IN, dp, 0)) {
    476 			lock_rw_unlock(&hints->lock);
    477 			return 0;
    478 		}
    479 	}
    480 
    481 	name_tree_init_parents(&hints->tree);
    482 	lock_rw_unlock(&hints->lock);
    483 	return 1;
    484 }
    485 
    486 struct delegpt*
    487 hints_find(struct iter_hints* hints, uint8_t* qname, uint16_t qclass,
    488 	int nolock)
    489 {
    490 	struct iter_hints_stub *stub;
    491 	size_t len;
    492 	int has_dp;
    493 	int labs = dname_count_size_labels(qname, &len);
    494 	/* lock_() calls are macros that could be nothing, surround in {} */
    495 	if(!nolock) { lock_rw_rdlock(&hints->lock); }
    496 	stub = (struct iter_hints_stub*)name_tree_find(&hints->tree,
    497 		qname, len, labs, qclass);
    498 	has_dp = stub && stub->dp;
    499 	if(!has_dp && !nolock) { lock_rw_unlock(&hints->lock); }
    500 	return has_dp?stub->dp:NULL;
    501 }
    502 
    503 struct delegpt*
    504 hints_find_root(struct iter_hints* hints, uint16_t qclass, int nolock)
    505 {
    506 	uint8_t rootlab = 0;
    507 	return hints_find(hints, &rootlab, qclass, nolock);
    508 }
    509 
    510 struct iter_hints_stub*
    511 hints_lookup_stub(struct iter_hints* hints, uint8_t* qname,
    512 	uint16_t qclass, struct delegpt* cache_dp, int nolock)
    513 {
    514 	size_t len;
    515 	int labs;
    516 	struct iter_hints_stub *r;
    517 
    518 	/* first lookup the stub */
    519 	labs = dname_count_size_labels(qname, &len);
    520 	/* lock_() calls are macros that could be nothing, surround in {} */
    521 	if(!nolock) { lock_rw_rdlock(&hints->lock); }
    522 	r = (struct iter_hints_stub*)name_tree_lookup(&hints->tree, qname,
    523 		len, labs, qclass);
    524 	if(!r) {
    525 		if(!nolock) { lock_rw_unlock(&hints->lock); }
    526 		return NULL;
    527 	}
    528 
    529 	/* If there is no cache (root prime situation) */
    530 	if(cache_dp == NULL) {
    531 		if(r->dp->namelabs != 1)
    532 			return r; /* no cache dp, use any non-root stub */
    533 		if(!nolock) { lock_rw_unlock(&hints->lock); }
    534 		return NULL;
    535 	}
    536 
    537 	/*
    538 	 * If the stub is same as the delegation we got
    539 	 * And has noprime set, we need to 'prime' to use this stub instead.
    540 	 */
    541 	if(r->noprime && query_dname_compare(cache_dp->name, r->dp->name)==0)
    542 		return r; /* use this stub instead of cached dp */
    543 
    544 	/*
    545 	 * If our cached delegation point is above the hint, we need to prime.
    546 	 */
    547 	if(dname_strict_subdomain(r->dp->name, r->dp->namelabs,
    548 		cache_dp->name, cache_dp->namelabs))
    549 		return r; /* need to prime this stub */
    550 	if(!nolock) { lock_rw_unlock(&hints->lock); }
    551 	return NULL;
    552 }
    553 
    554 int hints_next_root(struct iter_hints* hints, uint16_t* qclass, int nolock)
    555 {
    556 	int ret;
    557 	/* lock_() calls are macros that could be nothing, surround in {} */
    558 	if(!nolock) { lock_rw_rdlock(&hints->lock); }
    559 	ret = name_tree_next_root(&hints->tree, qclass);
    560 	if(!nolock) { lock_rw_unlock(&hints->lock); }
    561 	return ret;
    562 }
    563 
    564 size_t
    565 hints_get_mem(struct iter_hints* hints)
    566 {
    567 	size_t s;
    568 	struct iter_hints_stub* p;
    569 	if(!hints) return 0;
    570 	lock_rw_rdlock(&hints->lock);
    571 	s = sizeof(*hints);
    572 	RBTREE_FOR(p, struct iter_hints_stub*, &hints->tree) {
    573 		s += sizeof(*p) + delegpt_get_mem(p->dp);
    574 	}
    575 	lock_rw_unlock(&hints->lock);
    576 	return s;
    577 }
    578 
    579 int
    580 hints_add_stub(struct iter_hints* hints, uint16_t c, struct delegpt* dp,
    581 	int noprime, int nolock)
    582 {
    583 	struct iter_hints_stub *z;
    584 	/* lock_() calls are macros that could be nothing, surround in {} */
    585 	if(!nolock) { lock_rw_wrlock(&hints->lock); }
    586 	if((z=(struct iter_hints_stub*)name_tree_find(&hints->tree,
    587 		dp->name, dp->namelen, dp->namelabs, c)) != NULL) {
    588 		(void)rbtree_delete(&hints->tree, &z->node);
    589 		hints_stub_free(z);
    590 	}
    591 	if(!hints_insert(hints, c, dp, noprime)) {
    592 		if(!nolock) { lock_rw_unlock(&hints->lock); }
    593 		return 0;
    594 	}
    595 	name_tree_init_parents(&hints->tree);
    596 	if(!nolock) { lock_rw_unlock(&hints->lock); }
    597 	return 1;
    598 }
    599 
    600 void
    601 hints_delete_stub(struct iter_hints* hints, uint16_t c, uint8_t* nm,
    602 	int nolock)
    603 {
    604 	struct iter_hints_stub *z;
    605 	size_t len;
    606 	int labs = dname_count_size_labels(nm, &len);
    607 	/* lock_() calls are macros that could be nothing, surround in {} */
    608 	if(!nolock) { lock_rw_wrlock(&hints->lock); }
    609 	if(!(z=(struct iter_hints_stub*)name_tree_find(&hints->tree,
    610 		nm, len, labs, c))) {
    611 		if(!nolock) { lock_rw_unlock(&hints->lock); }
    612 		return; /* nothing to do */
    613 	}
    614 	(void)rbtree_delete(&hints->tree, &z->node);
    615 	hints_stub_free(z);
    616 	name_tree_init_parents(&hints->tree);
    617 	if(!nolock) { lock_rw_unlock(&hints->lock); }
    618 }
    619 
    620 void
    621 hints_swap_tree(struct iter_hints* hints, struct iter_hints* data)
    622 {
    623 	rbnode_type* oldroot = hints->tree.root;
    624 	size_t oldcount = hints->tree.count;
    625 	hints->tree.root = data->tree.root;
    626 	hints->tree.count = data->tree.count;
    627 	data->tree.root = oldroot;
    628 	data->tree.count = oldcount;
    629 }
    630