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