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 */
23 #include <sys/types.h>
24 #include <sys/socket.h>
33 #include <isc/buffer.h>
36 #include <isc/socket.h>
37 #include <isc/sockaddr.h>
38 #include <isc/string.h>
40 #include <isc/timer.h>
43 #include <dns/client.h>
44 #include <dns/fixedname.h>
46 #include <dns/message.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";
79 ISC_LINK(struct server
) link
;
81 isc_sockaddr_t address
;
82 query_result_t result_a
;
83 query_result_t result_aaaa
;
87 ISC_LINK(struct probe_ns
) link
;
89 dns_fixedname_t fixedname
;
91 struct server
*current_server
;
92 ISC_LIST(struct server
) servers
;
98 dns_fixedname_t fixedname
;
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
;
108 struct probe_ns
*current_ns
;
109 ISC_LIST(struct probe_ns
) nslist
;
114 unsigned long ignore
;
115 unsigned long nxdomain
;
116 unsigned long othererr
;
117 unsigned long multiplesoa
;
118 unsigned long multiplecname
;
119 unsigned long brokenanswer
;
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 */
140 print_rdataset(dns_rdataset_t
*rdataset
, dns_name_t
*owner
) {
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
,
155 if (result
!= ISC_R_SUCCESS
)
157 isc_buffer_usedregion(&target
, &r
);
158 printf("%.*s", (int)r
.length
, (char *)r
.base
);
160 return (ISC_R_SUCCESS
);
164 print_name(dns_name_t
*name
) {
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
);
176 printf("(invalid name)");
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
);
189 fprintf(fp
, "(invalid address)");
192 return (ISC_R_SUCCESS
);
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
);
210 isc_appctx_destroy(actxp
);
213 isc_mem_destroy(mctxp
);
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
)
223 result
= isc_mem_create(0, 0, mctxp
);
224 if (result
!= ISC_R_SUCCESS
)
227 result
= isc_appctx_create(*mctxp
, actxp
);
228 if (result
!= ISC_R_SUCCESS
)
231 result
= isc_taskmgr_createinctx(*mctxp
, *actxp
, 1, 0, taskmgrp
);
232 if (result
!= ISC_R_SUCCESS
)
235 result
= isc_socketmgr_createinctx(*mctxp
, *actxp
, socketmgrp
);
236 if (result
!= ISC_R_SUCCESS
)
239 result
= isc_timermgr_createinctx(*mctxp
, *actxp
, timermgrp
);
240 if (result
!= ISC_R_SUCCESS
)
243 return (ISC_R_SUCCESS
);
246 ctxs_destroy(mctxp
, actxp
, taskmgrp
, socketmgrp
, timermgrp
);
252 * Common routine to make query data
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
;
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
)
269 result
= dns_message_gettemprdataset(message
, &qrdataset
);
270 if (result
!= ISC_R_SUCCESS
)
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
);
284 dns_message_puttempname(message
, &qname
);
285 if (qrdataset
!= NULL
)
286 dns_message_puttemprdataset(message
, &qrdataset
);
288 dns_message_destroy(&message
);
296 increment_entry(unsigned long *entryp
) {
298 INSIST(*entryp
!= 0); /* check overflow */
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.
326 increment_entry(&server_stat
.valid
);
327 increment_entry(&local_stat
.valid
);
328 } else if (server
->result_a
== exist
) {
329 switch (server
->result_aaaa
) {
333 increment_entry(&server_stat
.valid
);
334 increment_entry(&local_stat
.valid
);
338 increment_entry(&server_stat
.ignore
);
339 increment_entry(&local_stat
.ignore
);
342 stattype
= "nxdomain";
343 increment_entry(&server_stat
.nxdomain
);
344 increment_entry(&local_stat
.nxdomain
);
347 stattype
= "othererr";
348 increment_entry(&server_stat
.othererr
);
349 increment_entry(&local_stat
.othererr
);
352 stattype
= "multiplesoa";
353 increment_entry(&server_stat
.multiplesoa
);
354 increment_entry(&local_stat
.multiplesoa
);
357 stattype
= "multiplecname";
358 increment_entry(&server_stat
.multiplecname
);
359 increment_entry(&local_stat
.multiplecname
);
362 stattype
= "brokenanswer";
363 increment_entry(&server_stat
.brokenanswer
);
364 increment_entry(&local_stat
.brokenanswer
);
368 increment_entry(&server_stat
.lame
);
369 increment_entry(&local_stat
.lame
);
372 stattype
= "unknown";
373 increment_entry(&server_stat
.unknown
);
374 increment_entry(&local_stat
.unknown
);
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
);
389 print_address(stdout
, &server
->address
);
390 printf(") for %s:%s\n", trans
->domain
,
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
);
403 if (local_stat
.nxdomain
> 0) {
404 if (verbose_level
> 0)
405 printf("%s:nxdomain\n", trans
->domain
);
406 increment_entry(&domain_stat
.nxdomain
);
409 if (local_stat
.othererr
> 0) {
410 if (verbose_level
> 0)
411 printf("%s:othererr\n", trans
->domain
);
412 increment_entry(&domain_stat
.othererr
);
415 if (local_stat
.multiplesoa
> 0) {
416 if (verbose_level
> 0)
417 printf("%s:multiplesoa\n", trans
->domain
);
418 increment_entry(&domain_stat
.multiplesoa
);
421 if (local_stat
.multiplecname
> 0) {
422 if (verbose_level
> 0)
423 printf("%s:multiplecname\n", trans
->domain
);
424 increment_entry(&domain_stat
.multiplecname
);
427 if (local_stat
.brokenanswer
> 0) {
428 if (verbose_level
> 0)
429 printf("%s:brokenanswer\n", trans
->domain
);
430 increment_entry(&domain_stat
.brokenanswer
);
433 if (local_stat
.lame
> 0) {
434 if (verbose_level
> 0)
435 printf("%s:lame\n", trans
->domain
);
436 increment_entry(&domain_stat
.lame
);
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
469 set_nextqname(struct probe_trans
*trans
) {
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
)
481 result
= isc_string_append(buf
, sizeof(buf
), trans
->domain
);
482 if (result
!= ISC_R_SUCCESS
)
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
,
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
;
506 query_result_t
*resultp
;
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
;
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)
531 else if (rmessage
->rcode
== dns_rcode_nxdomain
)
533 else if (rmessage
->rcode
!= dns_rcode_noerror
)
535 else if (rmessage
->counts
[DNS_SECTION_ANSWER
] == 0) {
536 /* no error but empty answer */
539 result
= dns_message_firstname(rmessage
,
541 while (result
== ISC_R_SUCCESS
) {
543 dns_message_currentname(rmessage
,
546 for (rdataset
= ISC_LIST_HEAD(name
->list
);
548 rdataset
= ISC_LIST_NEXT(rdataset
,
550 (void)print_rdataset(rdataset
, name
);
552 if (rdataset
->type
==
553 dns_rdatatype_cname
||
555 dns_rdatatype_dname
) {
556 /* Should chase the chain? */
559 } else if (rdataset
->type
== type
) {
564 result
= dns_message_nextname(rmessage
,
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
,
583 while (result
== ISC_R_SUCCESS
) {
585 * Check to see if the response has multiple
586 * CNAME RRs. Update the result code if so.
589 dns_message_currentname(rmessage
,
592 for (rdataset
= ISC_LIST_HEAD(name
->list
);
594 rdataset
= ISC_LIST_NEXT(rdataset
,
596 if (rdataset
->type
==
597 dns_rdatatype_cname
&&
598 dns_rdataset_count(rdataset
) > 1) {
599 *resultp
= multiplecname
;
603 result
= dns_message_nextname(rmessage
,
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.
617 dns_message_currentname(rmessage
,
618 DNS_SECTION_AUTHORITY
,
620 for (rdataset
= ISC_LIST_HEAD(name
->list
);
622 rdataset
= ISC_LIST_NEXT(rdataset
,
624 if (rdataset
->type
==
626 dns_rdataset_count(rdataset
) > 1) {
627 *resultp
= multiplesoa
;
631 result
= dns_message_nextname(rmessage
,
632 DNS_SECTION_AUTHORITY
);
635 } else if (rev
->result
== ISC_R_TIMEDOUT
)
638 fprintf(stderr
, "unexpected result: %d (domain=%s, server=",
639 rev
->result
, trans
->domain
);
640 print_address(stderr
, &server
->address
);
642 *resultp
= unexpected
;
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.
670 dns_fixedname_invalidate(&trans
->fixedname
);
672 result
= set_nextqname(trans
);
673 if (result
== ISC_R_SUCCESS
) {
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
);
680 server
= ISC_LIST_NEXT(server
,
682 INSIST(server
->result_aaaa
==
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
698 } else if (result
!= ISC_R_SUCCESS
)
699 reset_probe(trans
); /* XXX */
703 probe_name(struct probe_trans
*trans
, dns_rdatatype_t type
) {
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
;
726 trans
->current_ns
= pns
;
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
)
735 result
= dns_client_startrequest(client
, trans
->qmessage
,
737 &pns
->current_server
->address
,
738 0, DNS_MESSAGEPARSE_BESTEFFORT
,
740 probe_task
, request_done
, trans
,
747 * Get IP addresses of NSes
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
;
755 dns_rdataset_t
*rdataset
;
756 dns_rdata_t rdata
= DNS_RDATA_INIT
;
757 struct probe_ns
*pns
= trans
->current_ns
;
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
);
769 rdataset
= ISC_LIST_NEXT(rdataset
, link
)) {
770 (void)print_rdataset(rdataset
, name
);
772 if (rdataset
->type
!= dns_rdatatype_a
)
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
,
784 if (result
!= ISC_R_SUCCESS
)
787 server
= isc_mem_get(mctx
, sizeof(*server
));
788 if (server
== NULL
) {
789 fprintf(stderr
, "resolve_nsaddress: "
791 result
= ISC_R_NOMEMORY
;
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
);
805 dns_client_freeresanswer(client
, &rev
->answerlist
);
806 dns_client_destroyrestrans(&trans
->resid
);
807 isc_event_free(&event
);
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
);
815 result
= set_nextqname(trans
);
816 if (result
== ISC_R_SUCCESS
)
817 result
= probe_name(trans
, dns_rdatatype_a
);
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
)
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
,
842 * Get NS RRset for a given domain
846 reset_probe(struct probe_trans
*trans
) {
847 struct probe_ns
*pns
;
848 struct server
*server
;
851 REQUIRE(trans
->resid
== NULL
);
852 REQUIRE(trans
->reqid
== NULL
);
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
);
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
);
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
;
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
);
904 rdataset
= ISC_LIST_NEXT(rdataset
, link
)) {
905 (void)print_rdataset(rdataset
, name
);
907 if (rdataset
->type
!= dns_rdatatype_ns
)
910 for (result
= dns_rdataset_first(rdataset
);
911 result
== ISC_R_SUCCESS
;
912 result
= dns_rdataset_next(rdataset
)) {
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
)
923 pns
= isc_mem_get(mctx
, sizeof(*pns
));
926 "resolve_ns: mem_get failed");
927 result
= ISC_R_NOMEMORY
;
929 * XXX: should we continue with the
930 * available servers anyway?
935 dns_fixedname_init(&pns
->fixedname
);
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
);
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
);
959 result
= ISC_R_FAILURE
;
961 if (result
== ISC_R_SUCCESS
)
968 probe_domain(struct probe_trans
*trans
) {
972 char buf
[4096]; /* XXX ad hoc constant, but should be enough */
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
);
982 return (ISC_R_NOMORE
);
983 if ((cp
= strchr(buf
, '\n')) != NULL
) /* zap NL if any */
985 trans
->domain
= isc_mem_strdup(mctx
, buf
);
986 if (trans
->domain
== NULL
) {
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
)
1001 result
= dns_client_startresolve(client
, trans
->qname
,
1002 dns_rdataclass_in
, dns_rdatatype_ns
,
1003 0, probe_task
, resolve_ns
, trans
,
1005 if (result
!= ISC_R_SUCCESS
)
1008 trans
->inuse
= ISC_TRUE
;
1009 outstanding_probes
++;
1011 return (ISC_R_SUCCESS
);
1014 isc_mem_free(mctx
, trans
->domain
);
1015 dns_fixedname_invalidate(&trans
->fixedname
);
1020 ISC_PLATFORM_NORETURN_PRE
static void
1021 usage(void) ISC_PLATFORM_NORETURN_POST
;
1025 fprintf(stderr
, "usage: nsprobe [-d] [-v [-v...]] [-c cache_address] "
1032 main(int argc
, char *argv
[]) {
1034 struct addrinfo hints
, *res
;
1035 isc_result_t result
;
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) {
1045 cacheserver
= optarg
;
1048 debug_mode
= ISC_TRUE
;
1067 result
= dns_lib_init();
1068 if (result
!= ISC_R_SUCCESS
) {
1069 fprintf(stderr
, "dns_lib_init failed: %d\n", result
);
1073 result
= ctxs_init(&mctx
, &actx
, &taskmgr
, &socketmgr
,
1075 if (result
!= ISC_R_SUCCESS
) {
1076 fprintf(stderr
, "ctx create failed: %d\n", result
);
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
);
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
);
1095 fprintf(stderr
, "failed to convert server name (%s): %s\n",
1096 cacheserver
, gai_strerror(error
));
1100 if (res
->ai_addrlen
> sizeof(sa
.type
)) {
1102 "assumption failure: addrlen is too long: %d\n",
1106 memcpy(&sa
.type
.sa
, res
->ai_addr
, res
->ai_addrlen
);
1107 sa
.length
= res
->ai_addrlen
;
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
,
1114 if (result
!= ISC_R_SUCCESS
) {
1115 fprintf(stderr
, "failed to set server: %d\n", result
);
1119 /* Create the main task */
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
);
1127 /* Open input file */
1131 fp
= fopen(argv
[0], "r");
1133 fprintf(stderr
, "failed to open input file: %s\n",
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");
1164 for (i
= 0; i
< MAX_PROBES
; i
++) {
1165 result
= probe_domain(&probes
[i
]);
1166 if (result
== ISC_R_NOMORE
)
1168 else if (result
!= ISC_R_SUCCESS
) {
1169 fprintf(stderr
, "failed to issue an initial probe\n");
1174 /* Start event loop */
1175 isc_app_ctxrun(actx
);
1178 printf("Per domain results (out of %lu domains):\n",
1180 printf(" valid: %lu\n"
1184 " multiplesoa: %lu\n"
1185 " multiplecname: %lu\n"
1186 " brokenanswer: %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",
1196 printf(" valid: %lu\n"
1200 " multiplesoa: %lu\n"
1201 " multiplecname: %lu\n"
1202 " brokenanswer: %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
);
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
);
1218 isc_app_ctxfinish(actx
);
1219 ctxs_destroy(&mctx
, &actx
, &taskmgr
, &socketmgr
, &timermgr
);