xref: /minix/external/bsd/bind/dist/lib/samples/nsprobe.c (revision bb9622b5)
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
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
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
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
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
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
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
297 increment_entry(unsigned long *entryp) {
298 	(*entryp)++;
299 	INSIST(*entryp != 0U);	/* check overflow */
300 }
301 
302 static void
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
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
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
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
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
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
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
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
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
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
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