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