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