No empty .Rs/.Re
[netbsd-mini2440.git] / external / bsd / bind / dist / lib / export / samples / nsprobe.c
blobe20737a69139aa00196d6a3da692dc8c44556fa1
1 /* $NetBSD$ */
3 /*
4 * Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
11 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
12 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
13 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
14 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
15 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
16 * PERFORMANCE OF THIS SOFTWARE.
19 /* Id: nsprobe.c,v 1.5 2009/09/29 15:06:06 fdupont Exp */
21 #include <config.h>
23 #include <sys/types.h>
24 #include <sys/socket.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 #include <netdb.h>
32 #include <isc/app.h>
33 #include <isc/buffer.h>
34 #include <isc/lib.h>
35 #include <isc/mem.h>
36 #include <isc/socket.h>
37 #include <isc/sockaddr.h>
38 #include <isc/string.h>
39 #include <isc/task.h>
40 #include <isc/timer.h>
41 #include <isc/util.h>
43 #include <dns/client.h>
44 #include <dns/fixedname.h>
45 #include <dns/lib.h>
46 #include <dns/message.h>
47 #include <dns/name.h>
48 #include <dns/rdata.h>
49 #include <dns/rdataset.h>
50 #include <dns/rdatastruct.h>
51 #include <dns/rdatatype.h>
52 #include <dns/result.h>
54 #define MAX_PROBES 1000
56 static dns_client_t *client = NULL;
57 static isc_task_t *probe_task = NULL;
58 static isc_appctx_t *actx = NULL;
59 static isc_mem_t *mctx = NULL;
60 static unsigned int outstanding_probes = 0;
61 const char *cacheserver = "127.0.0.1";
62 static FILE *fp;
64 typedef enum {
65 none,
66 exist,
67 nxdomain,
68 othererr,
69 multiplesoa,
70 multiplecname,
71 brokenanswer,
72 lame,
73 timedout,
74 notype,
75 unexpected
76 } query_result_t;
78 struct server {
79 ISC_LINK(struct server) link;
81 isc_sockaddr_t address;
82 query_result_t result_a;
83 query_result_t result_aaaa;
86 struct probe_ns {
87 ISC_LINK(struct probe_ns) link;
89 dns_fixedname_t fixedname;
90 dns_name_t *name;
91 struct server *current_server;
92 ISC_LIST(struct server) servers;
95 struct probe_trans {
96 isc_boolean_t inuse;
97 char *domain;
98 dns_fixedname_t fixedname;
99 dns_name_t *qname;
100 const char **qlabel;
101 isc_boolean_t qname_found;
102 dns_clientrestrans_t *resid;
103 dns_message_t *qmessage;
104 dns_message_t *rmessage;
105 dns_clientreqtrans_t *reqid;
107 /* NS list */
108 struct probe_ns *current_ns;
109 ISC_LIST(struct probe_ns) nslist;
112 struct stat {
113 unsigned long valid;
114 unsigned long ignore;
115 unsigned long nxdomain;
116 unsigned long othererr;
117 unsigned long multiplesoa;
118 unsigned long multiplecname;
119 unsigned long brokenanswer;
120 unsigned long lame;
121 unsigned long unknown;
122 } server_stat, domain_stat;
124 static unsigned long number_of_domains = 0;
125 static unsigned long number_of_servers = 0;
126 static unsigned long multiple_error_domains = 0;
127 static isc_boolean_t debug_mode = ISC_FALSE;
128 static int verbose_level = 0;
129 static const char *qlabels[] = {"www.", "ftp.", NULL};
130 static struct probe_trans probes[MAX_PROBES];
132 static isc_result_t probe_domain(struct probe_trans *trans);
133 static void reset_probe(struct probe_trans *trans);
134 static isc_result_t fetch_nsaddress(struct probe_trans *trans);
135 static isc_result_t probe_name(struct probe_trans *trans,
136 dns_rdatatype_t type);
138 /* Dump an rdataset for debug */
139 static isc_result_t
140 print_rdataset(dns_rdataset_t *rdataset, dns_name_t *owner) {
141 isc_buffer_t target;
142 isc_result_t result;
143 isc_region_t r;
144 char t[4096];
146 if (!debug_mode)
147 return (ISC_R_SUCCESS);
149 isc_buffer_init(&target, t, sizeof(t));
151 if (!dns_rdataset_isassociated(rdataset))
152 return (ISC_R_SUCCESS);
153 result = dns_rdataset_totext(rdataset, owner, ISC_FALSE, ISC_FALSE,
154 &target);
155 if (result != ISC_R_SUCCESS)
156 return (result);
157 isc_buffer_usedregion(&target, &r);
158 printf("%.*s", (int)r.length, (char *)r.base);
160 return (ISC_R_SUCCESS);
163 static isc_result_t
164 print_name(dns_name_t *name) {
165 isc_result_t result;
166 isc_buffer_t target;
167 isc_region_t r;
168 char t[4096];
170 isc_buffer_init(&target, t, sizeof(t));
171 result = dns_name_totext(name, ISC_TRUE, &target);
172 if (result == ISC_R_SUCCESS) {
173 isc_buffer_usedregion(&target, &r);
174 printf("%.*s", (int)r.length, (char *)r.base);
175 } else
176 printf("(invalid name)");
178 return (result);
181 static isc_result_t
182 print_address(FILE *fp, isc_sockaddr_t *addr) {
183 char buf[NI_MAXHOST];
185 if (getnameinfo(&addr->type.sa, addr->length, buf, sizeof(buf),
186 NULL, 0, NI_NUMERICHOST) == 0) {
187 fprintf(fp, "%s", buf);
188 } else {
189 fprintf(fp, "(invalid address)");
192 return (ISC_R_SUCCESS);
195 static void
196 ctxs_destroy(isc_mem_t **mctxp, isc_appctx_t **actxp,
197 isc_taskmgr_t **taskmgrp, isc_socketmgr_t **socketmgrp,
198 isc_timermgr_t **timermgrp)
200 if (*taskmgrp != NULL)
201 isc_taskmgr_destroy(taskmgrp);
203 if (*timermgrp != NULL)
204 isc_timermgr_destroy(timermgrp);
206 if (*socketmgrp != NULL)
207 isc_socketmgr_destroy(socketmgrp);
209 if (*actxp != NULL)
210 isc_appctx_destroy(actxp);
212 if (*mctxp != NULL)
213 isc_mem_destroy(mctxp);
216 static isc_result_t
217 ctxs_init(isc_mem_t **mctxp, isc_appctx_t **actxp,
218 isc_taskmgr_t **taskmgrp, isc_socketmgr_t **socketmgrp,
219 isc_timermgr_t **timermgrp)
221 isc_result_t result;
223 result = isc_mem_create(0, 0, mctxp);
224 if (result != ISC_R_SUCCESS)
225 goto fail;
227 result = isc_appctx_create(*mctxp, actxp);
228 if (result != ISC_R_SUCCESS)
229 goto fail;
231 result = isc_taskmgr_createinctx(*mctxp, *actxp, 1, 0, taskmgrp);
232 if (result != ISC_R_SUCCESS)
233 goto fail;
235 result = isc_socketmgr_createinctx(*mctxp, *actxp, socketmgrp);
236 if (result != ISC_R_SUCCESS)
237 goto fail;
239 result = isc_timermgr_createinctx(*mctxp, *actxp, timermgrp);
240 if (result != ISC_R_SUCCESS)
241 goto fail;
243 return (ISC_R_SUCCESS);
245 fail:
246 ctxs_destroy(mctxp, actxp, taskmgrp, socketmgrp, timermgrp);
248 return (result);
252 * Common routine to make query data
254 static isc_result_t
255 make_querymessage(dns_message_t *message, dns_name_t *qname0,
256 dns_rdatatype_t rdtype)
258 dns_name_t *qname = NULL;
259 dns_rdataset_t *qrdataset = NULL;
260 isc_result_t result;
262 message->opcode = dns_opcode_query;
263 message->rdclass = dns_rdataclass_in;
265 result = dns_message_gettempname(message, &qname);
266 if (result != ISC_R_SUCCESS)
267 goto cleanup;
269 result = dns_message_gettemprdataset(message, &qrdataset);
270 if (result != ISC_R_SUCCESS)
271 goto cleanup;
273 dns_name_init(qname, NULL);
274 dns_name_clone(qname0, qname);
275 dns_rdataset_init(qrdataset);
276 dns_rdataset_makequestion(qrdataset, message->rdclass, rdtype);
277 ISC_LIST_APPEND(qname->list, qrdataset, link);
278 dns_message_addname(message, qname, DNS_SECTION_QUESTION);
280 return (ISC_R_SUCCESS);
282 cleanup:
283 if (qname != NULL)
284 dns_message_puttempname(message, &qname);
285 if (qrdataset != NULL)
286 dns_message_puttemprdataset(message, &qrdataset);
287 if (message != NULL)
288 dns_message_destroy(&message);
289 return (result);
293 * Update statistics
295 static inline void
296 increment_entry(unsigned long *entryp) {
297 (*entryp)++;
298 INSIST(*entryp != 0); /* check overflow */
301 static void
302 update_stat(struct probe_trans *trans) {
303 struct probe_ns *pns;
304 struct server *server;
305 struct stat local_stat;
306 unsigned int err_count = 0;
307 const char *stattype;
309 increment_entry(&number_of_domains);
310 memset(&local_stat, 0, sizeof(local_stat));
312 /* Update per sever statistics */
313 for (pns = ISC_LIST_HEAD(trans->nslist); pns != NULL;
314 pns = ISC_LIST_NEXT(pns, link)) {
315 for (server = ISC_LIST_HEAD(pns->servers); server != NULL;
316 server = ISC_LIST_NEXT(server, link)) {
317 increment_entry(&number_of_servers);
319 if (server->result_aaaa == exist ||
320 server->result_aaaa == notype) {
322 * Don't care about the result of A query if
323 * the answer to AAAA query was expected.
325 stattype = "valid";
326 increment_entry(&server_stat.valid);
327 increment_entry(&local_stat.valid);
328 } else if (server->result_a == exist) {
329 switch (server->result_aaaa) {
330 case exist:
331 case notype:
332 stattype = "valid";
333 increment_entry(&server_stat.valid);
334 increment_entry(&local_stat.valid);
335 break;
336 case timedout:
337 stattype = "ignore";
338 increment_entry(&server_stat.ignore);
339 increment_entry(&local_stat.ignore);
340 break;
341 case nxdomain:
342 stattype = "nxdomain";
343 increment_entry(&server_stat.nxdomain);
344 increment_entry(&local_stat.nxdomain);
345 break;
346 case othererr:
347 stattype = "othererr";
348 increment_entry(&server_stat.othererr);
349 increment_entry(&local_stat.othererr);
350 break;
351 case multiplesoa:
352 stattype = "multiplesoa";
353 increment_entry(&server_stat.multiplesoa);
354 increment_entry(&local_stat.multiplesoa);
355 break;
356 case multiplecname:
357 stattype = "multiplecname";
358 increment_entry(&server_stat.multiplecname);
359 increment_entry(&local_stat.multiplecname);
360 break;
361 case brokenanswer:
362 stattype = "brokenanswer";
363 increment_entry(&server_stat.brokenanswer);
364 increment_entry(&local_stat.brokenanswer);
365 break;
366 case lame:
367 stattype = "lame";
368 increment_entry(&server_stat.lame);
369 increment_entry(&local_stat.lame);
370 break;
371 default:
372 stattype = "unknown";
373 increment_entry(&server_stat.unknown);
374 increment_entry(&local_stat.unknown);
375 break;
377 } else {
378 stattype = "unknown";
379 increment_entry(&server_stat.unknown);
380 increment_entry(&local_stat.unknown);
383 if (verbose_level > 1 ||
384 (verbose_level == 1 &&
385 strcmp(stattype, "valid") != 0 &&
386 strcmp(stattype, "unknown") != 0)) {
387 print_name(pns->name);
388 putchar('(');
389 print_address(stdout, &server->address);
390 printf(") for %s:%s\n", trans->domain,
391 stattype);
396 /* Update per domain statistics */
397 if (local_stat.ignore > 0) {
398 if (verbose_level > 0)
399 printf("%s:ignore\n", trans->domain);
400 increment_entry(&domain_stat.ignore);
401 err_count++;
403 if (local_stat.nxdomain > 0) {
404 if (verbose_level > 0)
405 printf("%s:nxdomain\n", trans->domain);
406 increment_entry(&domain_stat.nxdomain);
407 err_count++;
409 if (local_stat.othererr > 0) {
410 if (verbose_level > 0)
411 printf("%s:othererr\n", trans->domain);
412 increment_entry(&domain_stat.othererr);
413 err_count++;
415 if (local_stat.multiplesoa > 0) {
416 if (verbose_level > 0)
417 printf("%s:multiplesoa\n", trans->domain);
418 increment_entry(&domain_stat.multiplesoa);
419 err_count++;
421 if (local_stat.multiplecname > 0) {
422 if (verbose_level > 0)
423 printf("%s:multiplecname\n", trans->domain);
424 increment_entry(&domain_stat.multiplecname);
425 err_count++;
427 if (local_stat.brokenanswer > 0) {
428 if (verbose_level > 0)
429 printf("%s:brokenanswer\n", trans->domain);
430 increment_entry(&domain_stat.brokenanswer);
431 err_count++;
433 if (local_stat.lame > 0) {
434 if (verbose_level > 0)
435 printf("%s:lame\n", trans->domain);
436 increment_entry(&domain_stat.lame);
437 err_count++;
440 if (err_count > 1)
441 increment_entry(&multiple_error_domains);
444 * We regard the domain as valid if and only if no authoritative server
445 * has a problem and at least one server is known to be valid.
447 if (local_stat.valid > 0 && err_count == 0) {
448 if (verbose_level > 1)
449 printf("%s:valid\n", trans->domain);
450 increment_entry(&domain_stat.valid);
454 * If the domain has no available server or all servers have the
455 * 'unknown' result, the domain's result is also regarded as unknown.
457 if (local_stat.valid == 0 && err_count == 0) {
458 if (verbose_level > 1)
459 printf("%s:unknown\n", trans->domain);
460 increment_entry(&domain_stat.unknown);
465 * Search for an existent name with an A RR
468 static isc_result_t
469 set_nextqname(struct probe_trans *trans) {
470 isc_result_t result;
471 size_t domainlen;
472 isc_buffer_t b;
473 char buf[4096]; /* XXX ad-hoc constant, but should be enough */
475 if (*trans->qlabel == NULL)
476 return (ISC_R_NOMORE);
478 result = isc_string_copy(buf, sizeof(buf), *trans->qlabel);
479 if (result != ISC_R_SUCCESS)
480 return (result);
481 result = isc_string_append(buf, sizeof(buf), trans->domain);
482 if (result != ISC_R_SUCCESS)
483 return (result);
485 domainlen = strlen(buf);
486 isc_buffer_init(&b, buf, domainlen);
487 isc_buffer_add(&b, domainlen);
488 dns_fixedname_init(&trans->fixedname);
489 trans->qname = dns_fixedname_name(&trans->fixedname);
490 result = dns_name_fromtext(trans->qname, &b, dns_rootname,
491 0, NULL);
493 trans->qlabel++;
495 return (result);
498 static void
499 request_done(isc_task_t *task, isc_event_t *event) {
500 struct probe_trans *trans = event->ev_arg;
501 dns_clientreqevent_t *rev = (dns_clientreqevent_t *)event;
502 dns_message_t *rmessage;
503 struct probe_ns *pns;
504 struct server *server;
505 isc_result_t result;
506 query_result_t *resultp;
507 dns_name_t *name;
508 dns_rdataset_t *rdataset;
509 dns_rdatatype_t type;
511 REQUIRE(task == probe_task);
512 REQUIRE(trans != NULL && trans->inuse == ISC_TRUE);
513 rmessage = rev->rmessage;
514 REQUIRE(rmessage == trans->rmessage);
515 INSIST(outstanding_probes > 0);
517 server = trans->current_ns->current_server;
518 INSIST(server != NULL);
520 if (server->result_a == none) {
521 type = dns_rdatatype_a;
522 resultp = &server->result_a;
523 } else {
524 resultp = &server->result_aaaa;
525 type = dns_rdatatype_aaaa;
528 if (rev->result == ISC_R_SUCCESS) {
529 if ((rmessage->flags & DNS_MESSAGEFLAG_AA) == 0)
530 *resultp = lame;
531 else if (rmessage->rcode == dns_rcode_nxdomain)
532 *resultp = nxdomain;
533 else if (rmessage->rcode != dns_rcode_noerror)
534 *resultp = othererr;
535 else if (rmessage->counts[DNS_SECTION_ANSWER] == 0) {
536 /* no error but empty answer */
537 *resultp = notype;
538 } else {
539 result = dns_message_firstname(rmessage,
540 DNS_SECTION_ANSWER);
541 while (result == ISC_R_SUCCESS) {
542 name = NULL;
543 dns_message_currentname(rmessage,
544 DNS_SECTION_ANSWER,
545 &name);
546 for (rdataset = ISC_LIST_HEAD(name->list);
547 rdataset != NULL;
548 rdataset = ISC_LIST_NEXT(rdataset,
549 link)) {
550 (void)print_rdataset(rdataset, name);
552 if (rdataset->type ==
553 dns_rdatatype_cname ||
554 rdataset->type ==
555 dns_rdatatype_dname) {
556 /* Should chase the chain? */
557 *resultp = exist;
558 goto found;
559 } else if (rdataset->type == type) {
560 *resultp = exist;
561 goto found;
564 result = dns_message_nextname(rmessage,
565 DNS_SECTION_ANSWER);
569 * Something unexpected happened: the response
570 * contained a non-empty authoritative answer, but we
571 * could not find an expected result.
573 *resultp = unexpected;
575 } else if (rev->result == DNS_R_RECOVERABLE ||
576 rev->result == DNS_R_BADLABELTYPE) {
577 /* Broken response. Try identifying known cases. */
578 *resultp = brokenanswer;
580 if (rmessage->counts[DNS_SECTION_ANSWER] > 0) {
581 result = dns_message_firstname(rmessage,
582 DNS_SECTION_ANSWER);
583 while (result == ISC_R_SUCCESS) {
585 * Check to see if the response has multiple
586 * CNAME RRs. Update the result code if so.
588 name = NULL;
589 dns_message_currentname(rmessage,
590 DNS_SECTION_ANSWER,
591 &name);
592 for (rdataset = ISC_LIST_HEAD(name->list);
593 rdataset != NULL;
594 rdataset = ISC_LIST_NEXT(rdataset,
595 link)) {
596 if (rdataset->type ==
597 dns_rdatatype_cname &&
598 dns_rdataset_count(rdataset) > 1) {
599 *resultp = multiplecname;
600 goto found;
603 result = dns_message_nextname(rmessage,
604 DNS_SECTION_ANSWER);
608 if (rmessage->counts[DNS_SECTION_AUTHORITY] > 0) {
609 result = dns_message_firstname(rmessage,
610 DNS_SECTION_AUTHORITY);
611 while (result == ISC_R_SUCCESS) {
613 * Check to see if the response has multiple
614 * SOA RRs. Update the result code if so.
616 name = NULL;
617 dns_message_currentname(rmessage,
618 DNS_SECTION_AUTHORITY,
619 &name);
620 for (rdataset = ISC_LIST_HEAD(name->list);
621 rdataset != NULL;
622 rdataset = ISC_LIST_NEXT(rdataset,
623 link)) {
624 if (rdataset->type ==
625 dns_rdatatype_soa &&
626 dns_rdataset_count(rdataset) > 1) {
627 *resultp = multiplesoa;
628 goto found;
631 result = dns_message_nextname(rmessage,
632 DNS_SECTION_AUTHORITY);
635 } else if (rev->result == ISC_R_TIMEDOUT)
636 *resultp = timedout;
637 else {
638 fprintf(stderr, "unexpected result: %d (domain=%s, server=",
639 rev->result, trans->domain);
640 print_address(stderr, &server->address);
641 fputc('\n', stderr);
642 *resultp = unexpected;
645 found:
646 INSIST(*resultp != none);
647 if (type == dns_rdatatype_a && *resultp == exist)
648 trans->qname_found = ISC_TRUE;
650 dns_client_destroyreqtrans(&trans->reqid);
651 isc_event_free(&event);
652 dns_message_reset(trans->rmessage, DNS_MESSAGE_INTENTPARSE);
654 result = probe_name(trans, type);
655 if (result == ISC_R_NOMORE) {
656 /* We've tried all addresses of all servers. */
657 if (type == dns_rdatatype_a && trans->qname_found) {
659 * If we've explored A RRs and found an existent
660 * record, we can move to AAAA.
662 trans->current_ns = ISC_LIST_HEAD(trans->nslist);
663 probe_name(trans, dns_rdatatype_aaaa);
664 result = ISC_R_SUCCESS;
665 } else if (type == dns_rdatatype_a) {
667 * No server provided an existent A RR of this name.
668 * Try next label.
670 dns_fixedname_invalidate(&trans->fixedname);
671 trans->qname = NULL;
672 result = set_nextqname(trans);
673 if (result == ISC_R_SUCCESS) {
674 trans->current_ns =
675 ISC_LIST_HEAD(trans->nslist);
676 for (pns = trans->current_ns; pns != NULL;
677 pns = ISC_LIST_NEXT(pns, link)) {
678 for (server = ISC_LIST_HEAD(pns->servers);
679 server != NULL;
680 server = ISC_LIST_NEXT(server,
681 link)) {
682 INSIST(server->result_aaaa ==
683 none);
684 server->result_a = none;
687 result = probe_name(trans, dns_rdatatype_a);
690 if (result != ISC_R_SUCCESS) {
692 * We've explored AAAA RRs or failed to find a valid
693 * query label. Wrap up the result and move to the
694 * next domain.
696 reset_probe(trans);
698 } else if (result != ISC_R_SUCCESS)
699 reset_probe(trans); /* XXX */
702 static isc_result_t
703 probe_name(struct probe_trans *trans, dns_rdatatype_t type) {
704 isc_result_t result;
705 struct probe_ns *pns;
706 struct server *server;
708 REQUIRE(trans->reqid == NULL);
709 REQUIRE(type == dns_rdatatype_a || type == dns_rdatatype_aaaa);
711 for (pns = trans->current_ns; pns != NULL;
712 pns = ISC_LIST_NEXT(pns, link)) {
713 for (server = ISC_LIST_HEAD(pns->servers); server != NULL;
714 server = ISC_LIST_NEXT(server, link)) {
715 if ((type == dns_rdatatype_a &&
716 server->result_a == none) ||
717 (type == dns_rdatatype_aaaa &&
718 server->result_aaaa == none)) {
719 pns->current_server = server;
720 goto found;
725 found:
726 trans->current_ns = pns;
727 if (pns == NULL)
728 return (ISC_R_NOMORE);
730 INSIST(pns->current_server != NULL);
731 dns_message_reset(trans->qmessage, DNS_MESSAGE_INTENTRENDER);
732 result = make_querymessage(trans->qmessage, trans->qname, type);
733 if (result != ISC_R_SUCCESS)
734 return (result);
735 result = dns_client_startrequest(client, trans->qmessage,
736 trans->rmessage,
737 &pns->current_server->address,
738 0, DNS_MESSAGEPARSE_BESTEFFORT,
739 NULL, 120, 0, 4,
740 probe_task, request_done, trans,
741 &trans->reqid);
743 return (result);
747 * Get IP addresses of NSes
750 static void
751 resolve_nsaddress(isc_task_t *task, isc_event_t *event) {
752 struct probe_trans *trans = event->ev_arg;
753 dns_clientresevent_t *rev = (dns_clientresevent_t *)event;
754 dns_name_t *name;
755 dns_rdataset_t *rdataset;
756 dns_rdata_t rdata = DNS_RDATA_INIT;
757 struct probe_ns *pns = trans->current_ns;
758 isc_result_t result;
760 REQUIRE(task == probe_task);
761 REQUIRE(trans->inuse == ISC_TRUE);
762 REQUIRE(pns != NULL);
763 INSIST(outstanding_probes > 0);
765 for (name = ISC_LIST_HEAD(rev->answerlist); name != NULL;
766 name = ISC_LIST_NEXT(name, link)) {
767 for (rdataset = ISC_LIST_HEAD(name->list);
768 rdataset != NULL;
769 rdataset = ISC_LIST_NEXT(rdataset, link)) {
770 (void)print_rdataset(rdataset, name);
772 if (rdataset->type != dns_rdatatype_a)
773 continue;
775 for (result = dns_rdataset_first(rdataset);
776 result == ISC_R_SUCCESS;
777 result = dns_rdataset_next(rdataset)) {
778 dns_rdata_in_a_t rdata_a;
779 struct server *server;
781 dns_rdataset_current(rdataset, &rdata);
782 result = dns_rdata_tostruct(&rdata, &rdata_a,
783 NULL);
784 if (result != ISC_R_SUCCESS)
785 continue;
787 server = isc_mem_get(mctx, sizeof(*server));
788 if (server == NULL) {
789 fprintf(stderr, "resolve_nsaddress: "
790 "mem_get failed");
791 result = ISC_R_NOMEMORY;
792 goto cleanup;
794 isc_sockaddr_fromin(&server->address,
795 &rdata_a.in_addr, 53);
796 ISC_LINK_INIT(server, link);
797 server->result_a = none;
798 server->result_aaaa = none;
799 ISC_LIST_APPEND(pns->servers, server, link);
804 cleanup:
805 dns_client_freeresanswer(client, &rev->answerlist);
806 dns_client_destroyrestrans(&trans->resid);
807 isc_event_free(&event);
809 next_ns:
810 trans->current_ns = ISC_LIST_NEXT(pns, link);
811 if (trans->current_ns == NULL) {
812 trans->current_ns = ISC_LIST_HEAD(trans->nslist);
813 dns_fixedname_invalidate(&trans->fixedname);
814 trans->qname = NULL;
815 result = set_nextqname(trans);
816 if (result == ISC_R_SUCCESS)
817 result = probe_name(trans, dns_rdatatype_a);
818 } else {
819 result = fetch_nsaddress(trans);
820 if (result != ISC_R_SUCCESS)
821 goto next_ns; /* XXX: this is unlikely to succeed */
824 if (result != ISC_R_SUCCESS)
825 reset_probe(trans);
828 static isc_result_t
829 fetch_nsaddress(struct probe_trans *trans) {
830 struct probe_ns *pns;
832 pns = trans->current_ns;
833 REQUIRE(pns != NULL);
835 return (dns_client_startresolve(client, pns->name, dns_rdataclass_in,
836 dns_rdatatype_a, 0, probe_task,
837 resolve_nsaddress, trans,
838 &trans->resid));
842 * Get NS RRset for a given domain
845 static void
846 reset_probe(struct probe_trans *trans) {
847 struct probe_ns *pns;
848 struct server *server;
849 isc_result_t result;
851 REQUIRE(trans->resid == NULL);
852 REQUIRE(trans->reqid == NULL);
854 update_stat(trans);
856 dns_message_reset(trans->qmessage, DNS_MESSAGE_INTENTRENDER);
857 dns_message_reset(trans->rmessage, DNS_MESSAGE_INTENTPARSE);
859 trans->inuse = ISC_FALSE;
860 if (trans->domain != NULL)
861 isc_mem_free(mctx, trans->domain);
862 trans->domain = NULL;
863 if (trans->qname != NULL)
864 dns_fixedname_invalidate(&trans->fixedname);
865 trans->qname = NULL;
866 trans->qlabel = qlabels;
867 trans->qname_found = ISC_FALSE;
868 trans->current_ns = NULL;
870 while ((pns = ISC_LIST_HEAD(trans->nslist)) != NULL) {
871 ISC_LIST_UNLINK(trans->nslist, pns, link);
872 while ((server = ISC_LIST_HEAD(pns->servers)) != NULL) {
873 ISC_LIST_UNLINK(pns->servers, server, link);
874 isc_mem_put(mctx, server, sizeof(*server));
876 isc_mem_put(mctx, pns, sizeof(*pns));
879 outstanding_probes--;
881 result = probe_domain(trans);
882 if (result == ISC_R_NOMORE && outstanding_probes == 0)
883 isc_app_ctxshutdown(actx);
886 static void
887 resolve_ns(isc_task_t *task, isc_event_t *event) {
888 struct probe_trans *trans = event->ev_arg;
889 dns_clientresevent_t *rev = (dns_clientresevent_t *)event;
890 dns_name_t *name;
891 dns_rdataset_t *rdataset;
892 isc_result_t result = ISC_R_SUCCESS;
893 dns_rdata_t rdata = DNS_RDATA_INIT;
894 struct probe_ns *pns;
896 REQUIRE(task == probe_task);
897 REQUIRE(trans->inuse == ISC_TRUE);
898 INSIST(outstanding_probes > 0);
900 for (name = ISC_LIST_HEAD(rev->answerlist); name != NULL;
901 name = ISC_LIST_NEXT(name, link)) {
902 for (rdataset = ISC_LIST_HEAD(name->list);
903 rdataset != NULL;
904 rdataset = ISC_LIST_NEXT(rdataset, link)) {
905 (void)print_rdataset(rdataset, name);
907 if (rdataset->type != dns_rdatatype_ns)
908 continue;
910 for (result = dns_rdataset_first(rdataset);
911 result == ISC_R_SUCCESS;
912 result = dns_rdataset_next(rdataset)) {
913 dns_rdata_ns_t ns;
915 dns_rdataset_current(rdataset, &rdata);
917 * Extract the name from the NS record.
919 result = dns_rdata_tostruct(&rdata, &ns, NULL);
920 if (result != ISC_R_SUCCESS)
921 continue;
923 pns = isc_mem_get(mctx, sizeof(*pns));
924 if (pns == NULL) {
925 fprintf(stderr,
926 "resolve_ns: mem_get failed");
927 result = ISC_R_NOMEMORY;
929 * XXX: should we continue with the
930 * available servers anyway?
932 goto cleanup;
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);
942 dns_name_copy(&ns.name, pns->name, NULL);
943 dns_rdata_reset(&rdata);
944 dns_rdata_freestruct(&ns);
949 cleanup:
950 dns_client_freeresanswer(client, &rev->answerlist);
951 dns_client_destroyrestrans(&trans->resid);
952 isc_event_free(&event);
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;
961 if (result == ISC_R_SUCCESS)
962 return;
964 reset_probe(trans);
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;
975 REQUIRE(trans != NULL);
976 REQUIRE(trans->inuse == ISC_FALSE);
977 REQUIRE(outstanding_probes < MAX_PROBES);
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);
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;
1008 trans->inuse = ISC_TRUE;
1009 outstanding_probes++;
1011 return (ISC_R_SUCCESS);
1013 cleanup:
1014 isc_mem_free(mctx, trans->domain);
1015 dns_fixedname_invalidate(&trans->fixedname);
1017 return (result);
1020 ISC_PLATFORM_NORETURN_PRE static void
1021 usage(void) ISC_PLATFORM_NORETURN_POST;
1023 static void
1024 usage(void) {
1025 fprintf(stderr, "usage: nsprobe [-d] [-v [-v...]] [-c cache_address] "
1026 "[input_file]\n");
1028 exit(1);
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;
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;
1062 argc -= optind;
1063 argv += optind;
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);
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);
1080 isc_app_ctxstart(actx);
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);
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);
1100 if (res->ai_addrlen > sizeof(sa.type)) {
1101 fprintf(stderr,
1102 "assumption failure: addrlen is too long: %d\n",
1103 res->ai_addrlen);
1104 exit(1);
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);
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);
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);
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;
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);
1159 if (result != ISC_R_SUCCESS) {
1160 fprintf(stderr, "initialization failure\n");
1161 exit(1);
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);
1174 /* Start event loop */
1175 isc_app_ctxrun(actx);
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);
1210 /* Cleanup */
1211 for (i = 0; i < MAX_PROBES; i++) {
1212 dns_message_destroy(&probes[i].qmessage);
1213 dns_message_destroy(&probes[i].rmessage);
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);
1221 exit(0);