]> CyberLeo.Net >> Repos - FreeBSD/releng/9.3.git/blob - contrib/bind9/lib/export/samples/nsprobe.c
Copy stable/9 to releng/9.3 as part of the 9.3-RELEASE cycle.
[FreeBSD/releng/9.3.git] / contrib / bind9 / lib / export / samples / nsprobe.c
1 /*
2  * Copyright (C) 2009-2014  Internet Systems Consortium, Inc. ("ISC")
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14  * PERFORMANCE OF THIS SOFTWARE.
15  */
16
17 /* $Id$ */
18
19 #include <config.h>
20
21 #include <sys/types.h>
22 #include <sys/socket.h>
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <netdb.h>
29
30 #include <isc/app.h>
31 #include <isc/buffer.h>
32 #include <isc/lib.h>
33 #include <isc/mem.h>
34 #include <isc/socket.h>
35 #include <isc/sockaddr.h>
36 #include <isc/string.h>
37 #include <isc/task.h>
38 #include <isc/timer.h>
39 #include <isc/util.h>
40
41 #include <dns/client.h>
42 #include <dns/fixedname.h>
43 #include <dns/lib.h>
44 #include <dns/message.h>
45 #include <dns/name.h>
46 #include <dns/rdata.h>
47 #include <dns/rdataset.h>
48 #include <dns/rdatastruct.h>
49 #include <dns/rdatatype.h>
50 #include <dns/result.h>
51
52 #define MAX_PROBES 1000
53
54 static dns_client_t *client = NULL;
55 static isc_task_t *probe_task = NULL;
56 static isc_appctx_t *actx = NULL;
57 static isc_mem_t *mctx = NULL;
58 static unsigned int outstanding_probes = 0;
59 const char *cacheserver = "127.0.0.1";
60 static FILE *fp;
61
62 typedef enum {
63         none,
64         exist,
65         nxdomain,
66         othererr,
67         multiplesoa,
68         multiplecname,
69         brokenanswer,
70         lame,
71         timedout,
72         notype,
73         unexpected
74 } query_result_t;
75
76 struct server {
77         ISC_LINK(struct server) link;
78
79         isc_sockaddr_t address;
80         query_result_t result_a;
81         query_result_t result_aaaa;
82 };
83
84 struct probe_ns {
85         ISC_LINK(struct probe_ns) link;
86
87         dns_fixedname_t fixedname;
88         dns_name_t *name;
89         struct server *current_server;
90         ISC_LIST(struct server) servers;
91 };
92
93 struct probe_trans {
94         isc_boolean_t inuse;
95         char *domain;
96         dns_fixedname_t fixedname;
97         dns_name_t *qname;
98         const char **qlabel;
99         isc_boolean_t qname_found;
100         dns_clientrestrans_t *resid;
101         dns_message_t *qmessage;
102         dns_message_t *rmessage;
103         dns_clientreqtrans_t *reqid;
104
105         /* NS list */
106         struct probe_ns *current_ns;
107         ISC_LIST(struct probe_ns) nslist;
108 };
109
110 struct lcl_stat {
111         unsigned long valid;
112         unsigned long ignore;
113         unsigned long nxdomain;
114         unsigned long othererr;
115         unsigned long multiplesoa;
116         unsigned long multiplecname;
117         unsigned long brokenanswer;
118         unsigned long lame;
119         unsigned long unknown;
120 } server_stat, domain_stat;
121
122 static unsigned long number_of_domains = 0;
123 static unsigned long number_of_servers = 0;
124 static unsigned long multiple_error_domains = 0;
125 static isc_boolean_t debug_mode = ISC_FALSE;
126 static int verbose_level = 0;
127 static const char *qlabels[] = {"www.", "ftp.", NULL};
128 static struct probe_trans probes[MAX_PROBES];
129
130 static isc_result_t probe_domain(struct probe_trans *trans);
131 static void reset_probe(struct probe_trans *trans);
132 static isc_result_t fetch_nsaddress(struct probe_trans *trans);
133 static isc_result_t probe_name(struct probe_trans *trans,
134                                dns_rdatatype_t type);
135
136 /* Dump an rdataset for debug */
137 static isc_result_t
138 print_rdataset(dns_rdataset_t *rdataset, dns_name_t *owner) {
139         isc_buffer_t target;
140         isc_result_t result;
141         isc_region_t r;
142         char t[4096];
143
144         if (!debug_mode)
145                 return (ISC_R_SUCCESS);
146
147         isc_buffer_init(&target, t, sizeof(t));
148
149         if (!dns_rdataset_isassociated(rdataset))
150                 return (ISC_R_SUCCESS);
151         result = dns_rdataset_totext(rdataset, owner, ISC_FALSE, ISC_FALSE,
152                                      &target);
153         if (result != ISC_R_SUCCESS)
154                 return (result);
155         isc_buffer_usedregion(&target, &r);
156         printf("%.*s", (int)r.length, (char *)r.base);
157
158         return (ISC_R_SUCCESS);
159 }
160
161 static isc_result_t
162 print_name(dns_name_t *name) {
163         isc_result_t result;
164         isc_buffer_t target;
165         isc_region_t r;
166         char t[4096];
167
168         isc_buffer_init(&target, t, sizeof(t));
169         result = dns_name_totext(name, ISC_TRUE, &target);
170         if (result == ISC_R_SUCCESS) {
171                 isc_buffer_usedregion(&target, &r);
172                 printf("%.*s", (int)r.length, (char *)r.base);
173         } else
174                 printf("(invalid name)");
175
176         return (result);
177 }
178
179 static isc_result_t
180 print_address(FILE *fp, isc_sockaddr_t *addr) {
181         char buf[NI_MAXHOST];
182
183         if (getnameinfo(&addr->type.sa, addr->length, buf, sizeof(buf),
184                         NULL, 0, NI_NUMERICHOST) == 0) {
185                 fprintf(fp, "%s", buf);
186         } else {
187                 fprintf(fp, "(invalid address)");
188         }
189
190         return (ISC_R_SUCCESS);
191 }
192
193 static void
194 ctxs_destroy(isc_mem_t **mctxp, isc_appctx_t **actxp,
195              isc_taskmgr_t **taskmgrp, isc_socketmgr_t **socketmgrp,
196              isc_timermgr_t **timermgrp)
197 {
198         if (*taskmgrp != NULL)
199                 isc_taskmgr_destroy(taskmgrp);
200
201         if (*timermgrp != NULL)
202                 isc_timermgr_destroy(timermgrp);
203
204         if (*socketmgrp != NULL)
205                 isc_socketmgr_destroy(socketmgrp);
206
207         if (*actxp != NULL)
208                 isc_appctx_destroy(actxp);
209
210         if (*mctxp != NULL)
211                 isc_mem_destroy(mctxp);
212 }
213
214 static isc_result_t
215 ctxs_init(isc_mem_t **mctxp, isc_appctx_t **actxp,
216           isc_taskmgr_t **taskmgrp, isc_socketmgr_t **socketmgrp,
217           isc_timermgr_t **timermgrp)
218 {
219         isc_result_t result;
220
221         result = isc_mem_create(0, 0, mctxp);
222         if (result != ISC_R_SUCCESS)
223                 goto fail;
224
225         result = isc_appctx_create(*mctxp, actxp);
226         if (result != ISC_R_SUCCESS)
227                 goto fail;
228
229         result = isc_taskmgr_createinctx(*mctxp, *actxp, 1, 0, taskmgrp);
230         if (result != ISC_R_SUCCESS)
231                 goto fail;
232
233         result = isc_socketmgr_createinctx(*mctxp, *actxp, socketmgrp);
234         if (result != ISC_R_SUCCESS)
235                 goto fail;
236
237         result = isc_timermgr_createinctx(*mctxp, *actxp, timermgrp);
238         if (result != ISC_R_SUCCESS)
239                 goto fail;
240
241         return (ISC_R_SUCCESS);
242
243  fail:
244         ctxs_destroy(mctxp, actxp, taskmgrp, socketmgrp, timermgrp);
245
246         return (result);
247 }
248
249 /*
250  * Common routine to make query data
251  */
252 static isc_result_t
253 make_querymessage(dns_message_t *message, dns_name_t *qname0,
254                   dns_rdatatype_t rdtype)
255 {
256         dns_name_t *qname = NULL;
257         dns_rdataset_t *qrdataset = NULL;
258         isc_result_t result;
259
260         message->opcode = dns_opcode_query;
261         message->rdclass = dns_rdataclass_in;
262
263         result = dns_message_gettempname(message, &qname);
264         if (result != ISC_R_SUCCESS)
265                 goto cleanup;
266
267         result = dns_message_gettemprdataset(message, &qrdataset);
268         if (result != ISC_R_SUCCESS)
269                 goto cleanup;
270
271         dns_name_init(qname, NULL);
272         dns_name_clone(qname0, qname);
273         dns_rdataset_init(qrdataset);
274         dns_rdataset_makequestion(qrdataset, message->rdclass, rdtype);
275         ISC_LIST_APPEND(qname->list, qrdataset, link);
276         dns_message_addname(message, qname, DNS_SECTION_QUESTION);
277
278         return (ISC_R_SUCCESS);
279
280  cleanup:
281         if (qname != NULL)
282                 dns_message_puttempname(message, &qname);
283         if (qrdataset != NULL)
284                 dns_message_puttemprdataset(message, &qrdataset);
285         return (result);
286 }
287
288 /*
289  * Update statistics
290  */
291 static inline void
292 increment_entry(unsigned long *entryp) {
293         (*entryp)++;
294         INSIST(*entryp != 0U);  /* check overflow */
295 }
296
297 static void
298 update_stat(struct probe_trans *trans) {
299         struct probe_ns *pns;
300         struct server *server;
301         struct lcl_stat local_stat;
302         unsigned int err_count = 0;
303         const char *stattype;
304
305         increment_entry(&number_of_domains);
306         memset(&local_stat, 0, sizeof(local_stat));
307
308         /* Update per sever statistics */
309         for (pns = ISC_LIST_HEAD(trans->nslist); pns != NULL;
310              pns = ISC_LIST_NEXT(pns, link)) {
311                 for (server = ISC_LIST_HEAD(pns->servers); server != NULL;
312                      server = ISC_LIST_NEXT(server, link)) {
313                         increment_entry(&number_of_servers);
314
315                         if (server->result_aaaa == exist ||
316                             server->result_aaaa == notype) {
317                                 /*
318                                  * Don't care about the result of A query if
319                                  * the answer to AAAA query was expected.
320                                  */
321                                 stattype = "valid";
322                                 increment_entry(&server_stat.valid);
323                                 increment_entry(&local_stat.valid);
324                         } else if (server->result_a == exist) {
325                                 switch (server->result_aaaa) {
326                                 case exist:
327                                 case notype:
328                                         stattype = "valid";
329                                         increment_entry(&server_stat.valid);
330                                         increment_entry(&local_stat.valid);
331                                         break;
332                                 case timedout:
333                                         stattype = "ignore";
334                                         increment_entry(&server_stat.ignore);
335                                         increment_entry(&local_stat.ignore);
336                                         break;
337                                 case nxdomain:
338                                         stattype = "nxdomain";
339                                         increment_entry(&server_stat.nxdomain);
340                                         increment_entry(&local_stat.nxdomain);
341                                         break;
342                                 case othererr:
343                                         stattype = "othererr";
344                                         increment_entry(&server_stat.othererr);
345                                         increment_entry(&local_stat.othererr);
346                                         break;
347                                 case multiplesoa:
348                                         stattype = "multiplesoa";
349                                         increment_entry(&server_stat.multiplesoa);
350                                         increment_entry(&local_stat.multiplesoa);
351                                         break;
352                                 case multiplecname:
353                                         stattype = "multiplecname";
354                                         increment_entry(&server_stat.multiplecname);
355                                         increment_entry(&local_stat.multiplecname);
356                                         break;
357                                 case brokenanswer:
358                                         stattype = "brokenanswer";
359                                         increment_entry(&server_stat.brokenanswer);
360                                         increment_entry(&local_stat.brokenanswer);
361                                         break;
362                                 case lame:
363                                         stattype = "lame";
364                                         increment_entry(&server_stat.lame);
365                                         increment_entry(&local_stat.lame);
366                                         break;
367                                 default:
368                                         stattype = "unknown";
369                                         increment_entry(&server_stat.unknown);
370                                         increment_entry(&local_stat.unknown);
371                                         break;
372                                 }
373                         } else {
374                                 stattype = "unknown";
375                                 increment_entry(&server_stat.unknown);
376                                 increment_entry(&local_stat.unknown);
377                         }
378
379                         if (verbose_level > 1 ||
380                             (verbose_level == 1 &&
381                              strcmp(stattype, "valid") != 0 &&
382                              strcmp(stattype, "unknown") != 0)) {
383                                 print_name(pns->name);
384                                 putchar('(');
385                                 print_address(stdout, &server->address);
386                                 printf(") for %s:%s\n", trans->domain,
387                                        stattype);
388                         }
389                 }
390         }
391
392         /* Update per domain statistics */
393         if (local_stat.ignore > 0U) {
394                 if (verbose_level > 0)
395                         printf("%s:ignore\n", trans->domain);
396                 increment_entry(&domain_stat.ignore);
397                 err_count++;
398         }
399         if (local_stat.nxdomain > 0U) {
400                 if (verbose_level > 0)
401                         printf("%s:nxdomain\n", trans->domain);
402                 increment_entry(&domain_stat.nxdomain);
403                 err_count++;
404         }
405         if (local_stat.othererr > 0U) {
406                 if (verbose_level > 0)
407                         printf("%s:othererr\n", trans->domain);
408                 increment_entry(&domain_stat.othererr);
409                 err_count++;
410         }
411         if (local_stat.multiplesoa > 0U) {
412                 if (verbose_level > 0)
413                         printf("%s:multiplesoa\n", trans->domain);
414                 increment_entry(&domain_stat.multiplesoa);
415                 err_count++;
416         }
417         if (local_stat.multiplecname > 0U) {
418                 if (verbose_level > 0)
419                         printf("%s:multiplecname\n", trans->domain);
420                 increment_entry(&domain_stat.multiplecname);
421                 err_count++;
422         }
423         if (local_stat.brokenanswer > 0U) {
424                 if (verbose_level > 0)
425                         printf("%s:brokenanswer\n", trans->domain);
426                 increment_entry(&domain_stat.brokenanswer);
427                 err_count++;
428         }
429         if (local_stat.lame > 0U) {
430                 if (verbose_level > 0)
431                         printf("%s:lame\n", trans->domain);
432                 increment_entry(&domain_stat.lame);
433                 err_count++;
434         }
435
436         if (err_count > 1U)
437                 increment_entry(&multiple_error_domains);
438
439         /*
440          * We regard the domain as valid if and only if no authoritative server
441          * has a problem and at least one server is known to be valid.
442          */
443         if (local_stat.valid > 0U && err_count == 0U) {
444                 if (verbose_level > 1)
445                         printf("%s:valid\n", trans->domain);
446                 increment_entry(&domain_stat.valid);
447         }
448
449         /*
450          * If the domain has no available server or all servers have the
451          * 'unknown' result, the domain's result is also regarded as unknown.
452          */
453         if (local_stat.valid == 0U && err_count == 0U) {
454                 if (verbose_level > 1)
455                         printf("%s:unknown\n", trans->domain);
456                 increment_entry(&domain_stat.unknown);
457         }
458 }
459
460 /*
461  * Search for an existent name with an A RR
462  */
463
464 static isc_result_t
465 set_nextqname(struct probe_trans *trans) {
466         isc_result_t result;
467         size_t domainlen;
468         isc_buffer_t b;
469         char buf[4096]; /* XXX ad-hoc constant, but should be enough */
470
471         if (*trans->qlabel == NULL)
472                 return (ISC_R_NOMORE);
473
474         result = isc_string_copy(buf, sizeof(buf), *trans->qlabel);
475         if (result != ISC_R_SUCCESS)
476                 return (result);
477         result = isc_string_append(buf, sizeof(buf), trans->domain);
478         if (result != ISC_R_SUCCESS)
479                 return (result);
480
481         domainlen = strlen(buf);
482         isc_buffer_init(&b, buf, domainlen);
483         isc_buffer_add(&b, domainlen);
484         dns_fixedname_init(&trans->fixedname);
485         trans->qname = dns_fixedname_name(&trans->fixedname);
486         result = dns_name_fromtext(trans->qname, &b, dns_rootname,
487                                    0, NULL);
488
489         trans->qlabel++;
490
491         return (result);
492 }
493
494 static void
495 request_done(isc_task_t *task, isc_event_t *event) {
496         struct probe_trans *trans = event->ev_arg;
497         dns_clientreqevent_t *rev = (dns_clientreqevent_t *)event;
498         dns_message_t *rmessage;
499         struct probe_ns *pns;
500         struct server *server;
501         isc_result_t result;
502         query_result_t *resultp;
503         dns_name_t *name;
504         dns_rdataset_t *rdataset;
505         dns_rdatatype_t type;
506
507         REQUIRE(task == probe_task);
508         REQUIRE(trans != NULL && trans->inuse == ISC_TRUE);
509         rmessage = rev->rmessage;
510         REQUIRE(rmessage == trans->rmessage);
511         INSIST(outstanding_probes > 0);
512
513         server = trans->current_ns->current_server;
514         INSIST(server != NULL);
515
516         if (server->result_a == none) {
517                 type = dns_rdatatype_a;
518                 resultp = &server->result_a;
519         } else {
520                 resultp = &server->result_aaaa;
521                 type = dns_rdatatype_aaaa;
522         }
523
524         if (rev->result == ISC_R_SUCCESS) {
525                 if ((rmessage->flags & DNS_MESSAGEFLAG_AA) == 0)
526                         *resultp = lame;
527                 else if (rmessage->rcode == dns_rcode_nxdomain)
528                         *resultp = nxdomain;
529                 else if (rmessage->rcode != dns_rcode_noerror)
530                         *resultp = othererr;
531                 else if (rmessage->counts[DNS_SECTION_ANSWER] == 0) {
532                         /* no error but empty answer */
533                         *resultp = notype;
534                 } else {
535                         result = dns_message_firstname(rmessage,
536                                                        DNS_SECTION_ANSWER);
537                         while (result == ISC_R_SUCCESS) {
538                                 name = NULL;
539                                 dns_message_currentname(rmessage,
540                                                         DNS_SECTION_ANSWER,
541                                                         &name);
542                                 for (rdataset = ISC_LIST_HEAD(name->list);
543                                      rdataset != NULL;
544                                      rdataset = ISC_LIST_NEXT(rdataset,
545                                                               link)) {
546                                         (void)print_rdataset(rdataset, name);
547
548                                         if (rdataset->type ==
549                                             dns_rdatatype_cname ||
550                                             rdataset->type ==
551                                             dns_rdatatype_dname) {
552                                                 /* Should chase the chain? */
553                                                 *resultp = exist;
554                                                 goto found;
555                                         } else if (rdataset->type == type) {
556                                                 *resultp = exist;
557                                                 goto found;
558                                         }
559                                 }
560                                 result = dns_message_nextname(rmessage,
561                                                               DNS_SECTION_ANSWER);
562                         }
563
564                         /*
565                          * Something unexpected happened: the response
566                          * contained a non-empty authoritative answer, but we
567                          * could not find an expected result.
568                          */
569                         *resultp = unexpected;
570                 }
571         } else if (rev->result == DNS_R_RECOVERABLE ||
572                    rev->result == DNS_R_BADLABELTYPE) {
573                 /* Broken response.  Try identifying known cases. */
574                 *resultp = brokenanswer;
575
576                 if (rmessage->counts[DNS_SECTION_ANSWER] > 0) {
577                         result = dns_message_firstname(rmessage,
578                                                        DNS_SECTION_ANSWER);
579                         while (result == ISC_R_SUCCESS) {
580                                 /*
581                                  * Check to see if the response has multiple
582                                  * CNAME RRs.  Update the result code if so.
583                                  */
584                                 name = NULL;
585                                 dns_message_currentname(rmessage,
586                                                         DNS_SECTION_ANSWER,
587                                                         &name);
588                                 for (rdataset = ISC_LIST_HEAD(name->list);
589                                      rdataset != NULL;
590                                      rdataset = ISC_LIST_NEXT(rdataset,
591                                                               link)) {
592                                         if (rdataset->type ==
593                                             dns_rdatatype_cname &&
594                                             dns_rdataset_count(rdataset) > 1) {
595                                                 *resultp = multiplecname;
596                                                 goto found;
597                                         }
598                                 }
599                                 result = dns_message_nextname(rmessage,
600                                                               DNS_SECTION_ANSWER);
601                         }
602                 }
603
604                 if (rmessage->counts[DNS_SECTION_AUTHORITY] > 0) {
605                         result = dns_message_firstname(rmessage,
606                                                        DNS_SECTION_AUTHORITY);
607                         while (result == ISC_R_SUCCESS) {
608                                 /*
609                                  * Check to see if the response has multiple
610                                  * SOA RRs.  Update the result code if so.
611                                  */
612                                 name = NULL;
613                                 dns_message_currentname(rmessage,
614                                                         DNS_SECTION_AUTHORITY,
615                                                         &name);
616                                 for (rdataset = ISC_LIST_HEAD(name->list);
617                                      rdataset != NULL;
618                                      rdataset = ISC_LIST_NEXT(rdataset,
619                                                               link)) {
620                                         if (rdataset->type ==
621                                             dns_rdatatype_soa &&
622                                             dns_rdataset_count(rdataset) > 1) {
623                                                 *resultp = multiplesoa;
624                                                 goto found;
625                                         }
626                                 }
627                                 result = dns_message_nextname(rmessage,
628                                                               DNS_SECTION_AUTHORITY);
629                         }
630                 }
631         } else if (rev->result == ISC_R_TIMEDOUT)
632                 *resultp = timedout;
633         else {
634                 fprintf(stderr, "unexpected result: %d (domain=%s, server=",
635                         rev->result, trans->domain);
636                 print_address(stderr, &server->address);
637                 fputc('\n', stderr);
638                 *resultp = unexpected;
639         }
640
641  found:
642         INSIST(*resultp != none);
643         if (type == dns_rdatatype_a && *resultp == exist)
644                 trans->qname_found = ISC_TRUE;
645
646         dns_client_destroyreqtrans(&trans->reqid);
647         isc_event_free(&event);
648         dns_message_reset(trans->rmessage, DNS_MESSAGE_INTENTPARSE);
649
650         result = probe_name(trans, type);
651         if (result == ISC_R_NOMORE) {
652                 /* We've tried all addresses of all servers. */
653                 if (type == dns_rdatatype_a && trans->qname_found) {
654                         /*
655                          * If we've explored A RRs and found an existent
656                          * record, we can move to AAAA.
657                          */
658                         trans->current_ns = ISC_LIST_HEAD(trans->nslist);
659                         probe_name(trans, dns_rdatatype_aaaa);
660                         result = ISC_R_SUCCESS;
661                 } else if (type == dns_rdatatype_a) {
662                         /*
663                          * No server provided an existent A RR of this name.
664                          * Try next label.
665                          */
666                         dns_fixedname_invalidate(&trans->fixedname);
667                         trans->qname = NULL;
668                         result = set_nextqname(trans);
669                         if (result == ISC_R_SUCCESS) {
670                                 trans->current_ns =
671                                         ISC_LIST_HEAD(trans->nslist);
672                                 for (pns = trans->current_ns; pns != NULL;
673                                      pns = ISC_LIST_NEXT(pns, link)) {
674                                         for (server = ISC_LIST_HEAD(pns->servers);
675                                              server != NULL;
676                                              server = ISC_LIST_NEXT(server,
677                                                                     link)) {
678                                                 INSIST(server->result_aaaa ==
679                                                        none);
680                                                 server->result_a = none;
681                                         }
682                                 }
683                                 result = probe_name(trans, dns_rdatatype_a);
684                         }
685                 }
686                 if (result != ISC_R_SUCCESS) {
687                         /*
688                          * We've explored AAAA RRs or failed to find a valid
689                          * query label.  Wrap up the result and move to the
690                          * next domain.
691                          */
692                         reset_probe(trans);
693                 }
694         } else if (result != ISC_R_SUCCESS)
695                 reset_probe(trans); /* XXX */
696 }
697
698 static isc_result_t
699 probe_name(struct probe_trans *trans, dns_rdatatype_t type) {
700         isc_result_t result;
701         struct probe_ns *pns;
702         struct server *server;
703
704         REQUIRE(trans->reqid == NULL);
705         REQUIRE(type == dns_rdatatype_a || type == dns_rdatatype_aaaa);
706
707         for (pns = trans->current_ns; pns != NULL;
708              pns = ISC_LIST_NEXT(pns, link)) {
709                 for (server = ISC_LIST_HEAD(pns->servers); server != NULL;
710                      server = ISC_LIST_NEXT(server, link)) {
711                         if ((type == dns_rdatatype_a &&
712                              server->result_a == none) ||
713                             (type == dns_rdatatype_aaaa &&
714                              server->result_aaaa == none)) {
715                                 pns->current_server = server;
716                                 goto found;
717                         }
718                 }
719         }
720
721  found:
722         trans->current_ns = pns;
723         if (pns == NULL)
724                 return (ISC_R_NOMORE);
725
726         INSIST(pns->current_server != NULL);
727         dns_message_reset(trans->qmessage, DNS_MESSAGE_INTENTRENDER);
728         result = make_querymessage(trans->qmessage, trans->qname, type);
729         if (result != ISC_R_SUCCESS)
730                 return (result);
731         result = dns_client_startrequest(client, trans->qmessage,
732                                          trans->rmessage,
733                                          &pns->current_server->address,
734                                          0, DNS_MESSAGEPARSE_BESTEFFORT,
735                                          NULL, 120, 0, 4,
736                                          probe_task, request_done, trans,
737                                          &trans->reqid);
738
739         return (result);
740 }
741
742 /*
743  * Get IP addresses of NSes
744  */
745
746 static void
747 resolve_nsaddress(isc_task_t *task, isc_event_t *event) {
748         struct probe_trans *trans = event->ev_arg;
749         dns_clientresevent_t *rev = (dns_clientresevent_t *)event;
750         dns_name_t *name;
751         dns_rdataset_t *rdataset;
752         dns_rdata_t rdata = DNS_RDATA_INIT;
753         struct probe_ns *pns = trans->current_ns;
754         isc_result_t result;
755
756         REQUIRE(task == probe_task);
757         REQUIRE(trans->inuse == ISC_TRUE);
758         REQUIRE(pns != NULL);
759         INSIST(outstanding_probes > 0);
760
761         for (name = ISC_LIST_HEAD(rev->answerlist); name != NULL;
762              name = ISC_LIST_NEXT(name, link)) {
763                 for (rdataset = ISC_LIST_HEAD(name->list);
764                      rdataset != NULL;
765                      rdataset = ISC_LIST_NEXT(rdataset, link)) {
766                         (void)print_rdataset(rdataset, name);
767
768                         if (rdataset->type != dns_rdatatype_a)
769                                 continue;
770
771                         for (result = dns_rdataset_first(rdataset);
772                              result == ISC_R_SUCCESS;
773                              result = dns_rdataset_next(rdataset)) {
774                                 dns_rdata_in_a_t rdata_a;
775                                 struct server *server;
776
777                                 dns_rdataset_current(rdataset, &rdata);
778                                 result = dns_rdata_tostruct(&rdata, &rdata_a,
779                                                             NULL);
780                                 if (result != ISC_R_SUCCESS)
781                                         continue;
782
783                                 server = isc_mem_get(mctx, sizeof(*server));
784                                 if (server == NULL) {
785                                         fprintf(stderr, "resolve_nsaddress: "
786                                                 "mem_get failed");
787                                         result = ISC_R_NOMEMORY;
788                                         POST(result);
789                                         goto cleanup;
790                                 }
791                                 isc_sockaddr_fromin(&server->address,
792                                                     &rdata_a.in_addr, 53);
793                                 ISC_LINK_INIT(server, link);
794                                 server->result_a = none;
795                                 server->result_aaaa = none;
796                                 ISC_LIST_APPEND(pns->servers, server, link);
797                         }
798                 }
799         }
800
801  cleanup:
802         dns_client_freeresanswer(client, &rev->answerlist);
803         dns_client_destroyrestrans(&trans->resid);
804         isc_event_free(&event);
805
806  next_ns:
807         trans->current_ns = ISC_LIST_NEXT(pns, link);
808         if (trans->current_ns == NULL) {
809                 trans->current_ns = ISC_LIST_HEAD(trans->nslist);
810                 dns_fixedname_invalidate(&trans->fixedname);
811                 trans->qname = NULL;
812                 result = set_nextqname(trans);
813                 if (result == ISC_R_SUCCESS)
814                          result = probe_name(trans, dns_rdatatype_a);
815         } else {
816                 result = fetch_nsaddress(trans);
817                 if (result != ISC_R_SUCCESS)
818                         goto next_ns; /* XXX: this is unlikely to succeed */
819         }
820
821         if (result != ISC_R_SUCCESS)
822                 reset_probe(trans);
823 }
824
825 static isc_result_t
826 fetch_nsaddress(struct probe_trans *trans) {
827         struct probe_ns *pns;
828
829         pns = trans->current_ns;
830         REQUIRE(pns != NULL);
831
832         return (dns_client_startresolve(client, pns->name, dns_rdataclass_in,
833                                         dns_rdatatype_a, 0, probe_task,
834                                         resolve_nsaddress, trans,
835                                         &trans->resid));
836 }
837
838 /*
839  * Get NS RRset for a given domain
840  */
841
842 static void
843 reset_probe(struct probe_trans *trans) {
844         struct probe_ns *pns;
845         struct server *server;
846         isc_result_t result;
847
848         REQUIRE(trans->resid == NULL);
849         REQUIRE(trans->reqid == NULL);
850
851         update_stat(trans);
852
853         dns_message_reset(trans->qmessage, DNS_MESSAGE_INTENTRENDER);
854         dns_message_reset(trans->rmessage, DNS_MESSAGE_INTENTPARSE);
855
856         trans->inuse = ISC_FALSE;
857         if (trans->domain != NULL)
858                 isc_mem_free(mctx, trans->domain);
859         trans->domain = NULL;
860         if (trans->qname != NULL)
861                 dns_fixedname_invalidate(&trans->fixedname);
862         trans->qname = NULL;
863         trans->qlabel = qlabels;
864         trans->qname_found = ISC_FALSE;
865         trans->current_ns = NULL;
866
867         while ((pns = ISC_LIST_HEAD(trans->nslist)) != NULL) {
868                 ISC_LIST_UNLINK(trans->nslist, pns, link);
869                 while ((server = ISC_LIST_HEAD(pns->servers)) != NULL) {
870                         ISC_LIST_UNLINK(pns->servers, server, link);
871                         isc_mem_put(mctx, server, sizeof(*server));
872                 }
873                 isc_mem_put(mctx, pns, sizeof(*pns));
874         }
875
876         outstanding_probes--;
877
878         result = probe_domain(trans);
879         if (result == ISC_R_NOMORE && outstanding_probes == 0)
880                 isc_app_ctxshutdown(actx);
881 }
882
883 static void
884 resolve_ns(isc_task_t *task, isc_event_t *event) {
885         struct probe_trans *trans = event->ev_arg;
886         dns_clientresevent_t *rev = (dns_clientresevent_t *)event;
887         dns_name_t *name;
888         dns_rdataset_t *rdataset;
889         isc_result_t result = ISC_R_SUCCESS;
890         dns_rdata_t rdata = DNS_RDATA_INIT;
891         struct probe_ns *pns;
892
893         REQUIRE(task == probe_task);
894         REQUIRE(trans->inuse == ISC_TRUE);
895         INSIST(outstanding_probes > 0);
896
897         for (name = ISC_LIST_HEAD(rev->answerlist); name != NULL;
898              name = ISC_LIST_NEXT(name, link)) {
899                 for (rdataset = ISC_LIST_HEAD(name->list);
900                      rdataset != NULL;
901                      rdataset = ISC_LIST_NEXT(rdataset, link)) {
902                         (void)print_rdataset(rdataset, name);
903
904                         if (rdataset->type != dns_rdatatype_ns)
905                                 continue;
906
907                         for (result = dns_rdataset_first(rdataset);
908                              result == ISC_R_SUCCESS;
909                              result = dns_rdataset_next(rdataset)) {
910                                 dns_rdata_ns_t ns;
911
912                                 dns_rdataset_current(rdataset, &rdata);
913                                 /*
914                                  * Extract the name from the NS record.
915                                  */
916                                 result = dns_rdata_tostruct(&rdata, &ns, NULL);
917                                 if (result != ISC_R_SUCCESS)
918                                         continue;
919
920                                 pns = isc_mem_get(mctx, sizeof(*pns));
921                                 if (pns == NULL) {
922                                         fprintf(stderr,
923                                                 "resolve_ns: mem_get failed");
924                                         result = ISC_R_NOMEMORY;
925                                         POST(result);
926                                         /*
927                                          * XXX: should we continue with the
928                                          * available servers anyway?
929                                          */
930                                         goto cleanup;
931                                 }
932
933                                 dns_fixedname_init(&pns->fixedname);
934                                 pns->name =
935                                         dns_fixedname_name(&pns->fixedname);
936                                 ISC_LINK_INIT(pns, link);
937                                 ISC_LIST_APPEND(trans->nslist, pns, link);
938                                 ISC_LIST_INIT(pns->servers);
939
940                                 dns_name_copy(&ns.name, pns->name, NULL);
941                                 dns_rdata_reset(&rdata);
942                                 dns_rdata_freestruct(&ns);
943                         }
944                 }
945         }
946
947  cleanup:
948         dns_client_freeresanswer(client, &rev->answerlist);
949         dns_client_destroyrestrans(&trans->resid);
950         isc_event_free(&event);
951
952         if (!ISC_LIST_EMPTY(trans->nslist)) {
953                 /* Go get addresses of NSes */
954                 trans->current_ns = ISC_LIST_HEAD(trans->nslist);
955                 result = fetch_nsaddress(trans);
956         } else
957                 result = ISC_R_FAILURE;
958
959         if (result == ISC_R_SUCCESS)
960                 return;
961
962         reset_probe(trans);
963 }
964
965 static isc_result_t
966 probe_domain(struct probe_trans *trans) {
967         isc_result_t result;
968         size_t domainlen;
969         isc_buffer_t b;
970         char buf[4096]; /* XXX ad hoc constant, but should be enough */
971         char *cp;
972
973         REQUIRE(trans != NULL);
974         REQUIRE(trans->inuse == ISC_FALSE);
975         REQUIRE(outstanding_probes < MAX_PROBES);
976
977         /* Construct domain */
978         cp = fgets(buf, sizeof(buf), fp);
979         if (cp == NULL)
980                 return (ISC_R_NOMORE);
981         if ((cp = strchr(buf, '\n')) != NULL) /* zap NL if any */
982                 *cp = '\0';
983         trans->domain = isc_mem_strdup(mctx, buf);
984         if (trans->domain == NULL) {
985                 fprintf(stderr,
986                         "failed to allocate memory for domain: %s", cp);
987                 return (ISC_R_NOMEMORY);
988         }
989
990         /* Start getting NS for the domain */
991         domainlen = strlen(buf);
992         isc_buffer_init(&b, buf, domainlen);
993         isc_buffer_add(&b, domainlen);
994         dns_fixedname_init(&trans->fixedname);
995         trans->qname = dns_fixedname_name(&trans->fixedname);
996         result = dns_name_fromtext(trans->qname, &b, dns_rootname, 0, NULL);
997         if (result != ISC_R_SUCCESS)
998                 goto cleanup;
999         result = dns_client_startresolve(client, trans->qname,
1000                                          dns_rdataclass_in, dns_rdatatype_ns,
1001                                          0, probe_task, resolve_ns, trans,
1002                                          &trans->resid);
1003         if (result != ISC_R_SUCCESS)
1004                 goto cleanup;
1005
1006         trans->inuse = ISC_TRUE;
1007         outstanding_probes++;
1008
1009         return (ISC_R_SUCCESS);
1010
1011  cleanup:
1012         isc_mem_free(mctx, trans->domain);
1013         dns_fixedname_invalidate(&trans->fixedname);
1014
1015         return (result);
1016 }
1017
1018 ISC_PLATFORM_NORETURN_PRE static void
1019 usage(void) ISC_PLATFORM_NORETURN_POST;
1020
1021 static void
1022 usage(void) {
1023         fprintf(stderr, "usage: nsprobe [-d] [-v [-v...]] [-c cache_address] "
1024                 "[input_file]\n");
1025
1026         exit(1);
1027 }
1028
1029 int
1030 main(int argc, char *argv[]) {
1031         int i, ch, error;
1032         struct addrinfo hints, *res;
1033         isc_result_t result;
1034         isc_sockaddr_t sa;
1035         isc_sockaddrlist_t servers;
1036         isc_taskmgr_t *taskmgr = NULL;
1037         isc_socketmgr_t *socketmgr = NULL;
1038         isc_timermgr_t *timermgr = NULL;
1039
1040         while ((ch = getopt(argc, argv, "c:dhv")) != -1) {
1041                 switch (ch) {
1042                 case 'c':
1043                         cacheserver = optarg;
1044                         break;
1045                 case 'd':
1046                         debug_mode = ISC_TRUE;
1047                         break;
1048                 case 'h':
1049                         usage();
1050                         break;
1051                 case 'v':
1052                         verbose_level++;
1053                         break;
1054                 default:
1055                         usage();
1056                         break;
1057                 }
1058         }
1059
1060         argc -= optind;
1061         argv += optind;
1062
1063         /* Common set up */
1064         isc_lib_register();
1065         result = dns_lib_init();
1066         if (result != ISC_R_SUCCESS) {
1067                 fprintf(stderr, "dns_lib_init failed: %d\n", result);
1068                 exit(1);
1069         }
1070
1071         result = ctxs_init(&mctx, &actx, &taskmgr, &socketmgr,
1072                            &timermgr);
1073         if (result != ISC_R_SUCCESS) {
1074                 fprintf(stderr, "ctx create failed: %d\n", result);
1075                 exit(1);
1076         }
1077
1078         isc_app_ctxstart(actx);
1079
1080         result = dns_client_createx(mctx, actx, taskmgr, socketmgr,
1081                                     timermgr, 0, &client);
1082         if (result != ISC_R_SUCCESS) {
1083                 fprintf(stderr, "dns_client_createx failed: %d\n", result);
1084                 exit(1);
1085         }
1086
1087         /* Set local cache server */
1088         memset(&hints, 0, sizeof(hints));
1089         hints.ai_family = AF_UNSPEC;
1090         hints.ai_socktype = SOCK_DGRAM;
1091         error = getaddrinfo(cacheserver, "53", &hints, &res);
1092         if (error != 0) {
1093                 fprintf(stderr, "failed to convert server name (%s): %s\n",
1094                         cacheserver, gai_strerror(error));
1095                 exit(1);
1096         }
1097
1098         if (res->ai_addrlen > sizeof(sa.type)) {
1099                 fprintf(stderr,
1100                         "assumption failure: addrlen is too long: %ld\n",
1101                         (long)res->ai_addrlen);
1102                 exit(1);
1103         }
1104         memmove(&sa.type.sa, res->ai_addr, res->ai_addrlen);
1105         sa.length = res->ai_addrlen;
1106         freeaddrinfo(res);
1107         ISC_LINK_INIT(&sa, link);
1108         ISC_LIST_INIT(servers);
1109         ISC_LIST_APPEND(servers, &sa, link);
1110         result = dns_client_setservers(client, dns_rdataclass_in, NULL,
1111                                        &servers);
1112         if (result != ISC_R_SUCCESS) {
1113                 fprintf(stderr, "failed to set server: %d\n", result);
1114                 exit(1);
1115         }
1116
1117         /* Create the main task */
1118         probe_task = NULL;
1119         result = isc_task_create(taskmgr, 0, &probe_task);
1120         if (result != ISC_R_SUCCESS) {
1121                 fprintf(stderr, "failed to create task: %d\n", result);
1122                 exit(1);
1123         }
1124
1125         /* Open input file */
1126         if (argc == 0)
1127                 fp = stdin;
1128         else {
1129                 fp = fopen(argv[0], "r");
1130                 if (fp == NULL) {
1131                         fprintf(stderr, "failed to open input file: %s\n",
1132                                 argv[0]);
1133                         exit(1);
1134                 }
1135         }
1136
1137         /* Set up and start probe */
1138         for (i = 0; i < MAX_PROBES; i++) {
1139                 probes[i].inuse = ISC_FALSE;
1140                 probes[i].domain = NULL;
1141                 dns_fixedname_init(&probes[i].fixedname);
1142                 probes[i].qname = NULL;
1143                 probes[i].qlabel = qlabels;
1144                 probes[i].qname_found = ISC_FALSE;
1145                 probes[i].resid = NULL;
1146                 ISC_LIST_INIT(probes[i].nslist);
1147                 probes[i].reqid = NULL;
1148
1149                 probes[i].qmessage = NULL;
1150                 result = dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER,
1151                                             &probes[i].qmessage);
1152                 if (result == ISC_R_SUCCESS) {
1153                         result = dns_message_create(mctx,
1154                                                     DNS_MESSAGE_INTENTPARSE,
1155                                                     &probes[i].rmessage);
1156                 }
1157                 if (result != ISC_R_SUCCESS) {
1158                         fprintf(stderr, "initialization failure\n");
1159                         exit(1);
1160                 }
1161         }
1162         for (i = 0; i < MAX_PROBES; i++) {
1163                 result = probe_domain(&probes[i]);
1164                 if (result == ISC_R_NOMORE)
1165                         break;
1166                 else if (result != ISC_R_SUCCESS) {
1167                         fprintf(stderr, "failed to issue an initial probe\n");
1168                         exit(1);
1169                 }
1170         }
1171
1172         /* Start event loop */
1173         isc_app_ctxrun(actx);
1174
1175         /* Dump results */
1176         printf("Per domain results (out of %lu domains):\n",
1177                number_of_domains);
1178         printf("  valid: %lu\n"
1179                "  ignore: %lu\n"
1180                "  nxdomain: %lu\n"
1181                "  othererr: %lu\n"
1182                "  multiplesoa: %lu\n"
1183                "  multiplecname: %lu\n"
1184                "  brokenanswer: %lu\n"
1185                "  lame: %lu\n"
1186                "  unknown: %lu\n"
1187                "  multiple errors: %lu\n",
1188                domain_stat.valid, domain_stat.ignore, domain_stat.nxdomain,
1189                domain_stat.othererr, domain_stat.multiplesoa,
1190                domain_stat.multiplecname, domain_stat.brokenanswer,
1191                domain_stat.lame, domain_stat.unknown, multiple_error_domains);
1192         printf("Per server results (out of %lu servers):\n",
1193                number_of_servers);
1194         printf("  valid: %lu\n"
1195                "  ignore: %lu\n"
1196                "  nxdomain: %lu\n"
1197                "  othererr: %lu\n"
1198                "  multiplesoa: %lu\n"
1199                "  multiplecname: %lu\n"
1200                "  brokenanswer: %lu\n"
1201                "  lame: %lu\n"
1202                "  unknown: %lu\n",
1203                server_stat.valid, server_stat.ignore, server_stat.nxdomain,
1204                server_stat.othererr, server_stat.multiplesoa,
1205                server_stat.multiplecname, server_stat.brokenanswer,
1206                server_stat.lame, server_stat.unknown);
1207
1208         /* Cleanup */
1209         for (i = 0; i < MAX_PROBES; i++) {
1210                 dns_message_destroy(&probes[i].qmessage);
1211                 dns_message_destroy(&probes[i].rmessage);
1212         }
1213         isc_task_detach(&probe_task);
1214         dns_client_destroy(&client);
1215         dns_lib_shutdown();
1216         isc_app_ctxfinish(actx);
1217         ctxs_destroy(&mctx, &actx, &taskmgr, &socketmgr, &timermgr);
1218
1219         return (0);
1220 }