xref: /netbsd-src/external/mpl/bind/dist/bin/dig/host.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1 /*	$NetBSD: host.c,v 1.13 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 /*! \file */
17 
18 #include <inttypes.h>
19 #include <limits.h>
20 #include <locale.h>
21 #include <stdbool.h>
22 #include <stdlib.h>
23 
24 #include <isc/attributes.h>
25 #include <isc/commandline.h>
26 #include <isc/loop.h>
27 #include <isc/netaddr.h>
28 #include <isc/string.h>
29 #include <isc/util.h>
30 
31 #include <dns/byaddr.h>
32 #include <dns/fixedname.h>
33 #include <dns/message.h>
34 #include <dns/name.h>
35 #include <dns/rdata.h>
36 #include <dns/rdataclass.h>
37 #include <dns/rdataset.h>
38 #include <dns/rdatastruct.h>
39 #include <dns/rdatatype.h>
40 
41 #include "dighost.h"
42 
43 static bool short_form = true, listed_server = false;
44 static bool default_lookups = true;
45 static int seen_error = -1;
46 static bool list_addresses = true;
47 static bool list_almost_all = false;
48 static dns_rdatatype_t list_type = dns_rdatatype_a;
49 static bool printed_server = false;
50 static bool ipv4only = false, ipv6only = false;
51 
52 static const char *opcodetext[] = { "QUERY",	  "IQUERY",	"STATUS",
53 				    "RESERVED3",  "NOTIFY",	"UPDATE",
54 				    "RESERVED6",  "RESERVED7",	"RESERVED8",
55 				    "RESERVED9",  "RESERVED10", "RESERVED11",
56 				    "RESERVED12", "RESERVED13", "RESERVED14",
57 				    "RESERVED15" };
58 
59 static const char *rcodetext[] = { "NOERROR",	 "FORMERR",    "SERVFAIL",
60 				   "NXDOMAIN",	 "NOTIMP",     "REFUSED",
61 				   "YXDOMAIN",	 "YXRRSET",    "NXRRSET",
62 				   "NOTAUTH",	 "NOTZONE",    "RESERVED11",
63 				   "RESERVED12", "RESERVED13", "RESERVED14",
64 				   "RESERVED15", "BADVERS" };
65 
66 struct rtype {
67 	unsigned int type;
68 	const char *text;
69 };
70 
71 struct rtype rtypes[] = { { 1, "has address" },
72 			  { 2, "name server" },
73 			  { 5, "is an alias for" },
74 			  { 11, "has well known services" },
75 			  { 12, "domain name pointer" },
76 			  { 13, "host information" },
77 			  { 15, "mail is handled by" },
78 			  { 16, "descriptive text" },
79 			  { 19, "x25 address" },
80 			  { 20, "ISDN address" },
81 			  { 24, "has signature" },
82 			  { 25, "has key" },
83 			  { 28, "has IPv6 address" },
84 			  { 29, "location" },
85 			  { 0, NULL } };
86 
87 static char *
88 rcode_totext(dns_rcode_t rcode) {
89 	static char buf[sizeof("?65535")];
90 	union {
91 		const char *consttext;
92 		char *deconsttext;
93 	} totext;
94 
95 	if (rcode >= (sizeof(rcodetext) / sizeof(rcodetext[0]))) {
96 		snprintf(buf, sizeof(buf), "?%u", rcode);
97 		totext.deconsttext = buf;
98 	} else {
99 		totext.consttext = rcodetext[rcode];
100 	}
101 	return totext.deconsttext;
102 }
103 
104 noreturn static void
105 show_usage(void);
106 
107 static void
108 show_usage(void) {
109 	fprintf(stderr,
110 		"Usage: host [-aCdilrTvVw] [-c class] [-N ndots] [-t type] [-W "
111 		"time]\n"
112 		"            [-R number] [-m flag] [-p port] hostname "
113 		"[server]\n"
114 		"       -a is equivalent to -v -t ANY\n"
115 		"       -A is like -a but omits RRSIG, NSEC, NSEC3\n"
116 		"       -c specifies query class for non-IN data\n"
117 		"       -C compares SOA records on authoritative nameservers\n"
118 		"       -d is equivalent to -v\n"
119 		"       -l lists all hosts in a domain, using AXFR\n"
120 		"       -m set memory debugging flag (trace|record|usage)\n"
121 		"       -N changes the number of dots allowed before root "
122 		"lookup "
123 		"is done\n"
124 		"       -p specifies the port on the server to query\n"
125 		"       -r disables recursive processing\n"
126 		"       -R specifies number of retries for UDP packets\n"
127 		"       -s a SERVFAIL response should stop query\n"
128 		"       -t specifies the query type\n"
129 		"       -T enables TCP/IP mode\n"
130 		"       -U enables UDP mode\n"
131 		"       -v enables verbose output\n"
132 		"       -V print version number and exit\n"
133 		"       -w specifies to wait forever for a reply\n"
134 		"       -W specifies how long to wait for a reply\n"
135 		"       -4 use IPv4 query transport only\n"
136 		"       -6 use IPv6 query transport only\n");
137 	exit(EXIT_FAILURE);
138 }
139 
140 static void
141 host_shutdown(void) {
142 	isc_loopmgr_shutdown(loopmgr);
143 }
144 
145 static void
146 received(unsigned int bytes, isc_sockaddr_t *from, dig_query_t *query) {
147 	isc_time_t now;
148 	int diff;
149 
150 	if (!short_form) {
151 		char fromtext[ISC_SOCKADDR_FORMATSIZE];
152 		isc_sockaddr_format(from, fromtext, sizeof(fromtext));
153 		if (query->lookup->use_usec) {
154 			now = isc_time_now_hires();
155 		} else {
156 			now = isc_time_now();
157 		}
158 		diff = (int)isc_time_microdiff(&now, &query->time_sent);
159 		printf("Received %u bytes from %s in %d ms\n", bytes, fromtext,
160 		       diff / 1000);
161 	}
162 }
163 
164 static void
165 trying(char *frm, dig_lookup_t *lookup) {
166 	UNUSED(lookup);
167 
168 	if (!short_form) {
169 		printf("Trying \"%s\"\n", frm);
170 	}
171 }
172 
173 static void
174 say_message(dns_name_t *name, const char *msg, dns_rdata_t *rdata,
175 	    dig_query_t *query) {
176 	isc_buffer_t *b = NULL;
177 	char namestr[DNS_NAME_FORMATSIZE];
178 	isc_region_t r;
179 	isc_result_t result;
180 	unsigned int bufsize = BUFSIZ;
181 
182 	dns_name_format(name, namestr, sizeof(namestr));
183 retry:
184 	isc_buffer_allocate(mctx, &b, bufsize);
185 	result = dns_rdata_totext(rdata, NULL, b);
186 	if (result == ISC_R_NOSPACE) {
187 		isc_buffer_free(&b);
188 		INSIST(bufsize <= (UINT_MAX / 2));
189 		bufsize *= 2;
190 		goto retry;
191 	}
192 	check_result(result, "dns_rdata_totext");
193 	isc_buffer_usedregion(b, &r);
194 	if (query->lookup->identify_previous_line) {
195 		printf("Nameserver %s:\n\t", query->servname);
196 	}
197 	printf("%s %s %.*s", namestr, msg, (int)r.length, (char *)r.base);
198 	if (query->lookup->identify) {
199 		printf(" on server %s", query->servname);
200 	}
201 	printf("\n");
202 	isc_buffer_free(&b);
203 }
204 
205 static isc_result_t
206 printsection(dns_message_t *msg, dns_section_t sectionid,
207 	     const char *section_name, bool headers, dig_query_t *query) {
208 	dns_name_t *name, *print_name;
209 	dns_rdataset_t *rdataset;
210 	dns_rdata_t rdata = DNS_RDATA_INIT;
211 	isc_buffer_t target;
212 	isc_result_t result, loopresult;
213 	isc_region_t r;
214 	dns_name_t empty_name;
215 	char tbuf[4096] = { 0 };
216 	bool first;
217 	bool no_rdata = (sectionid == DNS_SECTION_QUESTION);
218 
219 	if (headers) {
220 		printf(";; %s SECTION:\n", section_name);
221 	}
222 
223 	dns_name_init(&empty_name, NULL);
224 
225 	result = dns_message_firstname(msg, sectionid);
226 	if (result == ISC_R_NOMORE) {
227 		return ISC_R_SUCCESS;
228 	} else if (result != ISC_R_SUCCESS) {
229 		return result;
230 	}
231 
232 	for (;;) {
233 		name = NULL;
234 		dns_message_currentname(msg, sectionid, &name);
235 
236 		isc_buffer_init(&target, tbuf, sizeof(tbuf));
237 		first = true;
238 		print_name = name;
239 
240 		for (rdataset = ISC_LIST_HEAD(name->list); rdataset != NULL;
241 		     rdataset = ISC_LIST_NEXT(rdataset, link))
242 		{
243 			if (query->lookup->rdtype == dns_rdatatype_axfr &&
244 			    !((!list_addresses &&
245 			       (list_type == dns_rdatatype_any ||
246 				rdataset->type == list_type)) ||
247 			      (list_addresses &&
248 			       (rdataset->type == dns_rdatatype_a ||
249 				rdataset->type == dns_rdatatype_aaaa ||
250 				rdataset->type == dns_rdatatype_ns ||
251 				rdataset->type == dns_rdatatype_ptr))))
252 			{
253 				continue;
254 			}
255 			if (list_almost_all &&
256 			    (rdataset->type == dns_rdatatype_rrsig ||
257 			     rdataset->type == dns_rdatatype_nsec ||
258 			     rdataset->type == dns_rdatatype_nsec3))
259 			{
260 				continue;
261 			}
262 			if (!short_form) {
263 				result = dns_rdataset_totext(rdataset,
264 							     print_name, false,
265 							     no_rdata, &target);
266 				if (result != ISC_R_SUCCESS) {
267 					return result;
268 				}
269 #ifdef USEINITALWS
270 				if (first) {
271 					print_name = &empty_name;
272 					first = false;
273 				}
274 #else  /* ifdef USEINITALWS */
275 				UNUSED(first); /* Shut up compiler. */
276 #endif /* ifdef USEINITALWS */
277 			} else {
278 				loopresult = dns_rdataset_first(rdataset);
279 				while (loopresult == ISC_R_SUCCESS) {
280 					struct rtype *t;
281 					const char *rtt;
282 					char typebuf[DNS_RDATATYPE_FORMATSIZE];
283 					char typebuf2[DNS_RDATATYPE_FORMATSIZE +
284 						      20];
285 					dns_rdataset_current(rdataset, &rdata);
286 
287 					for (t = rtypes; t->text != NULL; t++) {
288 						if (t->type == rdata.type) {
289 							rtt = t->text;
290 							goto found;
291 						}
292 					}
293 
294 					dns_rdatatype_format(rdata.type,
295 							     typebuf,
296 							     sizeof(typebuf));
297 					snprintf(typebuf2, sizeof(typebuf2),
298 						 "has %s record", typebuf);
299 					rtt = typebuf2;
300 				found:
301 					say_message(print_name, rtt, &rdata,
302 						    query);
303 					dns_rdata_reset(&rdata);
304 					loopresult =
305 						dns_rdataset_next(rdataset);
306 				}
307 			}
308 		}
309 		if (!short_form) {
310 			isc_buffer_usedregion(&target, &r);
311 			if (no_rdata) {
312 				printf(";%.*s", (int)r.length, (char *)r.base);
313 			} else {
314 				printf("%.*s", (int)r.length, (char *)r.base);
315 			}
316 		}
317 
318 		result = dns_message_nextname(msg, sectionid);
319 		if (result == ISC_R_NOMORE) {
320 			break;
321 		} else if (result != ISC_R_SUCCESS) {
322 			return result;
323 		}
324 	}
325 
326 	return ISC_R_SUCCESS;
327 }
328 
329 static isc_result_t
330 printrdata(dns_message_t *msg, dns_rdataset_t *rdataset,
331 	   const dns_name_t *owner, const char *set_name, bool headers) {
332 	isc_buffer_t target;
333 	isc_result_t result;
334 	isc_region_t r;
335 	char tbuf[4096];
336 
337 	UNUSED(msg);
338 	if (headers) {
339 		printf(";; %s SECTION:\n", set_name);
340 	}
341 
342 	isc_buffer_init(&target, tbuf, sizeof(tbuf));
343 
344 	result = dns_rdataset_totext(rdataset, owner, false, false, &target);
345 	if (result != ISC_R_SUCCESS) {
346 		return result;
347 	}
348 	isc_buffer_usedregion(&target, &r);
349 	printf("%.*s", (int)r.length, (char *)r.base);
350 
351 	return ISC_R_SUCCESS;
352 }
353 
354 static void
355 chase_cnamechain(dns_message_t *msg, dns_name_t *qname) {
356 	isc_result_t result;
357 	dns_rdataset_t *rdataset;
358 	dns_rdata_cname_t cname;
359 	dns_rdata_t rdata = DNS_RDATA_INIT;
360 	unsigned int i = msg->counts[DNS_SECTION_ANSWER];
361 
362 	while (i-- > 0) {
363 		rdataset = NULL;
364 		result = dns_message_findname(msg, DNS_SECTION_ANSWER, qname,
365 					      dns_rdatatype_cname, 0, NULL,
366 					      &rdataset);
367 		if (result != ISC_R_SUCCESS) {
368 			return;
369 		}
370 		result = dns_rdataset_first(rdataset);
371 		check_result(result, "dns_rdataset_first");
372 		dns_rdata_reset(&rdata);
373 		dns_rdataset_current(rdataset, &rdata);
374 		result = dns_rdata_tostruct(&rdata, &cname, NULL);
375 		check_result(result, "dns_rdata_tostruct");
376 		dns_name_copy(&cname.cname, qname);
377 		dns_rdata_freestruct(&cname);
378 	}
379 }
380 
381 static isc_result_t
382 printmessage(dig_query_t *query, const isc_buffer_t *msgbuf, dns_message_t *msg,
383 	     bool headers) {
384 	bool did_flag = false;
385 	dns_rdataset_t *opt, *tsig = NULL;
386 	const dns_name_t *tsigname;
387 	isc_result_t result = ISC_R_SUCCESS;
388 	int force_error;
389 
390 	UNUSED(msgbuf);
391 	UNUSED(headers);
392 
393 	/*
394 	 * We get called multiple times.
395 	 * Preserve any existing error status.
396 	 */
397 	force_error = (seen_error == 1) ? 1 : 0;
398 	seen_error = 1;
399 	if (listed_server && !printed_server) {
400 		char sockstr[ISC_SOCKADDR_FORMATSIZE];
401 
402 		printf("Using domain server:\n");
403 		printf("Name: %s\n", query->userarg);
404 		isc_sockaddr_format(&query->sockaddr, sockstr, sizeof(sockstr));
405 		printf("Address: %s\n", sockstr);
406 		printf("Aliases: \n\n");
407 		printed_server = true;
408 	}
409 
410 	if (msg->rcode != 0) {
411 		char namestr[DNS_NAME_FORMATSIZE];
412 		dns_name_format(query->lookup->name, namestr, sizeof(namestr));
413 
414 		if (query->lookup->identify_previous_line) {
415 			printf("Nameserver %s:\n\t%s not found: %d(%s)\n",
416 			       query->servname,
417 			       (msg->rcode != dns_rcode_nxdomain)
418 				       ? namestr
419 				       : query->lookup->textname,
420 			       msg->rcode, rcode_totext(msg->rcode));
421 		} else {
422 			printf("Host %s not found: %d(%s)\n",
423 			       (msg->rcode != dns_rcode_nxdomain)
424 				       ? namestr
425 				       : query->lookup->textname,
426 			       msg->rcode, rcode_totext(msg->rcode));
427 		}
428 		return ISC_R_SUCCESS;
429 	}
430 
431 	if (default_lookups && query->lookup->rdtype == dns_rdatatype_a) {
432 		char namestr[DNS_NAME_FORMATSIZE];
433 		dig_lookup_t *lookup;
434 		dns_fixedname_t fixed;
435 		dns_name_t *name;
436 
437 		/* Add AAAA and MX lookups. */
438 		name = dns_fixedname_initname(&fixed);
439 		dns_name_copy(query->lookup->name, name);
440 		chase_cnamechain(msg, name);
441 		dns_name_format(name, namestr, sizeof(namestr));
442 		lookup = clone_lookup(query->lookup, false);
443 		if (lookup != NULL) {
444 			strlcpy(lookup->textname, namestr,
445 				sizeof(lookup->textname));
446 			lookup->rdtype = dns_rdatatype_aaaa;
447 			lookup->rdtypeset = true;
448 			lookup->origin = NULL;
449 			lookup->retries = tries;
450 			ISC_LIST_APPEND(lookup_list, lookup, link);
451 		}
452 		lookup = clone_lookup(query->lookup, false);
453 		if (lookup != NULL) {
454 			strlcpy(lookup->textname, namestr,
455 				sizeof(lookup->textname));
456 			lookup->rdtype = dns_rdatatype_mx;
457 			lookup->rdtypeset = true;
458 			lookup->origin = NULL;
459 			lookup->retries = tries;
460 			ISC_LIST_APPEND(lookup_list, lookup, link);
461 		}
462 	}
463 
464 	if (!short_form) {
465 		printf(";; ->>HEADER<<- opcode: %s, status: %s, id: %u\n",
466 		       opcodetext[msg->opcode], rcode_totext(msg->rcode),
467 		       msg->id);
468 		printf(";; flags: ");
469 		if ((msg->flags & DNS_MESSAGEFLAG_QR) != 0) {
470 			printf("qr");
471 			did_flag = true;
472 		}
473 		if ((msg->flags & DNS_MESSAGEFLAG_AA) != 0) {
474 			printf("%saa", did_flag ? " " : "");
475 			did_flag = true;
476 		}
477 		if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0) {
478 			printf("%stc", did_flag ? " " : "");
479 			did_flag = true;
480 		}
481 		if ((msg->flags & DNS_MESSAGEFLAG_RD) != 0) {
482 			printf("%srd", did_flag ? " " : "");
483 			did_flag = true;
484 		}
485 		if ((msg->flags & DNS_MESSAGEFLAG_RA) != 0) {
486 			printf("%sra", did_flag ? " " : "");
487 			did_flag = true;
488 		}
489 		if ((msg->flags & DNS_MESSAGEFLAG_AD) != 0) {
490 			printf("%sad", did_flag ? " " : "");
491 			did_flag = true;
492 		}
493 		if ((msg->flags & DNS_MESSAGEFLAG_CD) != 0) {
494 			printf("%scd", did_flag ? " " : "");
495 			did_flag = true;
496 			POST(did_flag);
497 		}
498 		printf("; QUERY: %u, ANSWER: %u, "
499 		       "AUTHORITY: %u, ADDITIONAL: %u\n",
500 		       msg->counts[DNS_SECTION_QUESTION],
501 		       msg->counts[DNS_SECTION_ANSWER],
502 		       msg->counts[DNS_SECTION_AUTHORITY],
503 		       msg->counts[DNS_SECTION_ADDITIONAL]);
504 		opt = dns_message_getopt(msg);
505 		if (opt != NULL) {
506 			printf(";; EDNS: version: %u, udp=%u\n",
507 			       (unsigned int)((opt->ttl & 0x00ff0000) >> 16),
508 			       (unsigned int)opt->rdclass);
509 		}
510 		tsigname = NULL;
511 		tsig = dns_message_gettsig(msg, &tsigname);
512 		if (tsig != NULL) {
513 			printf(";; PSEUDOSECTIONS: TSIG\n");
514 		}
515 	}
516 	if (!ISC_LIST_EMPTY(msg->sections[DNS_SECTION_QUESTION]) && !short_form)
517 	{
518 		printf("\n");
519 		result = printsection(msg, DNS_SECTION_QUESTION, "QUESTION",
520 				      true, query);
521 		if (result != ISC_R_SUCCESS) {
522 			return result;
523 		}
524 	}
525 	if (!ISC_LIST_EMPTY(msg->sections[DNS_SECTION_ANSWER])) {
526 		if (!short_form) {
527 			printf("\n");
528 		}
529 		result = printsection(msg, DNS_SECTION_ANSWER, "ANSWER",
530 				      !short_form, query);
531 		if (result != ISC_R_SUCCESS) {
532 			return result;
533 		}
534 	}
535 
536 	if (!ISC_LIST_EMPTY(msg->sections[DNS_SECTION_AUTHORITY]) &&
537 	    !short_form)
538 	{
539 		printf("\n");
540 		result = printsection(msg, DNS_SECTION_AUTHORITY, "AUTHORITY",
541 				      true, query);
542 		if (result != ISC_R_SUCCESS) {
543 			return result;
544 		}
545 	}
546 	if (!ISC_LIST_EMPTY(msg->sections[DNS_SECTION_ADDITIONAL]) &&
547 	    !short_form)
548 	{
549 		printf("\n");
550 		result = printsection(msg, DNS_SECTION_ADDITIONAL, "ADDITIONAL",
551 				      true, query);
552 		if (result != ISC_R_SUCCESS) {
553 			return result;
554 		}
555 	}
556 	if ((tsig != NULL) && !short_form) {
557 		printf("\n");
558 		result = printrdata(msg, tsig, tsigname, "PSEUDOSECTION TSIG",
559 				    true);
560 		if (result != ISC_R_SUCCESS) {
561 			return result;
562 		}
563 	}
564 	if (!short_form) {
565 		printf("\n");
566 	}
567 
568 	if (short_form && !default_lookups &&
569 	    ISC_LIST_EMPTY(msg->sections[DNS_SECTION_ANSWER]))
570 	{
571 		char namestr[DNS_NAME_FORMATSIZE];
572 		char typestr[DNS_RDATATYPE_FORMATSIZE];
573 		dns_name_format(query->lookup->name, namestr, sizeof(namestr));
574 		dns_rdatatype_format(query->lookup->rdtype, typestr,
575 				     sizeof(typestr));
576 		printf("%s has no %s record\n", namestr, typestr);
577 	}
578 	seen_error = force_error;
579 	return result;
580 }
581 
582 static const char *optstring = "46aAc:dilnm:p:rst:vVwCDN:R:TUW:";
583 
584 static void
585 pre_parse_args(int argc, char **argv) {
586 	int c;
587 
588 	while ((c = isc_commandline_parse(argc, argv, optstring)) != -1) {
589 		switch (c) {
590 		case 'm':
591 			memdebugging = true;
592 			if (strcasecmp("trace", isc_commandline_argument) == 0)
593 			{
594 				isc_mem_debugging |= ISC_MEM_DEBUGTRACE;
595 			} else if (strcasecmp("record",
596 					      isc_commandline_argument) == 0)
597 			{
598 				isc_mem_debugging |= ISC_MEM_DEBUGRECORD;
599 			} else if (strcasecmp("usage",
600 					      isc_commandline_argument) == 0)
601 			{
602 				isc_mem_debugging |= ISC_MEM_DEBUGUSAGE;
603 			}
604 			break;
605 
606 		case '4':
607 			if (ipv6only) {
608 				fatal("only one of -4 and -6 allowed");
609 			}
610 			ipv4only = true;
611 			break;
612 		case '6':
613 			if (ipv4only) {
614 				fatal("only one of -4 and -6 allowed");
615 			}
616 			ipv6only = true;
617 			break;
618 		case 'a':
619 			break;
620 		case 'A':
621 			break;
622 		case 'c':
623 			break;
624 		case 'C':
625 			break;
626 		case 'd':
627 			break;
628 		case 'D':
629 			if (debugging) {
630 				debugtiming = true;
631 			}
632 			debugging = true;
633 			break;
634 		case 'i':
635 			break;
636 		case 'l':
637 			break;
638 		case 'n':
639 			break;
640 		case 'N':
641 			break;
642 		case 'p':
643 			break;
644 		case 'r':
645 			break;
646 		case 'R':
647 			break;
648 		case 's':
649 			break;
650 		case 't':
651 			break;
652 		case 'T':
653 			break;
654 		case 'U':
655 			break;
656 		case 'v':
657 			break;
658 		case 'V':
659 			printf("host %s\n", PACKAGE_VERSION);
660 			exit(EXIT_SUCCESS);
661 			break;
662 		case 'w':
663 			break;
664 		case 'W':
665 			break;
666 		default:
667 			show_usage();
668 		}
669 	}
670 	isc_commandline_reset = true;
671 	isc_commandline_index = 1;
672 }
673 
674 static void
675 parse_args(bool is_batchfile, int argc, char **argv) {
676 	char hostname[MXNAME];
677 	dig_lookup_t *lookup;
678 	int c;
679 	char store[MXNAME];
680 	isc_textregion_t tr;
681 	isc_result_t result = ISC_R_SUCCESS;
682 	dns_rdatatype_t rdtype;
683 	dns_rdataclass_t rdclass;
684 	uint32_t serial = 0;
685 
686 	UNUSED(is_batchfile);
687 
688 	lookup = make_empty_lookup();
689 
690 	lookup->servfail_stops = false;
691 	lookup->besteffort = false;
692 	lookup->comments = false;
693 	short_form = !verbose;
694 
695 	while ((c = isc_commandline_parse(argc, argv, optstring)) != -1) {
696 		switch (c) {
697 		case 'l':
698 			lookup->tcp_mode = true;
699 			lookup->rdtype = dns_rdatatype_axfr;
700 			lookup->rdtypeset = true;
701 			fatalexit = 3;
702 			break;
703 		case 'v':
704 		case 'd':
705 			short_form = false;
706 			break;
707 		case 'r':
708 			lookup->recurse = false;
709 			break;
710 		case 't':
711 			if (strncasecmp(isc_commandline_argument, "ixfr=", 5) ==
712 			    0)
713 			{
714 				rdtype = dns_rdatatype_ixfr;
715 				/* XXXMPA add error checking */
716 				serial = strtoul(isc_commandline_argument + 5,
717 						 NULL, 10);
718 				result = ISC_R_SUCCESS;
719 			} else {
720 				tr.base = isc_commandline_argument;
721 				tr.length = strlen(isc_commandline_argument);
722 				result = dns_rdatatype_fromtext(
723 					&rdtype, (isc_textregion_t *)&tr);
724 			}
725 
726 			if (result != ISC_R_SUCCESS) {
727 				fatalexit = 2;
728 				fatal("invalid type: %s\n",
729 				      isc_commandline_argument);
730 			}
731 			if (!lookup->rdtypeset ||
732 			    lookup->rdtype != dns_rdatatype_axfr)
733 			{
734 				lookup->rdtype = rdtype;
735 			}
736 			lookup->rdtypeset = true;
737 			if (rdtype == dns_rdatatype_axfr) {
738 				/* -l -t any -v */
739 				list_type = dns_rdatatype_any;
740 				short_form = false;
741 				lookup->tcp_mode = true;
742 			} else if (rdtype == dns_rdatatype_ixfr) {
743 				lookup->ixfr_serial = serial;
744 				lookup->tcp_mode = true;
745 				list_type = rdtype;
746 			} else if (rdtype == dns_rdatatype_any) {
747 				if (!lookup->tcp_mode_set) {
748 					lookup->tcp_mode = true;
749 				}
750 			} else {
751 				list_type = rdtype;
752 			}
753 			list_addresses = false;
754 			default_lookups = false;
755 			break;
756 		case 'c':
757 			tr.base = isc_commandline_argument;
758 			tr.length = strlen(isc_commandline_argument);
759 			result = dns_rdataclass_fromtext(
760 				&rdclass, (isc_textregion_t *)&tr);
761 
762 			if (result != ISC_R_SUCCESS) {
763 				fatalexit = 2;
764 				fatal("invalid class: %s\n",
765 				      isc_commandline_argument);
766 			} else {
767 				lookup->rdclass = rdclass;
768 				lookup->rdclassset = true;
769 			}
770 			default_lookups = false;
771 			break;
772 		case 'A':
773 			list_almost_all = true;
774 			FALLTHROUGH;
775 		case 'a':
776 			if (!lookup->rdtypeset ||
777 			    lookup->rdtype != dns_rdatatype_axfr)
778 			{
779 				lookup->rdtype = dns_rdatatype_any;
780 			}
781 			list_type = dns_rdatatype_any;
782 			list_addresses = false;
783 			lookup->rdtypeset = true;
784 			short_form = false;
785 			default_lookups = false;
786 			break;
787 		case 'i':
788 			/* deprecated */
789 			break;
790 		case 'n':
791 			/* deprecated */
792 			break;
793 		case 'm':
794 			/* Handled by pre_parse_args(). */
795 			break;
796 		case 'w':
797 			/*
798 			 * The timer routines are coded such that
799 			 * timeout==MAXINT doesn't enable the timer
800 			 */
801 			timeout = INT_MAX;
802 			break;
803 		case 'W':
804 			timeout = atoi(isc_commandline_argument);
805 			if (timeout < 1) {
806 				timeout = 1;
807 			}
808 			break;
809 		case 'R':
810 			tries = atoi(isc_commandline_argument) + 1;
811 			if (tries < 2) {
812 				tries = 2;
813 			}
814 			break;
815 		case 'T':
816 			lookup->tcp_mode = true;
817 			lookup->tcp_mode_set = true;
818 			break;
819 		case 'U':
820 			lookup->tcp_mode = false;
821 			lookup->tcp_mode_set = true;
822 			break;
823 		case 'C':
824 			debug("showing all SOAs");
825 			lookup->rdtype = dns_rdatatype_ns;
826 			lookup->rdtypeset = true;
827 			lookup->rdclass = dns_rdataclass_in;
828 			lookup->rdclassset = true;
829 			lookup->ns_search_only = true;
830 			lookup->trace_root = true;
831 			lookup->identify_previous_line = true;
832 			default_lookups = false;
833 			break;
834 		case 'N':
835 			debug("setting NDOTS to %s", isc_commandline_argument);
836 			ndots = atoi(isc_commandline_argument);
837 			break;
838 		case 'D':
839 			/* Handled by pre_parse_args(). */
840 			break;
841 		case '4':
842 			/* Handled by pre_parse_args(). */
843 			break;
844 		case '6':
845 			/* Handled by pre_parse_args(). */
846 			break;
847 		case 's':
848 			lookup->servfail_stops = true;
849 			break;
850 		case 'p':
851 			port = atoi(isc_commandline_argument);
852 			port_set = true;
853 			break;
854 		}
855 	}
856 
857 	lookup->retries = tries;
858 
859 	if (isc_commandline_index >= argc) {
860 		show_usage();
861 	}
862 
863 	strlcpy(hostname, argv[isc_commandline_index], sizeof(hostname));
864 
865 	if (argc > isc_commandline_index + 1) {
866 		set_nameserver(argv[isc_commandline_index + 1]);
867 		debug("server is %s", argv[isc_commandline_index + 1]);
868 		listed_server = true;
869 	} else {
870 		check_ra = true;
871 	}
872 
873 	lookup->pending = false;
874 	if (get_reverse(store, sizeof(store), hostname, true) == ISC_R_SUCCESS)
875 	{
876 		strlcpy(lookup->textname, store, sizeof(lookup->textname));
877 		lookup->rdtype = dns_rdatatype_ptr;
878 		lookup->rdtypeset = true;
879 		default_lookups = false;
880 	} else {
881 		strlcpy(lookup->textname, hostname, sizeof(lookup->textname));
882 		usesearch = true;
883 	}
884 	lookup->new_search = true;
885 	ISC_LIST_APPEND(lookup_list, lookup, link);
886 }
887 
888 int
889 main(int argc, char **argv) {
890 	tries = 2;
891 
892 	ISC_LIST_INIT(lookup_list);
893 	ISC_LIST_INIT(server_list);
894 	ISC_LIST_INIT(search_list);
895 
896 	fatalexit = 1;
897 
898 	/* setup dighost callbacks */
899 	dighost_printmessage = printmessage;
900 	dighost_received = received;
901 	dighost_trying = trying;
902 	dighost_shutdown = host_shutdown;
903 
904 	debug("main()");
905 	progname = argv[0];
906 	pre_parse_args(argc, argv);
907 	setup_libs();
908 	setup_system(ipv4only, ipv6only);
909 	parse_args(false, argc, argv);
910 	if (keyfile[0] != 0) {
911 		setup_file_key();
912 	} else if (keysecret[0] != 0) {
913 		setup_text_key();
914 	}
915 
916 	isc_loopmgr_setup(loopmgr, run_loop, NULL);
917 	isc_loopmgr_run(loopmgr);
918 
919 	cancel_all();
920 	destroy_libs();
921 
922 	return (seen_error == 0) ? 0 : 1;
923 }
924