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