xref: /openbsd-src/usr.sbin/unbound/iterator/iter_hints.c (revision 4c1e55dc91edd6e69ccc60ce855900fbc12cf34f)
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/regional.h"
48 #include "util/log.h"
49 #include "util/config_file.h"
50 #include "util/net_help.h"
51 #include "util/data/dname.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 	hints->region = regional_create();
61 	if(!hints->region) {
62 		hints_delete(hints);
63 		return NULL;
64 	}
65 	return hints;
66 }
67 
68 void
69 hints_delete(struct iter_hints* hints)
70 {
71 	if(!hints)
72 		return;
73 	regional_destroy(hints->region);
74 	free(hints);
75 }
76 
77 /** add hint to delegation hints */
78 static int
79 ah(struct delegpt* dp, struct regional* r, const char* sv, const char* ip)
80 {
81 	struct sockaddr_storage addr;
82 	socklen_t addrlen;
83 	ldns_rdf* rdf = ldns_dname_new_frm_str(sv);
84 	if(!rdf) {
85 		log_err("could not parse %s", sv);
86 		return 0;
87 	}
88 	if(!delegpt_add_ns(dp, r, ldns_rdf_data(rdf), 0) ||
89 	   !extstrtoaddr(ip, &addr, &addrlen) ||
90 	   !delegpt_add_target(dp, r, ldns_rdf_data(rdf), ldns_rdf_size(rdf),
91 		&addr, addrlen, 0, 0)) {
92 		ldns_rdf_deep_free(rdf);
93 		return 0;
94 	}
95 	ldns_rdf_deep_free(rdf);
96 	return 1;
97 }
98 
99 /** obtain compiletime provided root hints */
100 static struct delegpt*
101 compile_time_root_prime(struct regional* r, int do_ip4, int do_ip6)
102 {
103 	/* from:
104 	 ;       This file is made available by InterNIC
105 	 ;       under anonymous FTP as
106 	 ;           file                /domain/named.cache
107 	 ;           on server           FTP.INTERNIC.NET
108 	 ;       -OR-                    RS.INTERNIC.NET
109 	 ;
110 	 ;       related version of root zone:   2010061700
111 	 */
112 	struct delegpt* dp = delegpt_create(r);
113 	if(!dp)
114 		return NULL;
115 	dp->has_parent_side_NS = 1;
116 	if(!delegpt_set_name(dp, r, (uint8_t*)"\000"))
117 		return NULL;
118       if(do_ip4) {
119 	if(!ah(dp, r, "A.ROOT-SERVERS.NET.", "198.41.0.4"))	return 0;
120 	if(!ah(dp, r, "B.ROOT-SERVERS.NET.", "192.228.79.201")) return 0;
121 	if(!ah(dp, r, "C.ROOT-SERVERS.NET.", "192.33.4.12"))	return 0;
122 	if(!ah(dp, r, "D.ROOT-SERVERS.NET.", "128.8.10.90"))	return 0;
123 	if(!ah(dp, r, "E.ROOT-SERVERS.NET.", "192.203.230.10")) return 0;
124 	if(!ah(dp, r, "F.ROOT-SERVERS.NET.", "192.5.5.241"))	return 0;
125 	if(!ah(dp, r, "G.ROOT-SERVERS.NET.", "192.112.36.4"))	return 0;
126 	if(!ah(dp, r, "H.ROOT-SERVERS.NET.", "128.63.2.53"))	return 0;
127 	if(!ah(dp, r, "I.ROOT-SERVERS.NET.", "192.36.148.17"))	return 0;
128 	if(!ah(dp, r, "J.ROOT-SERVERS.NET.", "192.58.128.30"))	return 0;
129 	if(!ah(dp, r, "K.ROOT-SERVERS.NET.", "193.0.14.129"))	return 0;
130 	if(!ah(dp, r, "L.ROOT-SERVERS.NET.", "199.7.83.42"))	return 0;
131 	if(!ah(dp, r, "M.ROOT-SERVERS.NET.", "202.12.27.33"))	return 0;
132       }
133       if(do_ip6) {
134 	if(!ah(dp, r, "A.ROOT-SERVERS.NET.", "2001:503:ba3e::2:30")) return 0;
135 	if(!ah(dp, r, "D.ROOT-SERVERS.NET.", "2001:500:2d::d")) return 0;
136 	if(!ah(dp, r, "F.ROOT-SERVERS.NET.", "2001:500:2f::f")) return 0;
137 	if(!ah(dp, r, "H.ROOT-SERVERS.NET.", "2001:500:1::803f:235")) return 0;
138 	if(!ah(dp, r, "I.ROOT-SERVERS.NET.", "2001:7fe::53")) return 0;
139 	if(!ah(dp, r, "J.ROOT-SERVERS.NET.", "2001:503:c27::2:30")) return 0;
140 	if(!ah(dp, r, "K.ROOT-SERVERS.NET.", "2001:7fd::1")) return 0;
141 	if(!ah(dp, r, "L.ROOT-SERVERS.NET.", "2001:500:3::42")) return 0;
142 	if(!ah(dp, r, "M.ROOT-SERVERS.NET.", "2001:dc3::35")) return 0;
143       }
144 	return dp;
145 }
146 
147 /** insert new hint info into hint structure */
148 static int
149 hints_insert(struct iter_hints* hints, uint16_t c, struct delegpt* dp,
150 	int noprime)
151 {
152 	struct iter_hints_stub* node = regional_alloc(hints->region,
153 		sizeof(struct iter_hints_stub));
154 	uint8_t* nm;
155 	if(!node)
156 		return 0;
157 	nm = regional_alloc_init(hints->region, dp->name, dp->namelen);
158 	if(!nm)
159 		return 0;
160 	node->dp = dp;
161 	node->noprime = (uint8_t)noprime;
162 	if(!name_tree_insert(&hints->tree, &node->node, nm, dp->namelen,
163 		dp->namelabs, c)) {
164 		log_err("second hints ignored.");
165 	}
166 	return 1;
167 }
168 
169 /** set stub name */
170 static int
171 read_stubs_name(struct iter_hints* hints, struct config_stub* s,
172 	struct delegpt* dp)
173 {
174 	ldns_rdf* rdf;
175 	if(!s->name) {
176 		log_err("stub zone without a name");
177 		return 0;
178 	}
179 	rdf = ldns_dname_new_frm_str(s->name);
180 	if(!rdf) {
181 		log_err("cannot parse stub zone name %s", s->name);
182 		return 0;
183 	}
184 	if(!delegpt_set_name(dp, hints->region, ldns_rdf_data(rdf))) {
185 		ldns_rdf_deep_free(rdf);
186 		log_err("out of memory");
187 		return 0;
188 	}
189 	ldns_rdf_deep_free(rdf);
190 	return 1;
191 }
192 
193 /** set stub host names */
194 static int
195 read_stubs_host(struct iter_hints* hints, struct config_stub* s,
196 	struct delegpt* dp)
197 {
198 	struct config_strlist* p;
199 	ldns_rdf* rdf;
200 	for(p = s->hosts; p; p = p->next) {
201 		log_assert(p->str);
202 		rdf = ldns_dname_new_frm_str(p->str);
203 		if(!rdf) {
204 			log_err("cannot parse stub %s nameserver name: '%s'",
205 				s->name, p->str);
206 			return 0;
207 		}
208 		if(!delegpt_add_ns(dp, hints->region, ldns_rdf_data(rdf), 0)) {
209 			ldns_rdf_deep_free(rdf);
210 			log_err("out of memory");
211 			return 0;
212 		}
213 		ldns_rdf_deep_free(rdf);
214 	}
215 	return 1;
216 }
217 
218 /** set stub server addresses */
219 static int
220 read_stubs_addr(struct iter_hints* hints, struct config_stub* s,
221 	struct delegpt* dp)
222 {
223 	struct config_strlist* p;
224 	struct sockaddr_storage addr;
225 	socklen_t addrlen;
226 	for(p = s->addrs; p; p = p->next) {
227 		log_assert(p->str);
228 		if(!extstrtoaddr(p->str, &addr, &addrlen)) {
229 			log_err("cannot parse stub %s ip address: '%s'",
230 				s->name, p->str);
231 			return 0;
232 		}
233 		if(!delegpt_add_addr(dp, hints->region, &addr, addrlen, 0, 0)) {
234 			log_err("out of memory");
235 			return 0;
236 		}
237 	}
238 	return 1;
239 }
240 
241 /** read stubs config */
242 static int
243 read_stubs(struct iter_hints* hints, struct config_file* cfg)
244 {
245 	struct config_stub* s;
246 	for(s = cfg->stubs; s; s = s->next) {
247 		struct delegpt* dp = delegpt_create(hints->region);
248 		if(!dp) {
249 			log_err("out of memory");
250 			return 0;
251 		}
252 		dp->has_parent_side_NS = 1;
253 		if(!read_stubs_name(hints, s, dp) ||
254 			!read_stubs_host(hints, s, dp) ||
255 			!read_stubs_addr(hints, s, dp))
256 			return 0;
257 		if(!hints_insert(hints, LDNS_RR_CLASS_IN, dp, !s->isprime))
258 			return 0;
259 		delegpt_log(VERB_QUERY, dp);
260 	}
261 	return 1;
262 }
263 
264 /** read root hints from file */
265 static int
266 read_root_hints(struct iter_hints* hints, char* fname)
267 {
268 	int lineno = 0;
269 	uint32_t default_ttl = 0;
270 	ldns_rdf* origin = NULL;
271 	ldns_rdf* prev_rr = NULL;
272 	struct delegpt* dp;
273 	ldns_rr* rr = NULL;
274 	ldns_status status;
275 	uint16_t c = LDNS_RR_CLASS_IN;
276 	FILE* f = fopen(fname, "r");
277 	if(!f) {
278 		log_err("could not read root hints %s: %s",
279 			fname, strerror(errno));
280 		return 0;
281 	}
282 	dp = delegpt_create(hints->region);
283 	if(!dp) {
284 		log_err("out of memory reading root hints");
285 		fclose(f);
286 		return 0;
287 	}
288 	verbose(VERB_QUERY, "Reading root hints from %s", fname);
289 	dp->has_parent_side_NS = 1;
290 	while(!feof(f)) {
291 		status = ldns_rr_new_frm_fp_l(&rr, f,
292 			&default_ttl, &origin, &prev_rr, &lineno);
293 		if(status == LDNS_STATUS_SYNTAX_EMPTY ||
294 			status == LDNS_STATUS_SYNTAX_TTL ||
295 			status == LDNS_STATUS_SYNTAX_ORIGIN)
296 			continue;
297 		if(status != LDNS_STATUS_OK) {
298 			log_err("reading root hints %s %d: %s", fname,
299 				lineno, ldns_get_errorstr_by_id(status));
300 			goto stop_read;
301 		}
302 		if(ldns_rr_get_type(rr) == LDNS_RR_TYPE_NS) {
303 			if(!delegpt_add_ns(dp, hints->region,
304 				ldns_rdf_data(ldns_rr_rdf(rr, 0)), 0)) {
305 				log_err("out of memory reading root hints");
306 				goto stop_read;
307 			}
308 			c = ldns_rr_get_class(rr);
309 			if(!dp->name) {
310 				if(!delegpt_set_name(dp, hints->region,
311 					ldns_rdf_data(ldns_rr_owner(rr)))){
312 					log_err("out of memory.");
313 					goto stop_read;
314 				}
315 			}
316 		} else if(ldns_rr_get_type(rr) == LDNS_RR_TYPE_A) {
317 			struct sockaddr_in sa;
318 			socklen_t len = (socklen_t)sizeof(sa);
319 			memset(&sa, 0, len);
320 			sa.sin_family = AF_INET;
321 			sa.sin_port = (in_port_t)htons(UNBOUND_DNS_PORT);
322 			memmove(&sa.sin_addr,
323 				ldns_rdf_data(ldns_rr_rdf(rr, 0)), INET_SIZE);
324 			if(!delegpt_add_target(dp, hints->region,
325 					ldns_rdf_data(ldns_rr_owner(rr)),
326 					ldns_rdf_size(ldns_rr_owner(rr)),
327 					(struct sockaddr_storage*)&sa, len,
328 					0, 0)) {
329 				log_err("out of memory reading root hints");
330 				goto stop_read;
331 			}
332 		} else if(ldns_rr_get_type(rr) == LDNS_RR_TYPE_AAAA) {
333 			struct sockaddr_in6 sa;
334 			socklen_t len = (socklen_t)sizeof(sa);
335 			memset(&sa, 0, len);
336 			sa.sin6_family = AF_INET6;
337 			sa.sin6_port = (in_port_t)htons(UNBOUND_DNS_PORT);
338 			memmove(&sa.sin6_addr,
339 				ldns_rdf_data(ldns_rr_rdf(rr, 0)), INET6_SIZE);
340 			if(!delegpt_add_target(dp, hints->region,
341 					ldns_rdf_data(ldns_rr_owner(rr)),
342 					ldns_rdf_size(ldns_rr_owner(rr)),
343 					(struct sockaddr_storage*)&sa, len,
344 					0, 0)) {
345 				log_err("out of memory reading root hints");
346 				goto stop_read;
347 			}
348 		} else {
349 			log_warn("root hints %s:%d skipping type %d",
350 				fname, lineno, ldns_rr_get_type(rr));
351 		}
352 
353 		ldns_rr_free(rr);
354 	}
355 
356 	if (origin)
357 		ldns_rdf_deep_free(origin);
358 	if (prev_rr)
359 		ldns_rdf_deep_free(prev_rr);
360 	fclose(f);
361 	if(!dp->name) {
362 		log_warn("root hints %s: no NS content", fname);
363 		return 1;
364 	}
365 	if(!hints_insert(hints, c, dp, 0)) {
366 		return 0;
367 	}
368 	delegpt_log(VERB_QUERY, dp);
369 	return 1;
370 
371 stop_read:
372 	if (origin)
373 		ldns_rdf_deep_free(origin);
374 	if (prev_rr)
375 		ldns_rdf_deep_free(prev_rr);
376 	fclose(f);
377 	return 0;
378 }
379 
380 /** read root hints list */
381 static int
382 read_root_hints_list(struct iter_hints* hints, struct config_file* cfg)
383 {
384 	struct config_strlist* p;
385 	for(p = cfg->root_hints; p; p = p->next) {
386 		log_assert(p->str);
387 		if(p->str && p->str[0]) {
388 			char* f = p->str;
389 			if(cfg->chrootdir && cfg->chrootdir[0] &&
390 				strncmp(p->str, cfg->chrootdir,
391 				strlen(cfg->chrootdir)) == 0)
392 				f += strlen(cfg->chrootdir);
393 			if(!read_root_hints(hints, f))
394 				return 0;
395 		}
396 	}
397 	return 1;
398 }
399 
400 int
401 hints_apply_cfg(struct iter_hints* hints, struct config_file* cfg)
402 {
403 	regional_free_all(hints->region);
404 	name_tree_init(&hints->tree);
405 
406 	/* read root hints */
407 	if(!read_root_hints_list(hints, cfg))
408 		return 0;
409 
410 	/* read stub hints */
411 	if(!read_stubs(hints, cfg))
412 		return 0;
413 
414 	/* use fallback compiletime root hints */
415 	if(!hints_lookup_root(hints, LDNS_RR_CLASS_IN)) {
416 		struct delegpt* dp = compile_time_root_prime(hints->region,
417 			cfg->do_ip4, cfg->do_ip6);
418 		verbose(VERB_ALGO, "no config, using builtin root hints.");
419 		if(!dp)
420 			return 0;
421 		if(!hints_insert(hints, LDNS_RR_CLASS_IN, dp, 0))
422 			return 0;
423 	}
424 
425 	name_tree_init_parents(&hints->tree);
426 	return 1;
427 }
428 
429 struct delegpt*
430 hints_lookup_root(struct iter_hints* hints, uint16_t qclass)
431 {
432 	uint8_t rootlab = 0;
433 	struct iter_hints_stub *stub;
434 	stub = (struct iter_hints_stub*)name_tree_find(&hints->tree,
435 		&rootlab, 1, 1, qclass);
436 	if(!stub)
437 		return NULL;
438 	return stub->dp;
439 }
440 
441 struct iter_hints_stub*
442 hints_lookup_stub(struct iter_hints* hints, uint8_t* qname,
443 	uint16_t qclass, struct delegpt* cache_dp)
444 {
445 	size_t len;
446 	int labs;
447 	struct iter_hints_stub *r;
448 
449 	/* first lookup the stub */
450 	labs = dname_count_size_labels(qname, &len);
451 	r = (struct iter_hints_stub*)name_tree_lookup(&hints->tree, qname,
452 		len, labs, qclass);
453 	if(!r) return NULL;
454 
455 	/* If there is no cache (root prime situation) */
456 	if(cache_dp == NULL) {
457 		if(r->dp->namelabs != 1)
458 			return r; /* no cache dp, use any non-root stub */
459 		return NULL;
460 	}
461 
462 	/*
463 	 * If the stub is same as the delegation we got
464 	 * And has noprime set, we need to 'prime' to use this stub instead.
465 	 */
466 	if(r->noprime && query_dname_compare(cache_dp->name, r->dp->name)==0)
467 		return r; /* use this stub instead of cached dp */
468 
469 	/*
470 	 * If our cached delegation point is above the hint, we need to prime.
471 	 */
472 	if(dname_strict_subdomain(r->dp->name, r->dp->namelabs,
473 		cache_dp->name, cache_dp->namelabs))
474 		return r; /* need to prime this stub */
475 	return NULL;
476 }
477 
478 int hints_next_root(struct iter_hints* hints, uint16_t* qclass)
479 {
480 	return name_tree_next_root(&hints->tree, qclass);
481 }
482 
483 size_t
484 hints_get_mem(struct iter_hints* hints)
485 {
486 	if(!hints) return 0;
487 	return sizeof(*hints) + regional_get_mem(hints->region);
488 }
489