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