xref: /freebsd/contrib/ldns/drill/chasetrace.c (revision 257e70f1d5ee61037c8c59b116538d3b6b1427a2)
1 /*
2  * chasetrace.c
3  * Where all the hard work concerning chasing
4  * and tracing is done
5  * (c) 2005, 2006 NLnet Labs
6  *
7  * See the file LICENSE for the license
8  *
9  */
10 
11 #include "drill.h"
12 #include <ldns/ldns.h>
13 
14 /* Cache all RRs from rr_list "rr_list" to "referrals" database for lookup
15  * later on.  Print the NS RRs that were not already present.
16  */
17 static void add_rr_list_to_referrals(
18     ldns_dnssec_zone *referrals, ldns_rr_list *rr_list)
19 {
20 	size_t i;
21 	ldns_rr *rr;
22 	ldns_dnssec_rrsets *rrset;
23 	ldns_dnssec_rrs *rrs;
24 
25 	for (i = 0; i < ldns_rr_list_rr_count(rr_list); i++) {
26 		rr = ldns_rr_list_rr(rr_list, i);
27 		/* Check if a RR equal to "rr" is present in "referrals" */
28 		rrset = ldns_dnssec_zone_find_rrset(
29 		    referrals, ldns_rr_owner(rr), ldns_rr_get_type(rr));
30 		if (rrset) {
31 			for (rrs = rrset->rrs; rrs; rrs = rrs->next)
32 				if (ldns_rr_compare(rr, rrs->rr) == 0)
33 					break;
34 			if (rrs) continue; /* "rr" is present, next! */
35 		}
36 		if (ldns_rr_get_type(rr) == LDNS_RR_TYPE_NS && verbosity != -1)
37 			ldns_rr_print(stdout, rr);
38 		(void) ldns_dnssec_zone_add_rr(referrals, rr);
39 	}
40 }
41 
42 /* Cache all RRs from packet "p" to "referrals" database for lookup later on.
43  * Print the NS RRs that were not already present.
44  */
45 static void add_referrals(ldns_dnssec_zone *referrals, ldns_pkt *p)
46 {
47 	ldns_rr_list *l = ldns_pkt_all_noquestion(p);
48 	if (l) {
49 		add_rr_list_to_referrals(referrals, l);
50 		ldns_rr_list_free(l);
51 	}
52 }
53 
54 /* Equip name-server "res" with the name-servers authoritative for as much
55  * of "name" as possible.  Lookup addresses if needed.
56  */
57 static bool set_nss_for_name(
58     ldns_resolver *res, ldns_dnssec_zone *referrals, ldns_rdf *name,
59     ldns_resolver *local_res, ldns_rr_class c)
60 {
61 	ldns_dnssec_rrsets *nss = NULL;
62 	ldns_dnssec_rrs *nss_rrs;
63 	ldns_dnssec_rrsets *as = NULL;
64 	ldns_dnssec_rrs *as_rrs;
65 	ldns_rdf *lookup = ldns_rdf_clone(name);
66 	ldns_rdf *new_lookup;
67 	ldns_rdf *addr;
68 	ldns_rr_list *addrs;
69 
70 	/* nss will become the rrset of as much of "name" as possible */
71 	for (;;) {
72 		nss = ldns_dnssec_zone_find_rrset(
73 		    referrals, lookup, LDNS_RR_TYPE_NS);
74 		if (nss != NULL) {
75 			ldns_rdf_deep_free(lookup);
76 			break;
77 		}
78 		new_lookup = ldns_dname_left_chop(lookup);
79 		ldns_rdf_deep_free(lookup);
80 		lookup = new_lookup;
81 		if (!lookup) {
82 			error("No referrals for name found");
83 			return false;
84 		}
85 	}
86 
87 	/* remove the old nameserver from the resolver */
88 	while ((addr = ldns_resolver_pop_nameserver(res)))
89 		ldns_rdf_deep_free(addr);
90 
91 	/* Find and add the address records for the rrset as name-servers */
92 	for (nss_rrs = nss->rrs; nss_rrs; nss_rrs = nss_rrs->next) {
93 
94 		if ((as = ldns_dnssec_zone_find_rrset(
95 		    referrals, ldns_rr_rdf(nss_rrs->rr, 0), LDNS_RR_TYPE_A)))
96 			for (as_rrs = as->rrs; as_rrs; as_rrs = as_rrs->next)
97 				(void) ldns_resolver_push_nameserver(
98 				    res, ldns_rr_rdf(as_rrs->rr, 0));
99 
100 		if ((as = ldns_dnssec_zone_find_rrset(
101 		    referrals, ldns_rr_rdf(nss_rrs->rr, 0), LDNS_RR_TYPE_AAAA)))
102 			for (as_rrs = as->rrs; as_rrs; as_rrs = as_rrs->next)
103 				(void) ldns_resolver_push_nameserver(
104 				    res, ldns_rr_rdf(as_rrs->rr, 0));
105 	}
106 	/* Is our resolver equipped with name-servers? Good! We're done */
107 	if (ldns_resolver_nameserver_count(res) > 0)
108 		return true;
109 
110 	/* Lookup addresses with local resolver add add to "referrals" database */
111 	addrs = ldns_rr_list_new();
112 	for (nss_rrs = nss->rrs; nss_rrs; nss_rrs = nss_rrs->next) {
113 		ldns_rr_list *addrs_by_name =
114 		    ldns_get_rr_list_addr_by_name(
115 			local_res, ldns_rr_rdf(nss_rrs->rr, 0), c, 0);
116 		ldns_rr_list_cat(addrs, addrs_by_name);
117 		ldns_rr_list_free(addrs_by_name);
118 	}
119 
120 	if (ldns_rr_list_rr_count(addrs) == 0)
121 		error("Could not find the nameserver ip addr; abort");
122 
123 	else if (ldns_resolver_push_nameserver_rr_list(res, addrs) !=
124 	    LDNS_STATUS_OK)
125 
126 		error("Error adding new nameservers");
127 	else {
128 		ldns_rr_list_deep_free(addrs);
129 		return true;
130 	}
131 	add_rr_list_to_referrals(referrals, addrs);
132 	ldns_rr_list_deep_free(addrs);
133 	return false;
134 }
135 
136 /**
137  * trace down from the root to name
138  */
139 
140 /* same naive method as in drill0.9
141  * We resolve _ALL_ the names, which is of course not needed.
142  * We _do_ use the local resolver to do that, so it still is
143  * fast, but it can be made to run much faster.
144  */
145 void
146 do_trace(ldns_resolver *local_res, ldns_rdf *name, ldns_rr_type t,
147 		ldns_rr_class c)
148 {
149 
150 	static uint8_t zero[1] = { 0 };
151 	static const ldns_rdf root_dname = { 1, LDNS_RDF_TYPE_DNAME, &zero };
152 
153 	ldns_resolver *res = NULL;
154 	ldns_pkt *p = NULL;
155 	ldns_rr_list *final_answer;
156 	ldns_rr_list *new_nss;
157 	ldns_rr_list *cname = NULL;
158 	ldns_rr_list *answers = NULL;
159 	uint16_t loop_count;
160 	ldns_status status;
161 	ldns_dnssec_zone* referrals = NULL;
162 	ldns_rdf *addr;
163 
164 	loop_count = 0;
165 	final_answer = NULL;
166 	res = ldns_resolver_new();
167 
168 	if (!res) {
169                 error("Memory allocation failed");
170 		goto cleanup;
171         }
172 
173 	/* transfer some properties of local_res to res,
174 	 * because they were given on the command line */
175 	ldns_resolver_set_ip6(res,
176 			ldns_resolver_ip6(local_res));
177 	ldns_resolver_set_port(res,
178 			ldns_resolver_port(local_res));
179 	ldns_resolver_set_debug(res,
180 			ldns_resolver_debug(local_res));
181 	ldns_resolver_set_dnssec(res,
182 			ldns_resolver_dnssec(local_res));
183 	ldns_resolver_set_fail(res,
184 			ldns_resolver_fail(local_res));
185 	ldns_resolver_set_usevc(res,
186 			ldns_resolver_usevc(local_res));
187 	ldns_resolver_set_random(res,
188 			ldns_resolver_random(local_res));
189 	ldns_resolver_set_source(res,
190 			ldns_resolver_source(local_res));
191 	ldns_resolver_set_recursive(res, false);
192 
193 	/* setup the root nameserver in the new resolver */
194 	status = ldns_resolver_push_nameserver_rr_list(res, global_dns_root);
195 	if (status != LDNS_STATUS_OK) {
196 		fprintf(stderr, "Error adding root servers to resolver: %s\n", ldns_get_errorstr_by_id(status));
197 		ldns_rr_list_print(stdout, global_dns_root);
198 		goto cleanup;
199 	}
200 
201 	/* this must be a real query to local_res */
202 	status = ldns_resolver_send(&p, res, &root_dname, LDNS_RR_TYPE_NS, c, 0);
203 	/* p can still be NULL */
204 
205 	if (ldns_pkt_empty(p)) {
206 		warning("No root server information received");
207 	}
208 
209 	if (status == LDNS_STATUS_OK) {
210 		if (!ldns_pkt_empty(p)) {
211 			drill_pkt_print(stdout, local_res, p);
212 		}
213 		referrals = ldns_dnssec_zone_new();
214 		add_referrals(referrals, p);
215 	} else {
216 		error("cannot use local resolver");
217 		goto cleanup;
218 	}
219 	if (! set_nss_for_name(res, referrals, name, local_res, c)) {
220 		goto cleanup;
221 	}
222 	ldns_pkt_free(p);
223 	p = NULL;
224 	status = ldns_resolver_send(&p, res, name, t, c, 0);
225 	while(status == LDNS_STATUS_OK &&
226 	      ldns_pkt_reply_type(p) == LDNS_PACKET_REFERRAL) {
227 
228 		if (!p) {
229 			/* some error occurred -- bail out */
230 			goto cleanup;
231 		}
232 		add_referrals(referrals, p);
233 
234 		/* checks itself for verbosity */
235 		drill_pkt_print_footer(stdout, local_res, p);
236 
237 		if (! set_nss_for_name(res, referrals, name, local_res, c)) {
238 			goto cleanup;
239 		}
240 		if (loop_count++ > 20) {
241 			/* unlikely that we are doing anything useful */
242 			error("Looks like we are looping");
243 			goto cleanup;
244 		}
245 		ldns_pkt_free(p);
246 		p = NULL;
247 		status = ldns_resolver_send(&p, res, name, t, c, 0);
248 
249 		/* Exit trace on error */
250 		if (status != LDNS_STATUS_OK)
251 			break;
252 
253 		/* An answer might be the desired answer (and no referral) */
254 		if (ldns_pkt_reply_type(p) != LDNS_PACKET_ANSWER)
255 			continue;
256 
257 		/* Exit trace when the requested type is found */
258 		answers = ldns_pkt_rr_list_by_type(p, t, LDNS_SECTION_ANSWER);
259 		if (answers && ldns_rr_list_rr_count(answers) > 0) {
260 			ldns_rr_list_free(answers);
261 			answers = NULL;
262 			break;
263 		}
264 		ldns_rr_list_free(answers);
265 		answers = NULL;
266 
267 		/* Get the CNAMEs from the answer */
268 		cname = ldns_pkt_rr_list_by_type(
269 		    p, LDNS_RR_TYPE_CNAME, LDNS_SECTION_ANSWER);
270 
271 		/* No CNAME either: exit trace */
272 		if (ldns_rr_list_rr_count(cname) == 0)
273 			break;
274 
275 		/* Print CNAME referral */
276 		ldns_rr_list_print(stdout, cname);
277 
278 		/* restart with the CNAME */
279 		name = ldns_rr_rdf(ldns_rr_list_rr(cname, 0), 0);
280 		ldns_rr_list_free(cname);
281 		cname = NULL;
282 
283 		/* remove the old nameserver from the resolver */
284 		while((addr = ldns_resolver_pop_nameserver(res)))
285 			ldns_rdf_deep_free(addr);
286 
287 		/* Restart trace from the root up */
288 		(void) ldns_resolver_push_nameserver_rr_list(
289 		    res, global_dns_root);
290 
291 		ldns_pkt_free(p);
292 		p = NULL;
293 		status = ldns_resolver_send(&p, res, name, t, c, 0);
294 	}
295 
296 	ldns_pkt_free(p);
297 	p = NULL;
298 	(void) ldns_resolver_send(&p, res, name, t, c, 0);
299 	if (!p) {
300 		goto cleanup;
301 	}
302 	new_nss = ldns_pkt_authority(p);
303 	final_answer = ldns_pkt_answer(p);
304 
305 	if (verbosity != -1) {
306 		ldns_rr_list_print(stdout, final_answer);
307 		ldns_rr_list_print(stdout, new_nss);
308 
309 	}
310 	drill_pkt_print_footer(stdout, local_res, p);
311 cleanup:
312 	if (res) {
313 		while((addr = ldns_resolver_pop_nameserver(res)))
314 			ldns_rdf_deep_free(addr);
315 		ldns_resolver_free(res);
316 	}
317 	if (referrals)
318 		ldns_dnssec_zone_deep_free(referrals);
319 	if (p)
320 		ldns_pkt_free(p);
321 }
322 
323 
324 /**
325  * Chase the given rr to a known and trusted key
326  *
327  * Based on drill 0.9
328  *
329  * the last argument prev_key_list, if not null, and type == DS, then the ds
330  * rr list we have must all be a ds for the keys in this list
331  */
332 #ifdef HAVE_SSL
333 ldns_status
334 do_chase(ldns_resolver *res,
335 	    ldns_rdf *name,
336 	    ldns_rr_type type,
337 	    ldns_rr_class c,
338 	    ldns_rr_list *trusted_keys,
339 	    ldns_pkt *pkt_o,
340 	    uint16_t qflags,
341 	    ldns_rr_list * ATTR_UNUSED(prev_key_list))
342 {
343 	ldns_rr_list *rrset = NULL;
344 	ldns_status result;
345 	ldns_rr *orig_rr = NULL;
346 
347 /*
348 	ldns_rr_list *sigs;
349 	ldns_rr *cur_sig;
350 	uint16_t sig_i;
351 	ldns_rr_list *keys;
352 */
353 	ldns_pkt *pkt;
354 	ldns_status tree_result;
355 	ldns_dnssec_data_chain *chain;
356 	ldns_dnssec_trust_tree *tree;
357 
358 	const ldns_rr_descriptor *descriptor;
359 	descriptor = ldns_rr_descript(type);
360 
361 	ldns_dname2canonical(name);
362 
363 	pkt = ldns_pkt_clone(pkt_o);
364 	if (!name) {
365 		mesg("No name to chase");
366 		ldns_pkt_free(pkt);
367 		return LDNS_STATUS_EMPTY_LABEL;
368 	}
369 	if (verbosity != -1) {
370 		printf(";; Chasing: ");
371 			ldns_rdf_print(stdout, name);
372 			if (descriptor && descriptor->_name) {
373 				printf(" %s\n", descriptor->_name);
374 			} else {
375 				printf(" type %d\n", type);
376 			}
377 	}
378 
379 	if (!trusted_keys || ldns_rr_list_rr_count(trusted_keys) < 1) {
380 		warning("No trusted keys specified");
381 	}
382 
383 	if (pkt) {
384 		rrset = ldns_pkt_rr_list_by_name_and_type(pkt,
385 				name,
386 				type,
387 				LDNS_SECTION_ANSWER
388 				);
389 		if (!rrset) {
390 			/* nothing in answer, try authority */
391 			rrset = ldns_pkt_rr_list_by_name_and_type(pkt,
392 					name,
393 					type,
394 					LDNS_SECTION_AUTHORITY
395 					);
396 		}
397 		/* answer might be a cname, chase that first, then chase
398 		   cname target? (TODO) */
399 		if (!rrset) {
400 			rrset = ldns_pkt_rr_list_by_name_and_type(pkt,
401 					name,
402 					LDNS_RR_TYPE_CNAME,
403 					LDNS_SECTION_ANSWER
404 					);
405 			if (!rrset) {
406 				/* nothing in answer, try authority */
407 				rrset = ldns_pkt_rr_list_by_name_and_type(pkt,
408 						name,
409 						LDNS_RR_TYPE_CNAME,
410 						LDNS_SECTION_AUTHORITY
411 						);
412 			}
413 		}
414 	} else {
415 		/* no packet? */
416 		if (verbosity >= 0) {
417 			fprintf(stderr, "%s", ldns_get_errorstr_by_id(LDNS_STATUS_MEM_ERR));
418 			fprintf(stderr, "\n");
419 		}
420 		return LDNS_STATUS_MEM_ERR;
421 	}
422 
423 	if (!rrset) {
424 		/* not found in original packet, try again */
425 		ldns_pkt_free(pkt);
426 		pkt = NULL;
427 		pkt = ldns_resolver_query(res, name, type, c, qflags);
428 
429 		if (!pkt) {
430 			if (verbosity >= 0) {
431 				fprintf(stderr, "%s", ldns_get_errorstr_by_id(LDNS_STATUS_NETWORK_ERR));
432 				fprintf(stderr, "\n");
433 			}
434 			return LDNS_STATUS_NETWORK_ERR;
435 		}
436 		if (verbosity >= 5) {
437 			ldns_pkt_print(stdout, pkt);
438 		}
439 
440 		rrset =	ldns_pkt_rr_list_by_name_and_type(pkt,
441 				name,
442 				type,
443 				LDNS_SECTION_ANSWER
444 				);
445 	}
446 
447 	orig_rr = ldns_rr_new();
448 
449 /* if the answer had no answer section, we need to construct our own rr (for instance if
450  * the rr qe asked for doesn't exist. This rr will be destroyed when the chain is freed */
451 	if (ldns_pkt_ancount(pkt) < 1) {
452 		ldns_rr_set_type(orig_rr, type);
453 		ldns_rr_set_owner(orig_rr, ldns_rdf_clone(name));
454 
455 		chain = ldns_dnssec_build_data_chain(res, qflags, rrset, pkt, ldns_rr_clone(orig_rr));
456 	} else {
457 		/* chase the first answer */
458 		chain = ldns_dnssec_build_data_chain(res, qflags, rrset, pkt, NULL);
459 	}
460 
461 	if (verbosity >= 4) {
462 		printf("\n\nDNSSEC Data Chain:\n");
463 		ldns_dnssec_data_chain_print(stdout, chain);
464 	}
465 
466 	result = LDNS_STATUS_OK;
467 
468 	tree = ldns_dnssec_derive_trust_tree(chain, NULL);
469 
470 	if (verbosity >= 2) {
471 		printf("\n\nDNSSEC Trust tree:\n");
472 		ldns_dnssec_trust_tree_print(stdout, tree, 0, true);
473 	}
474 
475 	if (ldns_rr_list_rr_count(trusted_keys) > 0) {
476 		tree_result = ldns_dnssec_trust_tree_contains_keys(tree, trusted_keys);
477 
478 		if (tree_result == LDNS_STATUS_DNSSEC_EXISTENCE_DENIED) {
479 			if (verbosity >= 1) {
480 				printf("Existence denied or verifiably insecure\n");
481 			}
482 			result = LDNS_STATUS_OK;
483 		} else if (tree_result != LDNS_STATUS_OK) {
484 			if (verbosity >= 1) {
485 				printf("No trusted keys found in tree: first error was: %s\n", ldns_get_errorstr_by_id(tree_result));
486 			}
487 			result = tree_result;
488 		}
489 
490 	} else {
491 		if (verbosity >= 0) {
492 			printf("You have not provided any trusted keys.\n");
493 		}
494 	}
495 
496 	ldns_rr_free(orig_rr);
497 	ldns_dnssec_trust_tree_free(tree);
498 	ldns_dnssec_data_chain_deep_free(chain);
499 
500 	ldns_rr_list_deep_free(rrset);
501 	ldns_pkt_free(pkt);
502 	/*	ldns_rr_free(orig_rr);*/
503 
504 	return result;
505 }
506 #endif /* HAVE_SSL */
507 
508