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