1/*	$NetBSD: nslookup.c,v 1.5 2012/12/04 23:38:38 spz Exp $	*/
2
3/*
4 * Copyright (C) 2004-2012  Internet Systems Consortium, Inc. ("ISC")
5 * Copyright (C) 2000-2003  Internet Software Consortium.
6 *
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
12 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
13 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
14 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
15 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
16 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17 * PERFORMANCE OF THIS SOFTWARE.
18 */
19
20/* Id: nslookup.c,v 1.130 2011/12/16 23:01:16 each Exp  */
21
22#include <config.h>
23
24#include <stdlib.h>
25#include <unistd.h>
26
27#include <isc/app.h>
28#include <isc/buffer.h>
29#include <isc/commandline.h>
30#include <isc/event.h>
31#include <isc/parseint.h>
32#include <isc/print.h>
33#include <isc/string.h>
34#include <isc/timer.h>
35#include <isc/util.h>
36#include <isc/task.h>
37#include <isc/netaddr.h>
38
39#include <dns/message.h>
40#include <dns/name.h>
41#include <dns/fixedname.h>
42#include <dns/rdata.h>
43#include <dns/rdataclass.h>
44#include <dns/rdataset.h>
45#include <dns/rdatastruct.h>
46#include <dns/rdatatype.h>
47#include <dns/byaddr.h>
48
49#include <dig/dig.h>
50
51#if defined(HAVE_READLINE)
52#include <readline/readline.h>
53#include <readline/history.h>
54#endif
55
56static isc_boolean_t short_form = ISC_TRUE,
57	tcpmode = ISC_FALSE,
58	identify = ISC_FALSE, stats = ISC_TRUE,
59	comments = ISC_TRUE, section_question = ISC_TRUE,
60	section_answer = ISC_TRUE, section_authority = ISC_TRUE,
61	section_additional = ISC_TRUE, recurse = ISC_TRUE,
62	aaonly = ISC_FALSE, nofail = ISC_TRUE;
63
64static isc_boolean_t interactive;
65
66static isc_boolean_t in_use = ISC_FALSE;
67static char defclass[MXRD] = "IN";
68static char deftype[MXRD] = "A";
69static isc_event_t *global_event = NULL;
70static int query_error = 1, print_error = 0;
71
72static char domainopt[DNS_NAME_MAXTEXT];
73
74static const char *rcodetext[] = {
75	"NOERROR",
76	"FORMERR",
77	"SERVFAIL",
78	"NXDOMAIN",
79	"NOTIMP",
80	"REFUSED",
81	"YXDOMAIN",
82	"YXRRSET",
83	"NXRRSET",
84	"NOTAUTH",
85	"NOTZONE",
86	"RESERVED11",
87	"RESERVED12",
88	"RESERVED13",
89	"RESERVED14",
90	"RESERVED15",
91	"BADVERS"
92};
93
94static const char *rtypetext[] = {
95	"rtype_0 = ",			/* 0 */
96	"internet address = ",		/* 1 */
97	"nameserver = ",		/* 2 */
98	"md = ",			/* 3 */
99	"mf = ",			/* 4 */
100	"canonical name = ",		/* 5 */
101	"soa = ",			/* 6 */
102	"mb = ",			/* 7 */
103	"mg = ",			/* 8 */
104	"mr = ",			/* 9 */
105	"rtype_10 = ",			/* 10 */
106	"protocol = ",			/* 11 */
107	"name = ",			/* 12 */
108	"hinfo = ",			/* 13 */
109	"minfo = ",			/* 14 */
110	"mail exchanger = ",		/* 15 */
111	"text = ",			/* 16 */
112	"rp = ",       			/* 17 */
113	"afsdb = ",			/* 18 */
114	"x25 address = ",		/* 19 */
115	"isdn address = ",		/* 20 */
116	"rt = ",			/* 21 */
117	"nsap = ",			/* 22 */
118	"nsap_ptr = ",			/* 23 */
119	"signature = ",			/* 24 */
120	"key = ",			/* 25 */
121	"px = ",			/* 26 */
122	"gpos = ",			/* 27 */
123	"has AAAA address ",		/* 28 */
124	"loc = ",			/* 29 */
125	"next = ",			/* 30 */
126	"rtype_31 = ",			/* 31 */
127	"rtype_32 = ",			/* 32 */
128	"service = ",			/* 33 */
129	"rtype_34 = ",			/* 34 */
130	"naptr = ",			/* 35 */
131	"kx = ",			/* 36 */
132	"cert = ",			/* 37 */
133	"v6 address = ",		/* 38 */
134	"dname = ",			/* 39 */
135	"rtype_40 = ",			/* 40 */
136	"optional = "			/* 41 */
137};
138
139#define N_KNOWN_RRTYPES (sizeof(rtypetext) / sizeof(rtypetext[0]))
140
141static void flush_lookup_list(void);
142static void getinput(isc_task_t *task, isc_event_t *event);
143
144static char *
145rcode_totext(dns_rcode_t rcode)
146{
147	static char buf[sizeof("?65535")];
148	union {
149		const char *consttext;
150		char *deconsttext;
151	} totext;
152
153	if (rcode >= (sizeof(rcodetext)/sizeof(rcodetext[0]))) {
154		snprintf(buf, sizeof(buf), "?%u", rcode);
155		totext.deconsttext = buf;
156	} else
157		totext.consttext = rcodetext[rcode];
158	return totext.deconsttext;
159}
160
161void
162dighost_shutdown(void) {
163	isc_event_t *event = global_event;
164
165	flush_lookup_list();
166	debug("dighost_shutdown()");
167
168	if (!in_use) {
169		isc_app_shutdown();
170		return;
171	}
172
173	isc_task_send(global_task, &event);
174}
175
176static void
177printsoa(dns_rdata_t *rdata) {
178	dns_rdata_soa_t soa;
179	isc_result_t result;
180	char namebuf[DNS_NAME_FORMATSIZE];
181
182	result = dns_rdata_tostruct(rdata, &soa, NULL);
183	check_result(result, "dns_rdata_tostruct");
184
185	dns_name_format(&soa.origin, namebuf, sizeof(namebuf));
186	printf("\torigin = %s\n", namebuf);
187	dns_name_format(&soa.contact, namebuf, sizeof(namebuf));
188	printf("\tmail addr = %s\n", namebuf);
189	printf("\tserial = %u\n", soa.serial);
190	printf("\trefresh = %u\n", soa.refresh);
191	printf("\tretry = %u\n", soa.retry);
192	printf("\texpire = %u\n", soa.expire);
193	printf("\tminimum = %u\n", soa.minimum);
194	dns_rdata_freestruct(&soa);
195}
196
197static void
198printa(dns_rdata_t *rdata) {
199	isc_result_t result;
200	char text[sizeof("255.255.255.255")];
201	isc_buffer_t b;
202
203	isc_buffer_init(&b, text, sizeof(text));
204	result = dns_rdata_totext(rdata, NULL, &b);
205	check_result(result, "dns_rdata_totext");
206	printf("Address: %.*s\n", (int)isc_buffer_usedlength(&b),
207	       (char *)isc_buffer_base(&b));
208}
209#ifdef DIG_SIGCHASE
210/* Just for compatibility : not use in host program */
211isc_result_t
212printrdataset(dns_name_t *owner_name, dns_rdataset_t *rdataset,
213	      isc_buffer_t *target)
214{
215	UNUSED(owner_name);
216	UNUSED(rdataset);
217	UNUSED(target);
218	return(ISC_FALSE);
219}
220#endif
221static void
222printrdata(dns_rdata_t *rdata) {
223	isc_result_t result;
224	isc_buffer_t *b = NULL;
225	unsigned int size = 1024;
226	isc_boolean_t done = ISC_FALSE;
227
228	if (rdata->type < N_KNOWN_RRTYPES)
229		printf("%s", rtypetext[rdata->type]);
230	else
231		printf("rdata_%d = ", rdata->type);
232
233	while (!done) {
234		result = isc_buffer_allocate(mctx, &b, size);
235		if (result != ISC_R_SUCCESS)
236			check_result(result, "isc_buffer_allocate");
237		result = dns_rdata_totext(rdata, NULL, b);
238		if (result == ISC_R_SUCCESS) {
239			printf("%.*s\n", (int)isc_buffer_usedlength(b),
240			       (char *)isc_buffer_base(b));
241			done = ISC_TRUE;
242		} else if (result != ISC_R_NOSPACE)
243			check_result(result, "dns_rdata_totext");
244		isc_buffer_free(&b);
245		size *= 2;
246	}
247}
248
249static isc_result_t
250printsection(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers,
251	     dns_section_t section) {
252	isc_result_t result, loopresult;
253	dns_name_t *name;
254	dns_rdataset_t *rdataset = NULL;
255	dns_rdata_t rdata = DNS_RDATA_INIT;
256	char namebuf[DNS_NAME_FORMATSIZE];
257
258	UNUSED(query);
259	UNUSED(headers);
260
261	debug("printsection()");
262
263	result = dns_message_firstname(msg, section);
264	if (result == ISC_R_NOMORE)
265		return (ISC_R_SUCCESS);
266	else if (result != ISC_R_SUCCESS)
267		return (result);
268	for (;;) {
269		name = NULL;
270		dns_message_currentname(msg, section,
271					&name);
272		for (rdataset = ISC_LIST_HEAD(name->list);
273		     rdataset != NULL;
274		     rdataset = ISC_LIST_NEXT(rdataset, link)) {
275			loopresult = dns_rdataset_first(rdataset);
276			while (loopresult == ISC_R_SUCCESS) {
277				dns_rdataset_current(rdataset, &rdata);
278				switch (rdata.type) {
279				case dns_rdatatype_a:
280					if (section != DNS_SECTION_ANSWER)
281						goto def_short_section;
282					dns_name_format(name, namebuf,
283							sizeof(namebuf));
284					printf("Name:\t%s\n", namebuf);
285					printa(&rdata);
286					break;
287				case dns_rdatatype_soa:
288					dns_name_format(name, namebuf,
289							sizeof(namebuf));
290					printf("%s\n", namebuf);
291					printsoa(&rdata);
292					break;
293				default:
294				def_short_section:
295					dns_name_format(name, namebuf,
296							sizeof(namebuf));
297					printf("%s\t", namebuf);
298					printrdata(&rdata);
299					break;
300				}
301				dns_rdata_reset(&rdata);
302				loopresult = dns_rdataset_next(rdataset);
303			}
304		}
305		result = dns_message_nextname(msg, section);
306		if (result == ISC_R_NOMORE)
307			break;
308		else if (result != ISC_R_SUCCESS) {
309			return (result);
310		}
311	}
312	return (ISC_R_SUCCESS);
313}
314
315static isc_result_t
316detailsection(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers,
317	     dns_section_t section) {
318	isc_result_t result, loopresult;
319	dns_name_t *name;
320	dns_rdataset_t *rdataset = NULL;
321	dns_rdata_t rdata = DNS_RDATA_INIT;
322	char namebuf[DNS_NAME_FORMATSIZE];
323
324	UNUSED(query);
325
326	debug("detailsection()");
327
328	if (headers) {
329		switch (section) {
330		case DNS_SECTION_QUESTION:
331			puts("    QUESTIONS:");
332			break;
333		case DNS_SECTION_ANSWER:
334			puts("    ANSWERS:");
335			break;
336		case DNS_SECTION_AUTHORITY:
337			puts("    AUTHORITY RECORDS:");
338			break;
339		case DNS_SECTION_ADDITIONAL:
340			puts("    ADDITIONAL RECORDS:");
341			break;
342		}
343	}
344
345	result = dns_message_firstname(msg, section);
346	if (result == ISC_R_NOMORE)
347		return (ISC_R_SUCCESS);
348	else if (result != ISC_R_SUCCESS)
349		return (result);
350	for (;;) {
351		name = NULL;
352		dns_message_currentname(msg, section,
353					&name);
354		for (rdataset = ISC_LIST_HEAD(name->list);
355		     rdataset != NULL;
356		     rdataset = ISC_LIST_NEXT(rdataset, link)) {
357			if (section == DNS_SECTION_QUESTION) {
358				dns_name_format(name, namebuf,
359						sizeof(namebuf));
360				printf("\t%s, ", namebuf);
361				dns_rdatatype_format(rdataset->type,
362						     namebuf,
363						     sizeof(namebuf));
364				printf("type = %s, ", namebuf);
365				dns_rdataclass_format(rdataset->rdclass,
366						      namebuf,
367						      sizeof(namebuf));
368				printf("class = %s\n", namebuf);
369			}
370			loopresult = dns_rdataset_first(rdataset);
371			while (loopresult == ISC_R_SUCCESS) {
372				dns_rdataset_current(rdataset, &rdata);
373
374				dns_name_format(name, namebuf,
375						sizeof(namebuf));
376				printf("    ->  %s\n", namebuf);
377
378				switch (rdata.type) {
379				case dns_rdatatype_soa:
380					printsoa(&rdata);
381					break;
382				default:
383					printf("\t");
384					printrdata(&rdata);
385				}
386				dns_rdata_reset(&rdata);
387				printf("\tttl = %u\n", rdataset->ttl);
388				loopresult = dns_rdataset_next(rdataset);
389			}
390		}
391		result = dns_message_nextname(msg, section);
392		if (result == ISC_R_NOMORE)
393			break;
394		else if (result != ISC_R_SUCCESS) {
395			return (result);
396		}
397	}
398	return (ISC_R_SUCCESS);
399}
400
401void
402received(int bytes, isc_sockaddr_t *from, dig_query_t *query)
403{
404	UNUSED(bytes);
405	UNUSED(from);
406	UNUSED(query);
407}
408
409void
410trying(char *frm, dig_lookup_t *lookup) {
411	UNUSED(frm);
412	UNUSED(lookup);
413
414}
415
416isc_result_t
417printmessage(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers) {
418	char servtext[ISC_SOCKADDR_FORMATSIZE];
419
420	/* I've we've gotten this far, we've reached a server. */
421	query_error = 0;
422
423	debug("printmessage()");
424
425	isc_sockaddr_format(&query->sockaddr, servtext, sizeof(servtext));
426	printf("Server:\t\t%s\n", query->userarg);
427	printf("Address:\t%s\n", servtext);
428
429	puts("");
430
431	if (!short_form) {
432		isc_boolean_t headers = ISC_TRUE;
433		puts("------------");
434		/*		detailheader(query, msg);*/
435		detailsection(query, msg, headers, DNS_SECTION_QUESTION);
436		detailsection(query, msg, headers, DNS_SECTION_ANSWER);
437		detailsection(query, msg, headers, DNS_SECTION_AUTHORITY);
438		detailsection(query, msg, headers, DNS_SECTION_ADDITIONAL);
439		puts("------------");
440	}
441
442	if (msg->rcode != 0) {
443		char nametext[DNS_NAME_FORMATSIZE];
444		dns_name_format(query->lookup->name,
445				nametext, sizeof(nametext));
446		printf("** server can't find %s: %s\n",
447		       (msg->rcode != dns_rcode_nxdomain) ? nametext :
448		       query->lookup->textname, rcode_totext(msg->rcode));
449		debug("returning with rcode == 0");
450
451		/* the lookup failed */
452		print_error |= 1;
453		return (ISC_R_SUCCESS);
454	}
455
456	if ((msg->flags & DNS_MESSAGEFLAG_AA) == 0)
457		puts("Non-authoritative answer:");
458	if (!ISC_LIST_EMPTY(msg->sections[DNS_SECTION_ANSWER]))
459		printsection(query, msg, headers, DNS_SECTION_ANSWER);
460	else
461		printf("*** Can't find %s: No answer\n",
462		       query->lookup->textname);
463
464	if (((msg->flags & DNS_MESSAGEFLAG_AA) == 0) &&
465	    (query->lookup->rdtype != dns_rdatatype_a)) {
466		puts("\nAuthoritative answers can be found from:");
467		printsection(query, msg, headers,
468			     DNS_SECTION_AUTHORITY);
469		printsection(query, msg, headers,
470			     DNS_SECTION_ADDITIONAL);
471	}
472	return (ISC_R_SUCCESS);
473}
474
475static void
476show_settings(isc_boolean_t full, isc_boolean_t serv_only) {
477	dig_server_t *srv;
478	isc_sockaddr_t sockaddr;
479	dig_searchlist_t *listent;
480	isc_result_t result;
481
482	srv = ISC_LIST_HEAD(server_list);
483
484	while (srv != NULL) {
485		char sockstr[ISC_SOCKADDR_FORMATSIZE];
486
487		result = get_address(srv->servername, port, &sockaddr);
488		check_result(result, "get_address");
489
490		isc_sockaddr_format(&sockaddr, sockstr, sizeof(sockstr));
491		printf("Default server: %s\nAddress: %s\n",
492			srv->userarg, sockstr);
493		if (!full)
494			return;
495		srv = ISC_LIST_NEXT(srv, link);
496	}
497	if (serv_only)
498		return;
499	printf("\nSet options:\n");
500	printf("  %s\t\t\t%s\t\t%s\n",
501	       tcpmode ? "vc" : "novc",
502	       short_form ? "nodebug" : "debug",
503	       debugging ? "d2" : "nod2");
504	printf("  %s\t\t%s\n",
505	       usesearch ? "search" : "nosearch",
506	       recurse ? "recurse" : "norecurse");
507	printf("  timeout = %d\t\tretry = %d\tport = %d\n",
508	       timeout, tries, port);
509	printf("  querytype = %-8s\tclass = %s\n", deftype, defclass);
510	printf("  srchlist = ");
511	for (listent = ISC_LIST_HEAD(search_list);
512	     listent != NULL;
513	     listent = ISC_LIST_NEXT(listent, link)) {
514		     printf("%s", listent->origin);
515		     if (ISC_LIST_NEXT(listent, link) != NULL)
516			     printf("/");
517	}
518	printf("\n");
519}
520
521static isc_boolean_t
522testtype(char *typetext) {
523	isc_result_t result;
524	isc_textregion_t tr;
525	dns_rdatatype_t rdtype;
526
527	tr.base = typetext;
528	tr.length = strlen(typetext);
529	result = dns_rdatatype_fromtext(&rdtype, &tr);
530	if (result == ISC_R_SUCCESS)
531		return (ISC_TRUE);
532	else {
533		printf("unknown query type: %s\n", typetext);
534		return (ISC_FALSE);
535	}
536}
537
538static isc_boolean_t
539testclass(char *typetext) {
540	isc_result_t result;
541	isc_textregion_t tr;
542	dns_rdataclass_t rdclass;
543
544	tr.base = typetext;
545	tr.length = strlen(typetext);
546	result = dns_rdataclass_fromtext(&rdclass, &tr);
547	if (result == ISC_R_SUCCESS)
548		return (ISC_TRUE);
549	else {
550		printf("unknown query class: %s\n", typetext);
551		return (ISC_FALSE);
552	}
553}
554
555static void
556set_port(const char *value) {
557	isc_uint32_t n;
558	isc_result_t result = parse_uint(&n, value, 65535, "port");
559	if (result == ISC_R_SUCCESS)
560		port = (isc_uint16_t) n;
561}
562
563static void
564set_timeout(const char *value) {
565	isc_uint32_t n;
566	isc_result_t result = parse_uint(&n, value, UINT_MAX, "timeout");
567	if (result == ISC_R_SUCCESS)
568		timeout = n;
569}
570
571static void
572set_tries(const char *value) {
573	isc_uint32_t n;
574	isc_result_t result = parse_uint(&n, value, INT_MAX, "tries");
575	if (result == ISC_R_SUCCESS)
576		tries = n;
577}
578
579static void
580setoption(char *opt) {
581	if (strncasecmp(opt, "all", 4) == 0) {
582		show_settings(ISC_TRUE, ISC_FALSE);
583	} else if (strncasecmp(opt, "class=", 6) == 0) {
584		if (testclass(&opt[6]))
585			strlcpy(defclass, &opt[6], sizeof(defclass));
586	} else if (strncasecmp(opt, "cl=", 3) == 0) {
587		if (testclass(&opt[3]))
588			strlcpy(defclass, &opt[3], sizeof(defclass));
589	} else if (strncasecmp(opt, "type=", 5) == 0) {
590		if (testtype(&opt[5]))
591			strlcpy(deftype, &opt[5], sizeof(deftype));
592	} else if (strncasecmp(opt, "ty=", 3) == 0) {
593		if (testtype(&opt[3]))
594			strlcpy(deftype, &opt[3], sizeof(deftype));
595	} else if (strncasecmp(opt, "querytype=", 10) == 0) {
596		if (testtype(&opt[10]))
597			strlcpy(deftype, &opt[10], sizeof(deftype));
598	} else if (strncasecmp(opt, "query=", 6) == 0) {
599		if (testtype(&opt[6]))
600			strlcpy(deftype, &opt[6], sizeof(deftype));
601	} else if (strncasecmp(opt, "qu=", 3) == 0) {
602		if (testtype(&opt[3]))
603			strlcpy(deftype, &opt[3], sizeof(deftype));
604	} else if (strncasecmp(opt, "q=", 2) == 0) {
605		if (testtype(&opt[2]))
606			strlcpy(deftype, &opt[2], sizeof(deftype));
607	} else if (strncasecmp(opt, "domain=", 7) == 0) {
608		strlcpy(domainopt, &opt[7], sizeof(domainopt));
609		set_search_domain(domainopt);
610		usesearch = ISC_TRUE;
611	} else if (strncasecmp(opt, "do=", 3) == 0) {
612		strlcpy(domainopt, &opt[3], sizeof(domainopt));
613		set_search_domain(domainopt);
614		usesearch = ISC_TRUE;
615	} else if (strncasecmp(opt, "port=", 5) == 0) {
616		set_port(&opt[5]);
617	} else if (strncasecmp(opt, "po=", 3) == 0) {
618		set_port(&opt[3]);
619	} else if (strncasecmp(opt, "timeout=", 8) == 0) {
620		set_timeout(&opt[8]);
621	} else if (strncasecmp(opt, "t=", 2) == 0) {
622		set_timeout(&opt[2]);
623	} else if (strncasecmp(opt, "rec", 3) == 0) {
624		recurse = ISC_TRUE;
625	} else if (strncasecmp(opt, "norec", 5) == 0) {
626		recurse = ISC_FALSE;
627	} else if (strncasecmp(opt, "retry=", 6) == 0) {
628		set_tries(&opt[6]);
629	} else if (strncasecmp(opt, "ret=", 4) == 0) {
630		set_tries(&opt[4]);
631	} else if (strncasecmp(opt, "def", 3) == 0) {
632		usesearch = ISC_TRUE;
633	} else if (strncasecmp(opt, "nodef", 5) == 0) {
634		usesearch = ISC_FALSE;
635	} else if (strncasecmp(opt, "vc", 3) == 0) {
636		tcpmode = ISC_TRUE;
637	} else if (strncasecmp(opt, "novc", 5) == 0) {
638		tcpmode = ISC_FALSE;
639	} else if (strncasecmp(opt, "deb", 3) == 0) {
640		short_form = ISC_FALSE;
641		showsearch = ISC_TRUE;
642	} else if (strncasecmp(opt, "nodeb", 5) == 0) {
643		short_form = ISC_TRUE;
644		showsearch = ISC_FALSE;
645	} else if (strncasecmp(opt, "d2", 2) == 0) {
646		debugging = ISC_TRUE;
647	} else if (strncasecmp(opt, "nod2", 4) == 0) {
648		debugging = ISC_FALSE;
649	} else if (strncasecmp(opt, "search", 3) == 0) {
650		usesearch = ISC_TRUE;
651	} else if (strncasecmp(opt, "nosearch", 5) == 0) {
652		usesearch = ISC_FALSE;
653	} else if (strncasecmp(opt, "sil", 3) == 0) {
654		/* deprecation_msg = ISC_FALSE; */
655	} else if (strncasecmp(opt, "fail", 3) == 0) {
656		nofail=ISC_FALSE;
657	} else if (strncasecmp(opt, "nofail", 3) == 0) {
658		nofail=ISC_TRUE;
659	} else {
660		printf("*** Invalid option: %s\n", opt);
661	}
662}
663
664static void
665addlookup(char *opt) {
666	dig_lookup_t *lookup;
667	isc_result_t result;
668	isc_textregion_t tr;
669	dns_rdatatype_t rdtype;
670	dns_rdataclass_t rdclass;
671	char store[MXNAME];
672
673	debug("addlookup()");
674	tr.base = deftype;
675	tr.length = strlen(deftype);
676	result = dns_rdatatype_fromtext(&rdtype, &tr);
677	if (result != ISC_R_SUCCESS) {
678		printf("unknown query type: %s\n", deftype);
679		rdclass = dns_rdatatype_a;
680	}
681	tr.base = defclass;
682	tr.length = strlen(defclass);
683	result = dns_rdataclass_fromtext(&rdclass, &tr);
684	if (result != ISC_R_SUCCESS) {
685		printf("unknown query class: %s\n", defclass);
686		rdclass = dns_rdataclass_in;
687	}
688	lookup = make_empty_lookup();
689	if (get_reverse(store, sizeof(store), opt, lookup->ip6_int, ISC_TRUE)
690	    == ISC_R_SUCCESS) {
691		strlcpy(lookup->textname, store, sizeof(lookup->textname));
692		lookup->rdtype = dns_rdatatype_ptr;
693		lookup->rdtypeset = ISC_TRUE;
694	} else {
695		strlcpy(lookup->textname, opt, sizeof(lookup->textname));
696		lookup->rdtype = rdtype;
697		lookup->rdtypeset = ISC_TRUE;
698	}
699	lookup->rdclass = rdclass;
700	lookup->rdclassset = ISC_TRUE;
701	lookup->trace = ISC_FALSE;
702	lookup->trace_root = lookup->trace;
703	lookup->ns_search_only = ISC_FALSE;
704	lookup->identify = identify;
705	lookup->recurse = recurse;
706	lookup->aaonly = aaonly;
707	lookup->retries = tries;
708	lookup->udpsize = 0;
709	lookup->comments = comments;
710	lookup->tcp_mode = tcpmode;
711	lookup->stats = stats;
712	lookup->section_question = section_question;
713	lookup->section_answer = section_answer;
714	lookup->section_authority = section_authority;
715	lookup->section_additional = section_additional;
716	lookup->new_search = ISC_TRUE;
717	if (nofail)
718		lookup->servfail_stops = ISC_FALSE;
719	ISC_LIST_INIT(lookup->q);
720	ISC_LINK_INIT(lookup, link);
721	ISC_LIST_APPEND(lookup_list, lookup, link);
722	lookup->origin = NULL;
723	ISC_LIST_INIT(lookup->my_server_list);
724	debug("looking up %s", lookup->textname);
725}
726
727static void
728do_next_command(char *input) {
729	char *ptr, *arg;
730
731	ptr = next_token(&input, " \t\r\n");
732	if (ptr == NULL)
733		return;
734	arg = next_token(&input, " \t\r\n");
735	if ((strcasecmp(ptr, "set") == 0) &&
736	    (arg != NULL))
737		setoption(arg);
738	else if ((strcasecmp(ptr, "server") == 0) ||
739		 (strcasecmp(ptr, "lserver") == 0)) {
740		isc_app_block();
741		set_nameserver(arg);
742		check_ra = ISC_FALSE;
743		isc_app_unblock();
744		show_settings(ISC_TRUE, ISC_TRUE);
745	} else if (strcasecmp(ptr, "exit") == 0) {
746		in_use = ISC_FALSE;
747	} else if (strcasecmp(ptr, "help") == 0 ||
748		   strcasecmp(ptr, "?") == 0) {
749		printf("The '%s' command is not yet implemented.\n", ptr);
750	} else if (strcasecmp(ptr, "finger") == 0 ||
751		   strcasecmp(ptr, "root") == 0 ||
752		   strcasecmp(ptr, "ls") == 0 ||
753		   strcasecmp(ptr, "view") == 0) {
754		printf("The '%s' command is not implemented.\n", ptr);
755	} else
756		addlookup(ptr);
757}
758
759static void
760get_next_command(void) {
761	char *buf;
762	char *ptr;
763
764	fflush(stdout);
765	buf = isc_mem_allocate(mctx, COMMSIZE);
766	if (buf == NULL)
767		fatal("memory allocation failure");
768	isc_app_block();
769	if (interactive) {
770#ifdef HAVE_READLINE
771		ptr = readline("> ");
772		add_history(ptr);
773#else
774		fputs("> ", stderr);
775		fflush(stderr);
776		ptr = fgets(buf, COMMSIZE, stdin);
777#endif
778	} else
779		ptr = fgets(buf, COMMSIZE, stdin);
780	isc_app_unblock();
781	if (ptr == NULL) {
782		in_use = ISC_FALSE;
783	} else
784		do_next_command(ptr);
785#ifdef HAVE_READLINE
786	if (interactive)
787		free(ptr);
788#endif
789	isc_mem_free(mctx, buf);
790}
791
792static void
793parse_args(int argc, char **argv) {
794	isc_boolean_t have_lookup = ISC_FALSE;
795
796	usesearch = ISC_TRUE;
797	for (argc--, argv++; argc > 0; argc--, argv++) {
798		debug("main parsing %s", argv[0]);
799		if (argv[0][0] == '-') {
800			if (argv[0][1] != 0)
801				setoption(&argv[0][1]);
802			else
803				have_lookup = ISC_TRUE;
804		} else {
805			if (!have_lookup) {
806				have_lookup = ISC_TRUE;
807				in_use = ISC_TRUE;
808				addlookup(argv[0]);
809			} else {
810				set_nameserver(argv[0]);
811				check_ra = ISC_FALSE;
812			}
813		}
814	}
815}
816
817static void
818flush_lookup_list(void) {
819	dig_lookup_t *l, *lp;
820	dig_query_t *q, *qp;
821	dig_server_t *s, *sp;
822
823	lookup_counter = 0;
824	l = ISC_LIST_HEAD(lookup_list);
825	while (l != NULL) {
826		q = ISC_LIST_HEAD(l->q);
827		while (q != NULL) {
828			if (q->sock != NULL) {
829				isc_socket_cancel(q->sock, NULL,
830						  ISC_SOCKCANCEL_ALL);
831				isc_socket_detach(&q->sock);
832			}
833			if (ISC_LINK_LINKED(&q->recvbuf, link))
834				ISC_LIST_DEQUEUE(q->recvlist, &q->recvbuf,
835						 link);
836			if (ISC_LINK_LINKED(&q->lengthbuf, link))
837				ISC_LIST_DEQUEUE(q->lengthlist, &q->lengthbuf,
838						 link);
839			isc_buffer_invalidate(&q->recvbuf);
840			isc_buffer_invalidate(&q->lengthbuf);
841			qp = q;
842			q = ISC_LIST_NEXT(q, link);
843			ISC_LIST_DEQUEUE(l->q, qp, link);
844			isc_mem_free(mctx, qp);
845		}
846		s = ISC_LIST_HEAD(l->my_server_list);
847		while (s != NULL) {
848			sp = s;
849			s = ISC_LIST_NEXT(s, link);
850			ISC_LIST_DEQUEUE(l->my_server_list, sp, link);
851			isc_mem_free(mctx, sp);
852
853		}
854		if (l->sendmsg != NULL)
855			dns_message_destroy(&l->sendmsg);
856		if (l->timer != NULL)
857			isc_timer_detach(&l->timer);
858		lp = l;
859		l = ISC_LIST_NEXT(l, link);
860		ISC_LIST_DEQUEUE(lookup_list, lp, link);
861		isc_mem_free(mctx, lp);
862	}
863}
864
865static void
866getinput(isc_task_t *task, isc_event_t *event) {
867	UNUSED(task);
868	if (global_event == NULL)
869		global_event = event;
870	while (in_use) {
871		get_next_command();
872		if (ISC_LIST_HEAD(lookup_list) != NULL) {
873			start_lookup();
874			return;
875		}
876	}
877	isc_app_shutdown();
878}
879
880int
881main(int argc, char **argv) {
882	isc_result_t result;
883
884	interactive = ISC_TF(isatty(0));
885
886	ISC_LIST_INIT(lookup_list);
887	ISC_LIST_INIT(server_list);
888	ISC_LIST_INIT(search_list);
889
890	check_ra = ISC_TRUE;
891
892	result = isc_app_start();
893	check_result(result, "isc_app_start");
894
895	setup_libs();
896	progname = argv[0];
897
898	parse_args(argc, argv);
899
900	setup_system();
901	if (domainopt[0] != '\0')
902		set_search_domain(domainopt);
903	if (in_use)
904		result = isc_app_onrun(mctx, global_task, onrun_callback,
905				       NULL);
906	else
907		result = isc_app_onrun(mctx, global_task, getinput, NULL);
908	check_result(result, "isc_app_onrun");
909	in_use = ISC_TF(!in_use);
910
911	(void)isc_app_run();
912
913	puts("");
914	debug("done, and starting to shut down");
915	if (global_event != NULL)
916		isc_event_free(&global_event);
917	cancel_all();
918	destroy_libs();
919	isc_app_finish();
920
921	return (query_error | print_error);
922}
923