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