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