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