xref: /dragonfly/contrib/ldns/drill/chasetrace.c (revision 7733acb50455a11cc2ee36edd926ff0fa3361e9a)
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  */
add_rr_list_to_referrals(ldns_dnssec_zone * referrals,ldns_rr_list * rr_list)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  */
add_referrals(ldns_dnssec_zone * referrals,ldns_pkt * p)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  */
set_nss_for_name(ldns_resolver * res,ldns_dnssec_zone * referrals,ldns_rdf * name,ldns_resolver * local_res,ldns_rr_class c)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
do_trace(ldns_resolver * local_res,ldns_rdf * name,ldns_rr_type t,ldns_rr_class c)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
do_chase(ldns_resolver * res,ldns_rdf * name,ldns_rr_type type,ldns_rr_class c,ldns_rr_list * trusted_keys,ldns_pkt * pkt_o,uint16_t qflags,ldns_rr_list * ATTR_UNUSED (prev_key_list))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