]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - contrib/bind9/lib/export/samples/nsprobe.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / contrib / bind9 / lib / export / samples / nsprobe.c
1 /*
2  * Copyright (C) 2009-2012  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         if (message != NULL)
286                 dns_message_destroy(&message);
287         return (result);
288 }
289
290 /*
291  * Update statistics
292  */
293 static inline void
294 increment_entry(unsigned long *entryp) {
295         (*entryp)++;
296         INSIST(*entryp != 0);   /* check overflow */
297 }
298
299 static void
300 update_stat(struct probe_trans *trans) {
301         struct probe_ns *pns;
302         struct server *server;
303         struct lcl_stat local_stat;
304         unsigned int err_count = 0;
305         const char *stattype;
306
307         increment_entry(&number_of_domains);
308         memset(&local_stat, 0, sizeof(local_stat));
309
310         /* Update per sever statistics */
311         for (pns = ISC_LIST_HEAD(trans->nslist); pns != NULL;
312              pns = ISC_LIST_NEXT(pns, link)) {
313                 for (server = ISC_LIST_HEAD(pns->servers); server != NULL;
314                      server = ISC_LIST_NEXT(server, link)) {
315                         increment_entry(&number_of_servers);
316
317                         if (server->result_aaaa == exist ||
318                             server->result_aaaa == notype) {
319                                 /*
320                                  * Don't care about the result of A query if
321                                  * the answer to AAAA query was expected.
322                                  */
323                                 stattype = "valid";
324                                 increment_entry(&server_stat.valid);
325                                 increment_entry(&local_stat.valid);
326                         } else if (server->result_a == exist) {
327                                 switch (server->result_aaaa) {
328                                 case exist:
329                                 case notype:
330                                         stattype = "valid";
331                                         increment_entry(&server_stat.valid);
332                                         increment_entry(&local_stat.valid);
333                                         break;
334                                 case timedout:
335                                         stattype = "ignore";
336                                         increment_entry(&server_stat.ignore);
337                                         increment_entry(&local_stat.ignore);
338                                         break;
339                                 case nxdomain:
340                                         stattype = "nxdomain";
341                                         increment_entry(&server_stat.nxdomain);
342                                         increment_entry(&local_stat.nxdomain);
343                                         break;
344                                 case othererr:
345                                         stattype = "othererr";
346                                         increment_entry(&server_stat.othererr);
347                                         increment_entry(&local_stat.othererr);
348                                         break;
349                                 case multiplesoa:
350                                         stattype = "multiplesoa";
351                                         increment_entry(&server_stat.multiplesoa);
352                                         increment_entry(&local_stat.multiplesoa);
353                                         break;
354                                 case multiplecname:
355                                         stattype = "multiplecname";
356                                         increment_entry(&server_stat.multiplecname);
357                                         increment_entry(&local_stat.multiplecname);
358                                         break;
359                                 case brokenanswer:
360                                         stattype = "brokenanswer";
361                                         increment_entry(&server_stat.brokenanswer);
362                                         increment_entry(&local_stat.brokenanswer);
363                                         break;
364                                 case lame:
365                                         stattype = "lame";
366                                         increment_entry(&server_stat.lame);
367                                         increment_entry(&local_stat.lame);
368                                         break;
369                                 default:
370                                         stattype = "unknown";
371                                         increment_entry(&server_stat.unknown);
372                                         increment_entry(&local_stat.unknown);
373                                         break;
374                                 }
375                         } else {
376                                 stattype = "unknown";
377                                 increment_entry(&server_stat.unknown);
378                                 increment_entry(&local_stat.unknown);
379                         }
380
381                         if (verbose_level > 1 ||
382                             (verbose_level == 1 &&
383                              strcmp(stattype, "valid") != 0 &&
384                              strcmp(stattype, "unknown") != 0)) {
385                                 print_name(pns->name);
386                                 putchar('(');
387                                 print_address(stdout, &server->address);
388                                 printf(") for %s:%s\n", trans->domain,
389                                        stattype);
390                         }
391                 }
392         }
393
394         /* Update per domain statistics */
395         if (local_stat.ignore > 0) {
396                 if (verbose_level > 0)
397                         printf("%s:ignore\n", trans->domain);
398                 increment_entry(&domain_stat.ignore);
399                 err_count++;
400         }
401         if (local_stat.nxdomain > 0) {
402                 if (verbose_level > 0)
403                         printf("%s:nxdomain\n", trans->domain);
404                 increment_entry(&domain_stat.nxdomain);
405                 err_count++;
406         }
407         if (local_stat.othererr > 0) {
408                 if (verbose_level > 0)
409                         printf("%s:othererr\n", trans->domain);
410                 increment_entry(&domain_stat.othererr);
411                 err_count++;
412         }
413         if (local_stat.multiplesoa > 0) {
414                 if (verbose_level > 0)
415                         printf("%s:multiplesoa\n", trans->domain);
416                 increment_entry(&domain_stat.multiplesoa);
417                 err_count++;
418         }
419         if (local_stat.multiplecname > 0) {
420                 if (verbose_level > 0)
421                         printf("%s:multiplecname\n", trans->domain);
422                 increment_entry(&domain_stat.multiplecname);
423                 err_count++;
424         }
425         if (local_stat.brokenanswer > 0) {
426                 if (verbose_level > 0)
427                         printf("%s:brokenanswer\n", trans->domain);
428                 increment_entry(&domain_stat.brokenanswer);
429                 err_count++;
430         }
431         if (local_stat.lame > 0) {
432                 if (verbose_level > 0)
433                         printf("%s:lame\n", trans->domain);
434                 increment_entry(&domain_stat.lame);
435                 err_count++;
436         }
437
438         if (err_count > 1)
439                 increment_entry(&multiple_error_domains);
440
441         /*
442          * We regard the domain as valid if and only if no authoritative server
443          * has a problem and at least one server is known to be valid.
444          */
445         if (local_stat.valid > 0 && err_count == 0) {
446                 if (verbose_level > 1)
447                         printf("%s:valid\n", trans->domain);
448                 increment_entry(&domain_stat.valid);
449         }
450
451         /*
452          * If the domain has no available server or all servers have the
453          * 'unknown' result, the domain's result is also regarded as unknown.
454          */
455         if (local_stat.valid == 0 && err_count == 0) {
456                 if (verbose_level > 1)
457                         printf("%s:unknown\n", trans->domain);
458                 increment_entry(&domain_stat.unknown);
459         }
460 }
461
462 /*
463  * Search for an existent name with an A RR
464  */
465
466 static isc_result_t
467 set_nextqname(struct probe_trans *trans) {
468         isc_result_t result;
469         size_t domainlen;
470         isc_buffer_t b;
471         char buf[4096]; /* XXX ad-hoc constant, but should be enough */
472
473         if (*trans->qlabel == NULL)
474                 return (ISC_R_NOMORE);
475
476         result = isc_string_copy(buf, sizeof(buf), *trans->qlabel);
477         if (result != ISC_R_SUCCESS)
478                 return (result);
479         result = isc_string_append(buf, sizeof(buf), trans->domain);
480         if (result != ISC_R_SUCCESS)
481                 return (result);
482
483         domainlen = strlen(buf);
484         isc_buffer_init(&b, buf, domainlen);
485         isc_buffer_add(&b, domainlen);
486         dns_fixedname_init(&trans->fixedname);
487         trans->qname = dns_fixedname_name(&trans->fixedname);
488         result = dns_name_fromtext(trans->qname, &b, dns_rootname,
489                                    0, NULL);
490
491         trans->qlabel++;
492
493         return (result);
494 }
495
496 static void
497 request_done(isc_task_t *task, isc_event_t *event) {
498         struct probe_trans *trans = event->ev_arg;
499         dns_clientreqevent_t *rev = (dns_clientreqevent_t *)event;
500         dns_message_t *rmessage;
501         struct probe_ns *pns;
502         struct server *server;
503         isc_result_t result;
504         query_result_t *resultp;
505         dns_name_t *name;
506         dns_rdataset_t *rdataset;
507         dns_rdatatype_t type;
508
509         REQUIRE(task == probe_task);
510         REQUIRE(trans != NULL && trans->inuse == ISC_TRUE);
511         rmessage = rev->rmessage;
512         REQUIRE(rmessage == trans->rmessage);
513         INSIST(outstanding_probes > 0);
514
515         server = trans->current_ns->current_server;
516         INSIST(server != NULL);
517
518         if (server->result_a == none) {
519                 type = dns_rdatatype_a;
520                 resultp = &server->result_a;
521         } else {
522                 resultp = &server->result_aaaa;
523                 type = dns_rdatatype_aaaa;
524         }
525
526         if (rev->result == ISC_R_SUCCESS) {
527                 if ((rmessage->flags & DNS_MESSAGEFLAG_AA) == 0)
528                         *resultp = lame;
529                 else if (rmessage->rcode == dns_rcode_nxdomain)
530                         *resultp = nxdomain;
531                 else if (rmessage->rcode != dns_rcode_noerror)
532                         *resultp = othererr;
533                 else if (rmessage->counts[DNS_SECTION_ANSWER] == 0) {
534                         /* no error but empty answer */
535                         *resultp = notype;
536                 } else {
537                         result = dns_message_firstname(rmessage,
538                                                        DNS_SECTION_ANSWER);
539                         while (result == ISC_R_SUCCESS) {
540                                 name = NULL;
541                                 dns_message_currentname(rmessage,
542                                                         DNS_SECTION_ANSWER,
543                                                         &name);
544                                 for (rdataset = ISC_LIST_HEAD(name->list);
545                                      rdataset != NULL;
546                                      rdataset = ISC_LIST_NEXT(rdataset,
547                                                               link)) {
548                                         (void)print_rdataset(rdataset, name);
549
550                                         if (rdataset->type ==
551                                             dns_rdatatype_cname ||
552                                             rdataset->type ==
553                                             dns_rdatatype_dname) {
554                                                 /* Should chase the chain? */
555                                                 *resultp = exist;
556                                                 goto found;
557                                         } else if (rdataset->type == type) {
558                                                 *resultp = exist;
559                                                 goto found;
560                                         }
561                                 }
562                                 result = dns_message_nextname(rmessage,
563                                                               DNS_SECTION_ANSWER);
564                         }
565
566                         /*
567                          * Something unexpected happened: the response
568                          * contained a non-empty authoritative answer, but we
569                          * could not find an expected result.
570                          */
571                         *resultp = unexpected;
572                 }
573         } else if (rev->result == DNS_R_RECOVERABLE ||
574                    rev->result == DNS_R_BADLABELTYPE) {
575                 /* Broken response.  Try identifying known cases. */
576                 *resultp = brokenanswer;
577
578                 if (rmessage->counts[DNS_SECTION_ANSWER] > 0) {
579                         result = dns_message_firstname(rmessage,
580                                                        DNS_SECTION_ANSWER);
581                         while (result == ISC_R_SUCCESS) {
582                                 /*
583                                  * Check to see if the response has multiple
584                                  * CNAME RRs.  Update the result code if so.
585                                  */
586                                 name = NULL;
587                                 dns_message_currentname(rmessage,
588                                                         DNS_SECTION_ANSWER,
589                                                         &name);
590                                 for (rdataset = ISC_LIST_HEAD(name->list);
591                                      rdataset != NULL;
592                                      rdataset = ISC_LIST_NEXT(rdataset,
593                                                               link)) {
594                                         if (rdataset->type ==
595                                             dns_rdatatype_cname &&
596                                             dns_rdataset_count(rdataset) > 1) {
597                                                 *resultp = multiplecname;
598                                                 goto found;
599                                         }
600                                 }
601                                 result = dns_message_nextname(rmessage,
602                                                               DNS_SECTION_ANSWER);
603                         }
604                 }
605
606                 if (rmessage->counts[DNS_SECTION_AUTHORITY] > 0) {
607                         result = dns_message_firstname(rmessage,
608                                                        DNS_SECTION_AUTHORITY);
609                         while (result == ISC_R_SUCCESS) {
610                                 /*
611                                  * Check to see if the response has multiple
612                                  * SOA RRs.  Update the result code if so.
613                                  */
614                                 name = NULL;
615                                 dns_message_currentname(rmessage,
616                                                         DNS_SECTION_AUTHORITY,
617                                                         &name);
618                                 for (rdataset = ISC_LIST_HEAD(name->list);
619                                      rdataset != NULL;
620                                      rdataset = ISC_LIST_NEXT(rdataset,
621                                                               link)) {
622                                         if (rdataset->type ==
623                                             dns_rdatatype_soa &&
624                                             dns_rdataset_count(rdataset) > 1) {
625                                                 *resultp = multiplesoa;
626                                                 goto found;
627                                         }
628                                 }
629                                 result = dns_message_nextname(rmessage,
630                                                               DNS_SECTION_AUTHORITY);
631                         }
632                 }
633         } else if (rev->result == ISC_R_TIMEDOUT)
634                 *resultp = timedout;
635         else {
636                 fprintf(stderr, "unexpected result: %d (domain=%s, server=",
637                         rev->result, trans->domain);
638                 print_address(stderr, &server->address);
639                 fputc('\n', stderr);
640                 *resultp = unexpected;
641         }
642
643  found:
644         INSIST(*resultp != none);
645         if (type == dns_rdatatype_a && *resultp == exist)
646                 trans->qname_found = ISC_TRUE;
647
648         dns_client_destroyreqtrans(&trans->reqid);
649         isc_event_free(&event);
650         dns_message_reset(trans->rmessage, DNS_MESSAGE_INTENTPARSE);
651
652         result = probe_name(trans, type);
653         if (result == ISC_R_NOMORE) {
654                 /* We've tried all addresses of all servers. */
655                 if (type == dns_rdatatype_a && trans->qname_found) {
656                         /*
657                          * If we've explored A RRs and found an existent
658                          * record, we can move to AAAA.
659                          */
660                         trans->current_ns = ISC_LIST_HEAD(trans->nslist);
661                         probe_name(trans, dns_rdatatype_aaaa);
662                         result = ISC_R_SUCCESS;
663                 } else if (type == dns_rdatatype_a) {
664                         /*
665                          * No server provided an existent A RR of this name.
666                          * Try next label.
667                          */
668                         dns_fixedname_invalidate(&trans->fixedname);
669                         trans->qname = NULL;
670                         result = set_nextqname(trans);
671                         if (result == ISC_R_SUCCESS) {
672                                 trans->current_ns =
673                                         ISC_LIST_HEAD(trans->nslist);
674                                 for (pns = trans->current_ns; pns != NULL;
675                                      pns = ISC_LIST_NEXT(pns, link)) {
676                                         for (server = ISC_LIST_HEAD(pns->servers);
677                                              server != NULL;
678                                              server = ISC_LIST_NEXT(server,
679                                                                     link)) {
680                                                 INSIST(server->result_aaaa ==
681                                                        none);
682                                                 server->result_a = none;
683                                         }
684                                 }
685                                 result = probe_name(trans, dns_rdatatype_a);
686                         }
687                 }
688                 if (result != ISC_R_SUCCESS) {
689                         /*
690                          * We've explored AAAA RRs or failed to find a valid
691                          * query label.  Wrap up the result and move to the
692                          * next domain.
693                          */
694                         reset_probe(trans);
695                 }
696         } else if (result != ISC_R_SUCCESS)
697                 reset_probe(trans); /* XXX */
698 }
699
700 static isc_result_t
701 probe_name(struct probe_trans *trans, dns_rdatatype_t type) {
702         isc_result_t result;
703         struct probe_ns *pns;
704         struct server *server;
705
706         REQUIRE(trans->reqid == NULL);
707         REQUIRE(type == dns_rdatatype_a || type == dns_rdatatype_aaaa);
708
709         for (pns = trans->current_ns; pns != NULL;
710              pns = ISC_LIST_NEXT(pns, link)) {
711                 for (server = ISC_LIST_HEAD(pns->servers); server != NULL;
712                      server = ISC_LIST_NEXT(server, link)) {
713                         if ((type == dns_rdatatype_a &&
714                              server->result_a == none) ||
715                             (type == dns_rdatatype_aaaa &&
716                              server->result_aaaa == none)) {
717                                 pns->current_server = server;
718                                 goto found;
719                         }
720                 }
721         }
722
723  found:
724         trans->current_ns = pns;
725         if (pns == NULL)
726                 return (ISC_R_NOMORE);
727
728         INSIST(pns->current_server != NULL);
729         dns_message_reset(trans->qmessage, DNS_MESSAGE_INTENTRENDER);
730         result = make_querymessage(trans->qmessage, trans->qname, type);
731         if (result != ISC_R_SUCCESS)
732                 return (result);
733         result = dns_client_startrequest(client, trans->qmessage,
734                                          trans->rmessage,
735                                          &pns->current_server->address,
736                                          0, DNS_MESSAGEPARSE_BESTEFFORT,
737                                          NULL, 120, 0, 4,
738                                          probe_task, request_done, trans,
739                                          &trans->reqid);
740
741         return (result);
742 }
743
744 /*
745  * Get IP addresses of NSes
746  */
747
748 static void
749 resolve_nsaddress(isc_task_t *task, isc_event_t *event) {
750         struct probe_trans *trans = event->ev_arg;
751         dns_clientresevent_t *rev = (dns_clientresevent_t *)event;
752         dns_name_t *name;
753         dns_rdataset_t *rdataset;
754         dns_rdata_t rdata = DNS_RDATA_INIT;
755         struct probe_ns *pns = trans->current_ns;
756         isc_result_t result;
757
758         REQUIRE(task == probe_task);
759         REQUIRE(trans->inuse == ISC_TRUE);
760         REQUIRE(pns != NULL);
761         INSIST(outstanding_probes > 0);
762
763         for (name = ISC_LIST_HEAD(rev->answerlist); name != NULL;
764              name = ISC_LIST_NEXT(name, link)) {
765                 for (rdataset = ISC_LIST_HEAD(name->list);
766                      rdataset != NULL;
767                      rdataset = ISC_LIST_NEXT(rdataset, link)) {
768                         (void)print_rdataset(rdataset, name);
769
770                         if (rdataset->type != dns_rdatatype_a)
771                                 continue;
772
773                         for (result = dns_rdataset_first(rdataset);
774                              result == ISC_R_SUCCESS;
775                              result = dns_rdataset_next(rdataset)) {
776                                 dns_rdata_in_a_t rdata_a;
777                                 struct server *server;
778
779                                 dns_rdataset_current(rdataset, &rdata);
780                                 result = dns_rdata_tostruct(&rdata, &rdata_a,
781                                                             NULL);
782                                 if (result != ISC_R_SUCCESS)
783                                         continue;
784
785                                 server = isc_mem_get(mctx, sizeof(*server));
786                                 if (server == NULL) {
787                                         fprintf(stderr, "resolve_nsaddress: "
788                                                 "mem_get failed");
789                                         result = ISC_R_NOMEMORY;
790                                         POST(result);
791                                         goto cleanup;
792                                 }
793                                 isc_sockaddr_fromin(&server->address,
794                                                     &rdata_a.in_addr, 53);
795                                 ISC_LINK_INIT(server, link);
796                                 server->result_a = none;
797                                 server->result_aaaa = none;
798                                 ISC_LIST_APPEND(pns->servers, server, link);
799                         }
800                 }
801         }
802
803  cleanup:
804         dns_client_freeresanswer(client, &rev->answerlist);
805         dns_client_destroyrestrans(&trans->resid);
806         isc_event_free(&event);
807
808  next_ns:
809         trans->current_ns = ISC_LIST_NEXT(pns, link);
810         if (trans->current_ns == NULL) {
811                 trans->current_ns = ISC_LIST_HEAD(trans->nslist);
812                 dns_fixedname_invalidate(&trans->fixedname);
813                 trans->qname = NULL;
814                 result = set_nextqname(trans);
815                 if (result == ISC_R_SUCCESS)
816                          result = probe_name(trans, dns_rdatatype_a);
817         } else {
818                 result = fetch_nsaddress(trans);
819                 if (result != ISC_R_SUCCESS)
820                         goto next_ns; /* XXX: this is unlikely to succeed */
821         }
822
823         if (result != ISC_R_SUCCESS)
824                 reset_probe(trans);
825 }
826
827 static isc_result_t
828 fetch_nsaddress(struct probe_trans *trans) {
829         struct probe_ns *pns;
830
831         pns = trans->current_ns;
832         REQUIRE(pns != NULL);
833
834         return (dns_client_startresolve(client, pns->name, dns_rdataclass_in,
835                                         dns_rdatatype_a, 0, probe_task,
836                                         resolve_nsaddress, trans,
837                                         &trans->resid));
838 }
839
840 /*
841  * Get NS RRset for a given domain
842  */
843
844 static void
845 reset_probe(struct probe_trans *trans) {
846         struct probe_ns *pns;
847         struct server *server;
848         isc_result_t result;
849
850         REQUIRE(trans->resid == NULL);
851         REQUIRE(trans->reqid == NULL);
852
853         update_stat(trans);
854
855         dns_message_reset(trans->qmessage, DNS_MESSAGE_INTENTRENDER);
856         dns_message_reset(trans->rmessage, DNS_MESSAGE_INTENTPARSE);
857
858         trans->inuse = ISC_FALSE;
859         if (trans->domain != NULL)
860                 isc_mem_free(mctx, trans->domain);
861         trans->domain = NULL;
862         if (trans->qname != NULL)
863                 dns_fixedname_invalidate(&trans->fixedname);
864         trans->qname = NULL;
865         trans->qlabel = qlabels;
866         trans->qname_found = ISC_FALSE;
867         trans->current_ns = NULL;
868
869         while ((pns = ISC_LIST_HEAD(trans->nslist)) != NULL) {
870                 ISC_LIST_UNLINK(trans->nslist, pns, link);
871                 while ((server = ISC_LIST_HEAD(pns->servers)) != NULL) {
872                         ISC_LIST_UNLINK(pns->servers, server, link);
873                         isc_mem_put(mctx, server, sizeof(*server));
874                 }
875                 isc_mem_put(mctx, pns, sizeof(*pns));
876         }
877
878         outstanding_probes--;
879
880         result = probe_domain(trans);
881         if (result == ISC_R_NOMORE && outstanding_probes == 0)
882                 isc_app_ctxshutdown(actx);
883 }
884
885 static void
886 resolve_ns(isc_task_t *task, isc_event_t *event) {
887         struct probe_trans *trans = event->ev_arg;
888         dns_clientresevent_t *rev = (dns_clientresevent_t *)event;
889         dns_name_t *name;
890         dns_rdataset_t *rdataset;
891         isc_result_t result = ISC_R_SUCCESS;
892         dns_rdata_t rdata = DNS_RDATA_INIT;
893         struct probe_ns *pns;
894
895         REQUIRE(task == probe_task);
896         REQUIRE(trans->inuse == ISC_TRUE);
897         INSIST(outstanding_probes > 0);
898
899         for (name = ISC_LIST_HEAD(rev->answerlist); name != NULL;
900              name = ISC_LIST_NEXT(name, link)) {
901                 for (rdataset = ISC_LIST_HEAD(name->list);
902                      rdataset != NULL;
903                      rdataset = ISC_LIST_NEXT(rdataset, link)) {
904                         (void)print_rdataset(rdataset, name);
905
906                         if (rdataset->type != dns_rdatatype_ns)
907                                 continue;
908
909                         for (result = dns_rdataset_first(rdataset);
910                              result == ISC_R_SUCCESS;
911                              result = dns_rdataset_next(rdataset)) {
912                                 dns_rdata_ns_t ns;
913
914                                 dns_rdataset_current(rdataset, &rdata);
915                                 /*
916                                  * Extract the name from the NS record.
917                                  */
918                                 result = dns_rdata_tostruct(&rdata, &ns, NULL);
919                                 if (result != ISC_R_SUCCESS)
920                                         continue;
921
922                                 pns = isc_mem_get(mctx, sizeof(*pns));
923                                 if (pns == NULL) {
924                                         fprintf(stderr,
925                                                 "resolve_ns: mem_get failed");
926                                         result = ISC_R_NOMEMORY;
927                                         POST(result);
928                                         /*
929                                          * XXX: should we continue with the
930                                          * available servers anyway?
931                                          */
932                                         goto cleanup;
933                                 }
934
935                                 dns_fixedname_init(&pns->fixedname);
936                                 pns->name =
937                                         dns_fixedname_name(&pns->fixedname);
938                                 ISC_LINK_INIT(pns, link);
939                                 ISC_LIST_APPEND(trans->nslist, pns, link);
940                                 ISC_LIST_INIT(pns->servers);
941
942                                 dns_name_copy(&ns.name, pns->name, NULL);
943                                 dns_rdata_reset(&rdata);
944                                 dns_rdata_freestruct(&ns);
945                         }
946                 }
947         }
948
949  cleanup:
950         dns_client_freeresanswer(client, &rev->answerlist);
951         dns_client_destroyrestrans(&trans->resid);
952         isc_event_free(&event);
953
954         if (!ISC_LIST_EMPTY(trans->nslist)) {
955                 /* Go get addresses of NSes */
956                 trans->current_ns = ISC_LIST_HEAD(trans->nslist);
957                 result = fetch_nsaddress(trans);
958         } else
959                 result = ISC_R_FAILURE;
960
961         if (result == ISC_R_SUCCESS)
962                 return;
963
964         reset_probe(trans);
965 }
966
967 static isc_result_t
968 probe_domain(struct probe_trans *trans) {
969         isc_result_t result;
970         size_t domainlen;
971         isc_buffer_t b;
972         char buf[4096]; /* XXX ad hoc constant, but should be enough */
973         char *cp;
974
975         REQUIRE(trans != NULL);
976         REQUIRE(trans->inuse == ISC_FALSE);
977         REQUIRE(outstanding_probes < MAX_PROBES);
978
979         /* Construct domain */
980         cp = fgets(buf, sizeof(buf), fp);
981         if (cp == NULL)
982                 return (ISC_R_NOMORE);
983         if ((cp = strchr(buf, '\n')) != NULL) /* zap NL if any */
984                 *cp = '\0';
985         trans->domain = isc_mem_strdup(mctx, buf);
986         if (trans->domain == NULL) {
987                 fprintf(stderr,
988                         "failed to allocate memory for domain: %s", cp);
989                 return (ISC_R_NOMEMORY);
990         }
991
992         /* Start getting NS for the domain */
993         domainlen = strlen(buf);
994         isc_buffer_init(&b, buf, domainlen);
995         isc_buffer_add(&b, domainlen);
996         dns_fixedname_init(&trans->fixedname);
997         trans->qname = dns_fixedname_name(&trans->fixedname);
998         result = dns_name_fromtext(trans->qname, &b, dns_rootname, 0, NULL);
999         if (result != ISC_R_SUCCESS)
1000                 goto cleanup;
1001         result = dns_client_startresolve(client, trans->qname,
1002                                          dns_rdataclass_in, dns_rdatatype_ns,
1003                                          0, probe_task, resolve_ns, trans,
1004                                          &trans->resid);
1005         if (result != ISC_R_SUCCESS)
1006                 goto cleanup;
1007
1008         trans->inuse = ISC_TRUE;
1009         outstanding_probes++;
1010
1011         return (ISC_R_SUCCESS);
1012
1013  cleanup:
1014         isc_mem_free(mctx, trans->domain);
1015         dns_fixedname_invalidate(&trans->fixedname);
1016
1017         return (result);
1018 }
1019
1020 ISC_PLATFORM_NORETURN_PRE static void
1021 usage(void) ISC_PLATFORM_NORETURN_POST;
1022
1023 static void
1024 usage(void) {
1025         fprintf(stderr, "usage: nsprobe [-d] [-v [-v...]] [-c cache_address] "
1026                 "[input_file]\n");
1027
1028         exit(1);
1029 }
1030
1031 int
1032 main(int argc, char *argv[]) {
1033         int i, ch, error;
1034         struct addrinfo hints, *res;
1035         isc_result_t result;
1036         isc_sockaddr_t sa;
1037         isc_sockaddrlist_t servers;
1038         isc_taskmgr_t *taskmgr = NULL;
1039         isc_socketmgr_t *socketmgr = NULL;
1040         isc_timermgr_t *timermgr = NULL;
1041
1042         while ((ch = getopt(argc, argv, "c:dhv")) != -1) {
1043                 switch (ch) {
1044                 case 'c':
1045                         cacheserver = optarg;
1046                         break;
1047                 case 'd':
1048                         debug_mode = ISC_TRUE;
1049                         break;
1050                 case 'h':
1051                         usage();
1052                         break;
1053                 case 'v':
1054                         verbose_level++;
1055                         break;
1056                 default:
1057                         usage();
1058                         break;
1059                 }
1060         }
1061
1062         argc -= optind;
1063         argv += optind;
1064
1065         /* Common set up */
1066         isc_lib_register();
1067         result = dns_lib_init();
1068         if (result != ISC_R_SUCCESS) {
1069                 fprintf(stderr, "dns_lib_init failed: %d\n", result);
1070                 exit(1);
1071         }
1072
1073         result = ctxs_init(&mctx, &actx, &taskmgr, &socketmgr,
1074                            &timermgr);
1075         if (result != ISC_R_SUCCESS) {
1076                 fprintf(stderr, "ctx create failed: %d\n", result);
1077                 exit(1);
1078         }
1079
1080         isc_app_ctxstart(actx);
1081
1082         result = dns_client_createx(mctx, actx, taskmgr, socketmgr,
1083                                     timermgr, 0, &client);
1084         if (result != ISC_R_SUCCESS) {
1085                 fprintf(stderr, "dns_client_createx failed: %d\n", result);
1086                 exit(1);
1087         }
1088
1089         /* Set local cache server */
1090         memset(&hints, 0, sizeof(hints));
1091         hints.ai_family = AF_UNSPEC;
1092         hints.ai_socktype = SOCK_DGRAM;
1093         error = getaddrinfo(cacheserver, "53", &hints, &res);
1094         if (error != 0) {
1095                 fprintf(stderr, "failed to convert server name (%s): %s\n",
1096                         cacheserver, gai_strerror(error));
1097                 exit(1);
1098         }
1099
1100         if (res->ai_addrlen > sizeof(sa.type)) {
1101                 fprintf(stderr,
1102                         "assumption failure: addrlen is too long: %ld\n",
1103                         (long)res->ai_addrlen);
1104                 exit(1);
1105         }
1106         memcpy(&sa.type.sa, res->ai_addr, res->ai_addrlen);
1107         sa.length = res->ai_addrlen;
1108         freeaddrinfo(res);
1109         ISC_LINK_INIT(&sa, link);
1110         ISC_LIST_INIT(servers);
1111         ISC_LIST_APPEND(servers, &sa, link);
1112         result = dns_client_setservers(client, dns_rdataclass_in, NULL,
1113                                        &servers);
1114         if (result != ISC_R_SUCCESS) {
1115                 fprintf(stderr, "failed to set server: %d\n", result);
1116                 exit(1);
1117         }
1118
1119         /* Create the main task */
1120         probe_task = NULL;
1121         result = isc_task_create(taskmgr, 0, &probe_task);
1122         if (result != ISC_R_SUCCESS) {
1123                 fprintf(stderr, "failed to create task: %d\n", result);
1124                 exit(1);
1125         }
1126
1127         /* Open input file */
1128         if (argc == 0)
1129                 fp = stdin;
1130         else {
1131                 fp = fopen(argv[0], "r");
1132                 if (fp == NULL) {
1133                         fprintf(stderr, "failed to open input file: %s\n",
1134                                 argv[0]);
1135                         exit(1);
1136                 }
1137         }
1138
1139         /* Set up and start probe */
1140         for (i = 0; i < MAX_PROBES; i++) {
1141                 probes[i].inuse = ISC_FALSE;
1142                 probes[i].domain = NULL;
1143                 dns_fixedname_init(&probes[i].fixedname);
1144                 probes[i].qname = NULL;
1145                 probes[i].qlabel = qlabels;
1146                 probes[i].qname_found = ISC_FALSE;
1147                 probes[i].resid = NULL;
1148                 ISC_LIST_INIT(probes[i].nslist);
1149                 probes[i].reqid = NULL;
1150
1151                 probes[i].qmessage = NULL;
1152                 result = dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER,
1153                                             &probes[i].qmessage);
1154                 if (result == ISC_R_SUCCESS) {
1155                         result = dns_message_create(mctx,
1156                                                     DNS_MESSAGE_INTENTPARSE,
1157                                                     &probes[i].rmessage);
1158                 }
1159                 if (result != ISC_R_SUCCESS) {
1160                         fprintf(stderr, "initialization failure\n");
1161                         exit(1);
1162                 }
1163         }
1164         for (i = 0; i < MAX_PROBES; i++) {
1165                 result = probe_domain(&probes[i]);
1166                 if (result == ISC_R_NOMORE)
1167                         break;
1168                 else if (result != ISC_R_SUCCESS) {
1169                         fprintf(stderr, "failed to issue an initial probe\n");
1170                         exit(1);
1171                 }
1172         }
1173
1174         /* Start event loop */
1175         isc_app_ctxrun(actx);
1176
1177         /* Dump results */
1178         printf("Per domain results (out of %lu domains):\n",
1179                number_of_domains);
1180         printf("  valid: %lu\n"
1181                "  ignore: %lu\n"
1182                "  nxdomain: %lu\n"
1183                "  othererr: %lu\n"
1184                "  multiplesoa: %lu\n"
1185                "  multiplecname: %lu\n"
1186                "  brokenanswer: %lu\n"
1187                "  lame: %lu\n"
1188                "  unknown: %lu\n"
1189                "  multiple errors: %lu\n",
1190                domain_stat.valid, domain_stat.ignore, domain_stat.nxdomain,
1191                domain_stat.othererr, domain_stat.multiplesoa,
1192                domain_stat.multiplecname, domain_stat.brokenanswer,
1193                domain_stat.lame, domain_stat.unknown, multiple_error_domains);
1194         printf("Per server results (out of %lu servers):\n",
1195                number_of_servers);
1196         printf("  valid: %lu\n"
1197                "  ignore: %lu\n"
1198                "  nxdomain: %lu\n"
1199                "  othererr: %lu\n"
1200                "  multiplesoa: %lu\n"
1201                "  multiplecname: %lu\n"
1202                "  brokenanswer: %lu\n"
1203                "  lame: %lu\n"
1204                "  unknown: %lu\n",
1205                server_stat.valid, server_stat.ignore, server_stat.nxdomain,
1206                server_stat.othererr, server_stat.multiplesoa,
1207                server_stat.multiplecname, server_stat.brokenanswer,
1208                server_stat.lame, server_stat.unknown);
1209
1210         /* Cleanup */
1211         for (i = 0; i < MAX_PROBES; i++) {
1212                 dns_message_destroy(&probes[i].qmessage);
1213                 dns_message_destroy(&probes[i].rmessage);
1214         }
1215         isc_task_detach(&probe_task);
1216         dns_client_destroy(&client);
1217         dns_lib_shutdown();
1218         isc_app_ctxfinish(actx);
1219         ctxs_destroy(&mctx, &actx, &taskmgr, &socketmgr, &timermgr);
1220
1221         exit(0);
1222 }