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