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