xref: /openbsd-src/usr.sbin/unbound/iterator/iter_hints.c (revision 55cae7495557a9a6146617cb64cad583183127f2)
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 LIMITED
25  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
27  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * 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 <ldns/dname.h>
44 #include <ldns/rr.h>
45 #include "iterator/iter_hints.h"
46 #include "iterator/iter_delegpt.h"
47 #include "util/log.h"
48 #include "util/config_file.h"
49 #include "util/net_help.h"
50 #include "util/data/dname.h"
51 
52 struct iter_hints*
53 hints_create(void)
54 {
55 	struct iter_hints* hints = (struct iter_hints*)calloc(1,
56 		sizeof(struct iter_hints));
57 	if(!hints)
58 		return NULL;
59 	return hints;
60 }
61 
62 static void hints_stub_free(struct iter_hints_stub* s)
63 {
64 	if(!s) return;
65 	delegpt_free_mlc(s->dp);
66 	free(s);
67 }
68 
69 static void delhintnode(rbnode_t* n, void* ATTR_UNUSED(arg))
70 {
71 	struct iter_hints_stub* node = (struct iter_hints_stub*)n;
72 	hints_stub_free(node);
73 }
74 
75 static void hints_del_tree(struct iter_hints* hints)
76 {
77 	traverse_postorder(&hints->tree, &delhintnode, NULL);
78 }
79 
80 void
81 hints_delete(struct iter_hints* hints)
82 {
83 	if(!hints)
84 		return;
85 	hints_del_tree(hints);
86 	free(hints);
87 }
88 
89 /** add hint to delegation hints */
90 static int
91 ah(struct delegpt* dp, const char* sv, const char* ip)
92 {
93 	struct sockaddr_storage addr;
94 	socklen_t addrlen;
95 	ldns_rdf* rdf = ldns_dname_new_frm_str(sv);
96 	if(!rdf) {
97 		log_err("could not parse %s", sv);
98 		return 0;
99 	}
100 	if(!delegpt_add_ns_mlc(dp, ldns_rdf_data(rdf), 0) ||
101 	   !extstrtoaddr(ip, &addr, &addrlen) ||
102 	   !delegpt_add_target_mlc(dp, ldns_rdf_data(rdf), ldns_rdf_size(rdf),
103 		&addr, addrlen, 0, 0)) {
104 		ldns_rdf_deep_free(rdf);
105 		return 0;
106 	}
107 	ldns_rdf_deep_free(rdf);
108 	return 1;
109 }
110 
111 /** obtain compiletime provided root hints */
112 static struct delegpt*
113 compile_time_root_prime(int do_ip4, int do_ip6)
114 {
115 	/* from:
116 	 ;       This file is made available by InterNIC
117 	 ;       under anonymous FTP as
118 	 ;           file                /domain/named.cache
119 	 ;           on server           FTP.INTERNIC.NET
120 	 ;       -OR-                    RS.INTERNIC.NET
121 	 ;
122 	 ;       related version of root zone:   2010061700
123 	 */
124 	struct delegpt* dp = delegpt_create_mlc((uint8_t*)"\000");
125 	if(!dp)
126 		return NULL;
127 	dp->has_parent_side_NS = 1;
128       if(do_ip4) {
129 	if(!ah(dp, "A.ROOT-SERVERS.NET.", "198.41.0.4"))	goto failed;
130 	if(!ah(dp, "B.ROOT-SERVERS.NET.", "192.228.79.201")) goto failed;
131 	if(!ah(dp, "C.ROOT-SERVERS.NET.", "192.33.4.12"))	goto failed;
132 	if(!ah(dp, "D.ROOT-SERVERS.NET.", "199.7.91.13"))	goto failed;
133 	if(!ah(dp, "E.ROOT-SERVERS.NET.", "192.203.230.10")) goto failed;
134 	if(!ah(dp, "F.ROOT-SERVERS.NET.", "192.5.5.241"))	goto failed;
135 	if(!ah(dp, "G.ROOT-SERVERS.NET.", "192.112.36.4"))	goto failed;
136 	if(!ah(dp, "H.ROOT-SERVERS.NET.", "128.63.2.53"))	goto failed;
137 	if(!ah(dp, "I.ROOT-SERVERS.NET.", "192.36.148.17"))	goto failed;
138 	if(!ah(dp, "J.ROOT-SERVERS.NET.", "192.58.128.30"))	goto failed;
139 	if(!ah(dp, "K.ROOT-SERVERS.NET.", "193.0.14.129"))	goto failed;
140 	if(!ah(dp, "L.ROOT-SERVERS.NET.", "199.7.83.42"))	goto failed;
141 	if(!ah(dp, "M.ROOT-SERVERS.NET.", "202.12.27.33"))	goto failed;
142       }
143       if(do_ip6) {
144 	if(!ah(dp, "A.ROOT-SERVERS.NET.", "2001:503:ba3e::2:30")) goto failed;
145 	if(!ah(dp, "D.ROOT-SERVERS.NET.", "2001:500:2d::d")) goto failed;
146 	if(!ah(dp, "F.ROOT-SERVERS.NET.", "2001:500:2f::f")) goto failed;
147 	if(!ah(dp, "H.ROOT-SERVERS.NET.", "2001:500:1::803f:235")) goto failed;
148 	if(!ah(dp, "I.ROOT-SERVERS.NET.", "2001:7fe::53")) goto failed;
149 	if(!ah(dp, "J.ROOT-SERVERS.NET.", "2001:503:c27::2:30")) goto failed;
150 	if(!ah(dp, "K.ROOT-SERVERS.NET.", "2001:7fd::1")) goto failed;
151 	if(!ah(dp, "L.ROOT-SERVERS.NET.", "2001:500:3::42")) goto failed;
152 	if(!ah(dp, "M.ROOT-SERVERS.NET.", "2001:dc3::35")) goto failed;
153       }
154 	return dp;
155 failed:
156 	delegpt_free_mlc(dp);
157 	return 0;
158 }
159 
160 /** insert new hint info into hint structure */
161 static int
162 hints_insert(struct iter_hints* hints, uint16_t c, struct delegpt* dp,
163 	int noprime)
164 {
165 	struct iter_hints_stub* node = (struct iter_hints_stub*)malloc(
166 		sizeof(struct iter_hints_stub));
167 	if(!node) {
168 		delegpt_free_mlc(dp);
169 		return 0;
170 	}
171 	node->dp = dp;
172 	node->noprime = (uint8_t)noprime;
173 	if(!name_tree_insert(&hints->tree, &node->node, dp->name, dp->namelen,
174 		dp->namelabs, c)) {
175 		log_err("second hints ignored.");
176 		delegpt_free_mlc(dp);
177 		free(node);
178 	}
179 	return 1;
180 }
181 
182 /** set stub name */
183 static struct delegpt*
184 read_stubs_name(struct config_stub* s)
185 {
186 	struct delegpt* dp;
187 	ldns_rdf* rdf;
188 	if(!s->name) {
189 		log_err("stub zone without a name");
190 		return NULL;
191 	}
192 	rdf = ldns_dname_new_frm_str(s->name);
193 	if(!rdf) {
194 		log_err("cannot parse stub zone name %s", s->name);
195 		return NULL;
196 	}
197 	if(!(dp=delegpt_create_mlc(ldns_rdf_data(rdf)))) {
198 		ldns_rdf_deep_free(rdf);
199 		log_err("out of memory");
200 		return NULL;
201 	}
202 	ldns_rdf_deep_free(rdf);
203 	return dp;
204 }
205 
206 /** set stub host names */
207 static int
208 read_stubs_host(struct config_stub* s, struct delegpt* dp)
209 {
210 	struct config_strlist* p;
211 	ldns_rdf* rdf;
212 	for(p = s->hosts; p; p = p->next) {
213 		log_assert(p->str);
214 		rdf = ldns_dname_new_frm_str(p->str);
215 		if(!rdf) {
216 			log_err("cannot parse stub %s nameserver name: '%s'",
217 				s->name, p->str);
218 			return 0;
219 		}
220 		if(!delegpt_add_ns_mlc(dp, ldns_rdf_data(rdf), 0)) {
221 			ldns_rdf_deep_free(rdf);
222 			log_err("out of memory");
223 			return 0;
224 		}
225 		ldns_rdf_deep_free(rdf);
226 	}
227 	return 1;
228 }
229 
230 /** set stub server addresses */
231 static int
232 read_stubs_addr(struct config_stub* s, struct delegpt* dp)
233 {
234 	struct config_strlist* p;
235 	struct sockaddr_storage addr;
236 	socklen_t addrlen;
237 	for(p = s->addrs; p; p = p->next) {
238 		log_assert(p->str);
239 		if(!extstrtoaddr(p->str, &addr, &addrlen)) {
240 			log_err("cannot parse stub %s ip address: '%s'",
241 				s->name, p->str);
242 			return 0;
243 		}
244 		if(!delegpt_add_addr_mlc(dp, &addr, addrlen, 0, 0)) {
245 			log_err("out of memory");
246 			return 0;
247 		}
248 	}
249 	return 1;
250 }
251 
252 /** read stubs config */
253 static int
254 read_stubs(struct iter_hints* hints, struct config_file* cfg)
255 {
256 	struct config_stub* s;
257 	struct delegpt* dp;
258 	for(s = cfg->stubs; s; s = s->next) {
259 		if(!(dp=read_stubs_name(s)))
260 			return 0;
261 		if(!read_stubs_host(s, dp) || !read_stubs_addr(s, dp)) {
262 			delegpt_free_mlc(dp);
263 			return 0;
264 		}
265 		/* the flag is turned off for 'stub-first' so that the
266 		 * last resort will ask for parent-side NS record and thus
267 		 * fallback to the internet name servers on a failure */
268 		dp->has_parent_side_NS = (uint8_t)!s->isfirst;
269 		delegpt_log(VERB_QUERY, dp);
270 		if(!hints_insert(hints, LDNS_RR_CLASS_IN, dp, !s->isprime))
271 			return 0;
272 	}
273 	return 1;
274 }
275 
276 /** read root hints from file */
277 static int
278 read_root_hints(struct iter_hints* hints, char* fname)
279 {
280 	int lineno = 0;
281 	uint32_t default_ttl = 0;
282 	ldns_rdf* origin = NULL;
283 	ldns_rdf* prev_rr = NULL;
284 	struct delegpt* dp;
285 	ldns_rr* rr = NULL;
286 	ldns_status status;
287 	uint16_t c = LDNS_RR_CLASS_IN;
288 	FILE* f = fopen(fname, "r");
289 	if(!f) {
290 		log_err("could not read root hints %s: %s",
291 			fname, strerror(errno));
292 		return 0;
293 	}
294 	dp = delegpt_create_mlc(NULL);
295 	if(!dp) {
296 		log_err("out of memory reading root hints");
297 		fclose(f);
298 		return 0;
299 	}
300 	verbose(VERB_QUERY, "Reading root hints from %s", fname);
301 	dp->has_parent_side_NS = 1;
302 	while(!feof(f)) {
303 		status = ldns_rr_new_frm_fp_l(&rr, f,
304 			&default_ttl, &origin, &prev_rr, &lineno);
305 		if(status == LDNS_STATUS_SYNTAX_EMPTY ||
306 			status == LDNS_STATUS_SYNTAX_TTL ||
307 			status == LDNS_STATUS_SYNTAX_ORIGIN)
308 			continue;
309 		if(status != LDNS_STATUS_OK) {
310 			log_err("reading root hints %s %d: %s", fname,
311 				lineno, ldns_get_errorstr_by_id(status));
312 			goto stop_read;
313 		}
314 		if(ldns_rr_get_type(rr) == LDNS_RR_TYPE_NS) {
315 			if(!delegpt_add_ns_mlc(dp,
316 				ldns_rdf_data(ldns_rr_rdf(rr, 0)), 0)) {
317 				log_err("out of memory reading root hints");
318 				goto stop_read;
319 			}
320 			c = ldns_rr_get_class(rr);
321 			if(!dp->name) {
322 				if(!delegpt_set_name_mlc(dp,
323 					ldns_rdf_data(ldns_rr_owner(rr)))){
324 					log_err("out of memory.");
325 					goto stop_read;
326 				}
327 			}
328 		} else if(ldns_rr_get_type(rr) == LDNS_RR_TYPE_A) {
329 			struct sockaddr_in sa;
330 			socklen_t len = (socklen_t)sizeof(sa);
331 			memset(&sa, 0, len);
332 			sa.sin_family = AF_INET;
333 			sa.sin_port = (in_port_t)htons(UNBOUND_DNS_PORT);
334 			memmove(&sa.sin_addr,
335 				ldns_rdf_data(ldns_rr_rdf(rr, 0)), INET_SIZE);
336 			if(!delegpt_add_target_mlc(dp,
337 					ldns_rdf_data(ldns_rr_owner(rr)),
338 					ldns_rdf_size(ldns_rr_owner(rr)),
339 					(struct sockaddr_storage*)&sa, len,
340 					0, 0)) {
341 				log_err("out of memory reading root hints");
342 				goto stop_read;
343 			}
344 		} else if(ldns_rr_get_type(rr) == LDNS_RR_TYPE_AAAA) {
345 			struct sockaddr_in6 sa;
346 			socklen_t len = (socklen_t)sizeof(sa);
347 			memset(&sa, 0, len);
348 			sa.sin6_family = AF_INET6;
349 			sa.sin6_port = (in_port_t)htons(UNBOUND_DNS_PORT);
350 			memmove(&sa.sin6_addr,
351 				ldns_rdf_data(ldns_rr_rdf(rr, 0)), INET6_SIZE);
352 			if(!delegpt_add_target_mlc(dp,
353 					ldns_rdf_data(ldns_rr_owner(rr)),
354 					ldns_rdf_size(ldns_rr_owner(rr)),
355 					(struct sockaddr_storage*)&sa, len,
356 					0, 0)) {
357 				log_err("out of memory reading root hints");
358 				goto stop_read;
359 			}
360 		} else {
361 			log_warn("root hints %s:%d skipping type %d",
362 				fname, lineno, ldns_rr_get_type(rr));
363 		}
364 
365 		ldns_rr_free(rr);
366 	}
367 
368 	if (origin)
369 		ldns_rdf_deep_free(origin);
370 	if (prev_rr)
371 		ldns_rdf_deep_free(prev_rr);
372 	fclose(f);
373 	if(!dp->name) {
374 		log_warn("root hints %s: no NS content", fname);
375 		delegpt_free_mlc(dp);
376 		return 1;
377 	}
378 	if(!hints_insert(hints, c, dp, 0)) {
379 		return 0;
380 	}
381 	delegpt_log(VERB_QUERY, dp);
382 	return 1;
383 
384 stop_read:
385 	if (origin)
386 		ldns_rdf_deep_free(origin);
387 	if (prev_rr)
388 		ldns_rdf_deep_free(prev_rr);
389 	delegpt_free_mlc(dp);
390 	fclose(f);
391 	return 0;
392 }
393 
394 /** read root hints list */
395 static int
396 read_root_hints_list(struct iter_hints* hints, struct config_file* cfg)
397 {
398 	struct config_strlist* p;
399 	for(p = cfg->root_hints; p; p = p->next) {
400 		log_assert(p->str);
401 		if(p->str && p->str[0]) {
402 			char* f = p->str;
403 			if(cfg->chrootdir && cfg->chrootdir[0] &&
404 				strncmp(p->str, cfg->chrootdir,
405 				strlen(cfg->chrootdir)) == 0)
406 				f += strlen(cfg->chrootdir);
407 			if(!read_root_hints(hints, f))
408 				return 0;
409 		}
410 	}
411 	return 1;
412 }
413 
414 int
415 hints_apply_cfg(struct iter_hints* hints, struct config_file* cfg)
416 {
417 	hints_del_tree(hints);
418 	name_tree_init(&hints->tree);
419 
420 	/* read root hints */
421 	if(!read_root_hints_list(hints, cfg))
422 		return 0;
423 
424 	/* read stub hints */
425 	if(!read_stubs(hints, cfg))
426 		return 0;
427 
428 	/* use fallback compiletime root hints */
429 	if(!hints_lookup_root(hints, LDNS_RR_CLASS_IN)) {
430 		struct delegpt* dp = compile_time_root_prime(cfg->do_ip4,
431 			cfg->do_ip6);
432 		verbose(VERB_ALGO, "no config, using builtin root hints.");
433 		if(!dp)
434 			return 0;
435 		if(!hints_insert(hints, LDNS_RR_CLASS_IN, dp, 0))
436 			return 0;
437 	}
438 
439 	name_tree_init_parents(&hints->tree);
440 	return 1;
441 }
442 
443 struct delegpt*
444 hints_lookup_root(struct iter_hints* hints, uint16_t qclass)
445 {
446 	uint8_t rootlab = 0;
447 	struct iter_hints_stub *stub;
448 	stub = (struct iter_hints_stub*)name_tree_find(&hints->tree,
449 		&rootlab, 1, 1, qclass);
450 	if(!stub)
451 		return NULL;
452 	return stub->dp;
453 }
454 
455 struct iter_hints_stub*
456 hints_lookup_stub(struct iter_hints* hints, uint8_t* qname,
457 	uint16_t qclass, struct delegpt* cache_dp)
458 {
459 	size_t len;
460 	int labs;
461 	struct iter_hints_stub *r;
462 
463 	/* first lookup the stub */
464 	labs = dname_count_size_labels(qname, &len);
465 	r = (struct iter_hints_stub*)name_tree_lookup(&hints->tree, qname,
466 		len, labs, qclass);
467 	if(!r) return NULL;
468 
469 	/* If there is no cache (root prime situation) */
470 	if(cache_dp == NULL) {
471 		if(r->dp->namelabs != 1)
472 			return r; /* no cache dp, use any non-root stub */
473 		return NULL;
474 	}
475 
476 	/*
477 	 * If the stub is same as the delegation we got
478 	 * And has noprime set, we need to 'prime' to use this stub instead.
479 	 */
480 	if(r->noprime && query_dname_compare(cache_dp->name, r->dp->name)==0)
481 		return r; /* use this stub instead of cached dp */
482 
483 	/*
484 	 * If our cached delegation point is above the hint, we need to prime.
485 	 */
486 	if(dname_strict_subdomain(r->dp->name, r->dp->namelabs,
487 		cache_dp->name, cache_dp->namelabs))
488 		return r; /* need to prime this stub */
489 	return NULL;
490 }
491 
492 int hints_next_root(struct iter_hints* hints, uint16_t* qclass)
493 {
494 	return name_tree_next_root(&hints->tree, qclass);
495 }
496 
497 size_t
498 hints_get_mem(struct iter_hints* hints)
499 {
500 	size_t s;
501 	struct iter_hints_stub* p;
502 	if(!hints) return 0;
503 	s = sizeof(*hints);
504 	RBTREE_FOR(p, struct iter_hints_stub*, &hints->tree) {
505 		s += sizeof(*p) + delegpt_get_mem(p->dp);
506 	}
507 	return s;
508 }
509 
510 int
511 hints_add_stub(struct iter_hints* hints, uint16_t c, struct delegpt* dp,
512 	int noprime)
513 {
514 	struct iter_hints_stub *z;
515 	if((z=(struct iter_hints_stub*)name_tree_find(&hints->tree,
516 		dp->name, dp->namelen, dp->namelabs, c)) != NULL) {
517 		(void)rbtree_delete(&hints->tree, &z->node);
518 		hints_stub_free(z);
519 	}
520 	if(!hints_insert(hints, c, dp, noprime))
521 		return 0;
522 	name_tree_init_parents(&hints->tree);
523 	return 1;
524 }
525 
526 void
527 hints_delete_stub(struct iter_hints* hints, uint16_t c, uint8_t* nm)
528 {
529 	struct iter_hints_stub *z;
530 	size_t len;
531 	int labs = dname_count_size_labels(nm, &len);
532 	if(!(z=(struct iter_hints_stub*)name_tree_find(&hints->tree,
533 		nm, len, labs, c)))
534 		return; /* nothing to do */
535 	(void)rbtree_delete(&hints->tree, &z->node);
536 	hints_stub_free(z);
537 	name_tree_init_parents(&hints->tree);
538 }
539 
540