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