xref: /openbsd/usr.bin/dig/host.c (revision 4bdff4be)
1 /*
2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14  * PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 /*! \file */
18 #include <sys/types.h>
19 
20 #include <err.h>
21 #include <limits.h>
22 #include <locale.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <time.h>
27 #include <stdint.h>
28 
29 #include <isc/app.h>
30 #include <isc/util.h>
31 
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/rdatatype.h>
39 
40 #include "dig.h"
41 
42 static int short_form = 1, listed_server = 0;
43 static int default_lookups = 1;
44 static int seen_error = -1;
45 static int list_addresses = 1;
46 static dns_rdatatype_t list_type = dns_rdatatype_a;
47 static int printed_server = 0;
48 static int ipv4only = 0, ipv6only = 0;
49 
50 static const char *opcodetext[] = {
51 	"QUERY",
52 	"IQUERY",
53 	"STATUS",
54 	"RESERVED3",
55 	"NOTIFY",
56 	"UPDATE",
57 	"RESERVED6",
58 	"RESERVED7",
59 	"RESERVED8",
60 	"RESERVED9",
61 	"RESERVED10",
62 	"RESERVED11",
63 	"RESERVED12",
64 	"RESERVED13",
65 	"RESERVED14",
66 	"RESERVED15"
67 };
68 
69 static const char *rcodetext[] = {
70 	"NOERROR",
71 	"FORMERR",
72 	"SERVFAIL",
73 	"NXDOMAIN",
74 	"NOTIMP",
75 	"REFUSED",
76 	"YXDOMAIN",
77 	"YXRRSET",
78 	"NXRRSET",
79 	"NOTAUTH",
80 	"NOTZONE",
81 	"RESERVED11",
82 	"RESERVED12",
83 	"RESERVED13",
84 	"RESERVED14",
85 	"RESERVED15",
86 	"BADVERS"
87 };
88 
89 struct rtype {
90 	unsigned int type;
91 	const char *text;
92 };
93 
94 static struct rtype rtypes[] = {
95 	{ 1, 	"has address" },
96 	{ 2, 	"name server" },
97 	{ 5, 	"is an alias for" },
98 	{ 11,	"has well known services" },
99 	{ 12,	"domain name pointer" },
100 	{ 13,	"host information" },
101 	{ 15,	"mail is handled by" },
102 	{ 16,	"descriptive text" },
103 	{ 19,	"x25 address" },
104 	{ 20,	"ISDN address" },
105 	{ 24,	"has signature" },
106 	{ 25,	"has key" },
107 	{ 28,	"has IPv6 address" },
108 	{ 29,	"location" },
109 	{ 0, NULL }
110 };
111 
112 static char *
113 rcode_totext(dns_rcode_t rcode)
114 {
115 	static char buf[sizeof("?65535")];
116 	union {
117 		const char *consttext;
118 		char *deconsttext;
119 	} totext;
120 
121 	if (rcode >= (sizeof(rcodetext)/sizeof(rcodetext[0]))) {
122 		snprintf(buf, sizeof(buf), "?%u", rcode);
123 		totext.deconsttext = buf;
124 	} else
125 		totext.consttext = rcodetext[rcode];
126 	return totext.deconsttext;
127 }
128 
129 static __dead void
130 show_usage(void);
131 
132 static void
133 show_usage(void) {
134 	fputs(
135 "usage: host [-46aCdilrsTVvw] [-c class] [-m flag] [-N ndots] [-R number]\n"
136 "            [-t type] [-W wait] name [server]\n", stderr);
137 	exit(1);
138 }
139 
140 static void
141 host_shutdown(void) {
142 	(void) isc_app_shutdown();
143 }
144 
145 static void
146 received(unsigned int bytes, struct sockaddr_storage *from, dig_query_t *query) {
147 	struct timespec now;
148 
149 	if (!short_form) {
150 		char fromtext[ISC_SOCKADDR_FORMATSIZE];
151 		isc_sockaddr_format(from, fromtext, sizeof(fromtext));
152 		clock_gettime(CLOCK_MONOTONIC, &now);
153 		printf("Received %u bytes from %s in %lld ms\n",
154 		    bytes, fromtext, uelapsed(&now, &query->time_sent)/1000);
155 	}
156 }
157 
158 static void
159 trying(char *frm, dig_lookup_t *lookup) {
160 	UNUSED(lookup);
161 
162 	if (!short_form)
163 		printf("Trying \"%s\"\n", frm);
164 }
165 
166 static void
167 say_message(dns_name_t *name, const char *msg, dns_rdata_t *rdata,
168 	    dig_query_t *query)
169 {
170 	isc_buffer_t *b = NULL;
171 	char namestr[DNS_NAME_FORMATSIZE];
172 	isc_region_t r;
173 	isc_result_t result;
174 	unsigned int bufsize = BUFSIZ;
175 
176 	dns_name_format(name, namestr, sizeof(namestr));
177  retry:
178 	result = isc_buffer_allocate(&b, bufsize);
179 	check_result(result, "isc_buffer_allocate");
180 	result = dns_rdata_totext(rdata, NULL, b);
181 	if (result == ISC_R_NOSPACE) {
182 		isc_buffer_free(&b);
183 		bufsize *= 2;
184 		goto retry;
185 	}
186 	check_result(result, "dns_rdata_totext");
187 	isc_buffer_usedregion(b, &r);
188 	if (query->lookup->identify_previous_line) {
189 		printf("Nameserver %s:\n\t",
190 			query->servname);
191 	}
192 	printf("%s %s %.*s", namestr,
193 	       msg, (int)r.length, (char *)r.base);
194 	if (query->lookup->identify) {
195 		printf(" on server %s", query->servname);
196 	}
197 	printf("\n");
198 	isc_buffer_free(&b);
199 }
200 static isc_result_t
201 printsection(dns_message_t *msg, dns_section_t sectionid,
202 	     const char *section_name, int headers,
203 	     dig_query_t *query)
204 {
205 	dns_name_t *name, *print_name;
206 	dns_rdataset_t *rdataset;
207 	dns_rdata_t rdata = DNS_RDATA_INIT;
208 	isc_buffer_t target;
209 	isc_result_t result, loopresult;
210 	isc_region_t r;
211 	dns_name_t empty_name;
212 	char tbuf[4096];
213 	int first;
214 	int no_rdata;
215 
216 	if (sectionid == DNS_SECTION_QUESTION)
217 		no_rdata = 1;
218 	else
219 		no_rdata = 0;
220 
221 	if (headers)
222 		printf(";; %s SECTION:\n", section_name);
223 
224 	dns_name_init(&empty_name, NULL);
225 
226 	result = dns_message_firstname(msg, sectionid);
227 	if (result == ISC_R_NOMORE)
228 		return (ISC_R_SUCCESS);
229 	else if (result != ISC_R_SUCCESS)
230 		return (result);
231 
232 	for (;;) {
233 		name = NULL;
234 		dns_message_currentname(msg, sectionid, &name);
235 
236 		isc_buffer_init(&target, tbuf, sizeof(tbuf));
237 		first = 1;
238 		print_name = name;
239 
240 		for (rdataset = ISC_LIST_HEAD(name->list);
241 		     rdataset != NULL;
242 		     rdataset = ISC_LIST_NEXT(rdataset, link)) {
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 				continue;
253 			if (!short_form) {
254 				result = dns_rdataset_totext(rdataset,
255 							     print_name,
256 							     0,
257 							     no_rdata,
258 							     &target);
259 				if (result != ISC_R_SUCCESS)
260 					return (result);
261 				UNUSED(first); /* Shut up compiler. */
262 			} else {
263 				loopresult = dns_rdataset_first(rdataset);
264 				while (loopresult == ISC_R_SUCCESS) {
265 					struct rtype *t;
266 					const char *rtt;
267 					char typebuf[DNS_RDATATYPE_FORMATSIZE];
268 					char typebuf2[DNS_RDATATYPE_FORMATSIZE
269 						     + 20];
270 					dns_rdataset_current(rdataset, &rdata);
271 
272 					for (t = rtypes; t->text != NULL; t++) {
273 						if (t->type == rdata.type) {
274 							rtt = t->text;
275 							goto found;
276 						}
277 					}
278 
279 					dns_rdatatype_format(rdata.type,
280 							     typebuf,
281 							     sizeof(typebuf));
282 					snprintf(typebuf2, sizeof(typebuf2),
283 						 "has %s record", typebuf);
284 					rtt = typebuf2;
285 				found:
286 					say_message(print_name, rtt,
287 						    &rdata, query);
288 					dns_rdata_reset(&rdata);
289 					loopresult =
290 						dns_rdataset_next(rdataset);
291 				}
292 			}
293 		}
294 		if (!short_form) {
295 			isc_buffer_usedregion(&target, &r);
296 			if (no_rdata)
297 				printf(";%.*s", (int)r.length,
298 				       (char *)r.base);
299 			else
300 				printf("%.*s", (int)r.length, (char *)r.base);
301 		}
302 
303 		result = dns_message_nextname(msg, sectionid);
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 printrdata(dns_message_t *msg, dns_rdataset_t *rdataset, dns_name_t *owner,
315 	   const char *set_name, int headers)
316 {
317 	isc_buffer_t target;
318 	isc_result_t result;
319 	isc_region_t r;
320 	char tbuf[4096];
321 
322 	UNUSED(msg);
323 	if (headers)
324 		printf(";; %s SECTION:\n", set_name);
325 
326 	isc_buffer_init(&target, tbuf, sizeof(tbuf));
327 
328 	result = dns_rdataset_totext(rdataset, owner, 0, 0,
329 				     &target);
330 	if (result != ISC_R_SUCCESS)
331 		return (result);
332 	isc_buffer_usedregion(&target, &r);
333 	printf("%.*s", (int)r.length, (char *)r.base);
334 
335 	return (ISC_R_SUCCESS);
336 }
337 
338 static void
339 chase_cnamechain(dns_message_t *msg, dns_name_t *qname) {
340 	isc_result_t result;
341 	dns_rdataset_t *rdataset;
342 	dns_rdata_cname_t cname;
343 	dns_rdata_t rdata = DNS_RDATA_INIT;
344 	unsigned int i = msg->counts[DNS_SECTION_ANSWER];
345 
346 	while (i-- > 0) {
347 		rdataset = NULL;
348 		result = dns_message_findname(msg, DNS_SECTION_ANSWER, qname,
349 					      dns_rdatatype_cname, 0, NULL,
350 					      &rdataset);
351 		if (result != ISC_R_SUCCESS)
352 			return;
353 		result = dns_rdataset_first(rdataset);
354 		check_result(result, "dns_rdataset_first");
355 		dns_rdata_reset(&rdata);
356 		dns_rdataset_current(rdataset, &rdata);
357 		result = dns_rdata_tostruct_cname(&rdata, &cname);
358 		check_result(result, "dns_rdata_tostruct_cname");
359 		dns_name_copy(&cname.cname, qname, NULL);
360 		dns_rdata_freestruct_cname(&cname);
361 	}
362 }
363 
364 static isc_result_t
365 printmessage(dig_query_t *query, dns_message_t *msg, int headers) {
366 	int did_flag = 0;
367 	dns_rdataset_t *opt, *tsig = NULL;
368 	dns_name_t *tsigname;
369 	isc_result_t result = ISC_R_SUCCESS;
370 	int force_error;
371 
372 	UNUSED(headers);
373 
374 	/*
375 	 * We get called multiple times.
376 	 * Preserve any existing error status.
377 	 */
378 	force_error = (seen_error == 1) ? 1 : 0;
379 	seen_error = 1;
380 	if (listed_server && !printed_server) {
381 		char sockstr[ISC_SOCKADDR_FORMATSIZE];
382 
383 		printf("Using domain server:\n");
384 		printf("Name: %s\n", query->userarg);
385 		isc_sockaddr_format(&query->sockaddr, sockstr,
386 				    sizeof(sockstr));
387 		printf("Address: %s\n", sockstr);
388 		printf("Aliases: \n\n");
389 		printed_server = 1;
390 	}
391 
392 	if (msg->rcode != 0) {
393 		char namestr[DNS_NAME_FORMATSIZE];
394 		dns_name_format(query->lookup->name, namestr, sizeof(namestr));
395 
396 		if (query->lookup->identify_previous_line)
397 			printf("Nameserver %s:\n\t%s not found: %d(%s)\n",
398 			       query->servname,
399 			       (msg->rcode != dns_rcode_nxdomain) ? namestr :
400 			       query->lookup->textname, msg->rcode,
401 			       rcode_totext(msg->rcode));
402 		else
403 			printf("Host %s not found: %d(%s)\n",
404 			       (msg->rcode != dns_rcode_nxdomain) ? namestr :
405 			       query->lookup->textname, msg->rcode,
406 			       rcode_totext(msg->rcode));
407 		return (ISC_R_SUCCESS);
408 	}
409 
410 	if (default_lookups && query->lookup->rdtype == dns_rdatatype_a) {
411 		char namestr[DNS_NAME_FORMATSIZE];
412 		dig_lookup_t *lookup;
413 		dns_fixedname_t fixed;
414 		dns_name_t *name;
415 
416 		/* Add AAAA and MX lookups. */
417 		dns_fixedname_init(&fixed);
418 		name = dns_fixedname_name(&fixed);
419 		dns_name_copy(query->lookup->name, name, NULL);
420 		chase_cnamechain(msg, name);
421 		dns_name_format(name, namestr, sizeof(namestr));
422 		lookup = clone_lookup(query->lookup, 0);
423 		if (lookup != NULL) {
424 			strlcpy(lookup->textname, namestr,
425 				sizeof(lookup->textname));
426 			lookup->rdtype = dns_rdatatype_aaaa;
427 			lookup->rdtypeset = 1;
428 			lookup->origin = NULL;
429 			lookup->retries = tries;
430 			ISC_LIST_APPEND(lookup_list, lookup, link);
431 		}
432 		lookup = clone_lookup(query->lookup, 0);
433 		if (lookup != NULL) {
434 			strlcpy(lookup->textname, namestr,
435 				sizeof(lookup->textname));
436 			lookup->rdtype = dns_rdatatype_mx;
437 			lookup->rdtypeset = 1;
438 			lookup->origin = NULL;
439 			lookup->retries = tries;
440 			ISC_LIST_APPEND(lookup_list, lookup, link);
441 		}
442 	}
443 
444 	if (!short_form) {
445 		printf(";; ->>HEADER<<- opcode: %s, status: %s, id: %u\n",
446 		       opcodetext[msg->opcode], rcode_totext(msg->rcode),
447 		       msg->id);
448 		printf(";; flags: ");
449 		if ((msg->flags & DNS_MESSAGEFLAG_QR) != 0) {
450 			printf("qr");
451 			did_flag = 1;
452 		}
453 		if ((msg->flags & DNS_MESSAGEFLAG_AA) != 0) {
454 			printf("%saa", did_flag ? " " : "");
455 			did_flag = 1;
456 		}
457 		if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0) {
458 			printf("%stc", did_flag ? " " : "");
459 			did_flag = 1;
460 		}
461 		if ((msg->flags & DNS_MESSAGEFLAG_RD) != 0) {
462 			printf("%srd", did_flag ? " " : "");
463 			did_flag = 1;
464 		}
465 		if ((msg->flags & DNS_MESSAGEFLAG_RA) != 0) {
466 			printf("%sra", did_flag ? " " : "");
467 			did_flag = 1;
468 		}
469 		if ((msg->flags & DNS_MESSAGEFLAG_AD) != 0) {
470 			printf("%sad", did_flag ? " " : "");
471 			did_flag = 1;
472 		}
473 		if ((msg->flags & DNS_MESSAGEFLAG_CD) != 0) {
474 			printf("%scd", did_flag ? " " : "");
475 			did_flag = 1;
476 			POST(did_flag);
477 		}
478 		printf("; QUERY: %u, ANSWER: %u, "
479 		       "AUTHORITY: %u, ADDITIONAL: %u\n",
480 		       msg->counts[DNS_SECTION_QUESTION],
481 		       msg->counts[DNS_SECTION_ANSWER],
482 		       msg->counts[DNS_SECTION_AUTHORITY],
483 		       msg->counts[DNS_SECTION_ADDITIONAL]);
484 		opt = dns_message_getopt(msg);
485 		if (opt != NULL)
486 			printf(";; EDNS: version: %u, udp=%u\n",
487 			       (unsigned int)((opt->ttl & 0x00ff0000) >> 16),
488 			       (unsigned int)opt->rdclass);
489 		tsigname = NULL;
490 		tsig = dns_message_gettsig(msg, &tsigname);
491 		if (tsig != NULL)
492 			printf(";; PSEUDOSECTIONS: TSIG\n");
493 	}
494 	if (! ISC_LIST_EMPTY(msg->sections[DNS_SECTION_QUESTION]) &&
495 	    !short_form) {
496 		printf("\n");
497 		result = printsection(msg, DNS_SECTION_QUESTION, "QUESTION",
498 				      1, query);
499 		if (result != ISC_R_SUCCESS)
500 			return (result);
501 	}
502 	if (! ISC_LIST_EMPTY(msg->sections[DNS_SECTION_ANSWER])) {
503 		if (!short_form)
504 			printf("\n");
505 		result = printsection(msg, DNS_SECTION_ANSWER, "ANSWER",
506 				      !short_form, query);
507 		if (result != ISC_R_SUCCESS)
508 			return (result);
509 	}
510 
511 	if (! ISC_LIST_EMPTY(msg->sections[DNS_SECTION_AUTHORITY]) &&
512 	    !short_form) {
513 		printf("\n");
514 		result = printsection(msg, DNS_SECTION_AUTHORITY, "AUTHORITY",
515 				      1, query);
516 		if (result != ISC_R_SUCCESS)
517 			return (result);
518 	}
519 	if (! ISC_LIST_EMPTY(msg->sections[DNS_SECTION_ADDITIONAL]) &&
520 	    !short_form) {
521 		printf("\n");
522 		result = printsection(msg, DNS_SECTION_ADDITIONAL,
523 				      "ADDITIONAL", 1, query);
524 		if (result != ISC_R_SUCCESS)
525 			return (result);
526 	}
527 	if ((tsig != NULL) && !short_form) {
528 		printf("\n");
529 		result = printrdata(msg, tsig, tsigname,
530 				    "PSEUDOSECTION TSIG", 1);
531 		if (result != ISC_R_SUCCESS)
532 			return (result);
533 	}
534 	if (!short_form)
535 		printf("\n");
536 
537 	if (short_form && !default_lookups &&
538 	    ISC_LIST_EMPTY(msg->sections[DNS_SECTION_ANSWER])) {
539 		char namestr[DNS_NAME_FORMATSIZE];
540 		char typestr[DNS_RDATATYPE_FORMATSIZE];
541 		dns_name_format(query->lookup->name, namestr, sizeof(namestr));
542 		dns_rdatatype_format(query->lookup->rdtype, typestr,
543 				     sizeof(typestr));
544 		printf("%s has no %s record\n", namestr, typestr);
545 	}
546 	seen_error = force_error;
547 	return (result);
548 }
549 
550 static const char * optstring = "46ac:dilnrst:vVwCDN:R:TW:";
551 
552 /*% version */
553 static void
554 version(void) {
555 	fputs("host " VERSION "\n", stderr);
556 }
557 
558 static void
559 pre_parse_args(int argc, char **argv) {
560 	int c;
561 
562 	while ((c = getopt(argc, argv, optstring)) != -1) {
563 		switch (c) {
564 		case '4':
565 			if (ipv6only)
566 				fatal("only one of -4 and -6 allowed");
567 			ipv4only = 1;
568 			break;
569 		case '6':
570 			if (ipv4only)
571 				fatal("only one of -4 and -6 allowed");
572 			ipv6only = 1;
573 			break;
574 		case 'a': break;
575 		case 'c': break;
576 		case 'd': break;
577 		case 'i': break;
578 		case 'l': break;
579 		case 'n': break;
580 		case 'r': break;
581 		case 's': break;
582 		case 't': break;
583 		case 'v': break;
584 		case 'V':
585 			  version();
586 			  exit(0);
587 			  break;
588 		case 'w': break;
589 		case 'C': break;
590 		case 'D':
591 			if (debugging)
592 				debugtiming = 1;
593 			debugging = 1;
594 			break;
595 		case 'N': break;
596 		case 'R': break;
597 		case 'T': break;
598 		case 'W': break;
599 		default:
600 			show_usage();
601 		}
602 	}
603 	optind = 1;
604 	optreset = 1;
605 }
606 
607 static void
608 parse_args(int argc, char **argv) {
609 	char hostname[MXNAME];
610 	dig_lookup_t *lookup;
611 	int c;
612 	char store[MXNAME];
613 	isc_textregion_t tr;
614 	isc_result_t result = ISC_R_SUCCESS;
615 	dns_rdatatype_t rdtype;
616 	dns_rdataclass_t rdclass;
617 	uint32_t serial = 0;
618 	const char *errstr;
619 
620 	lookup = make_empty_lookup();
621 
622 	lookup->servfail_stops = 0;
623 	lookup->comments = 0;
624 
625 	while ((c = getopt(argc, argv, optstring)) != -1) {
626 		switch (c) {
627 		case 'l':
628 			lookup->tcp_mode = 1;
629 			lookup->rdtype = dns_rdatatype_axfr;
630 			lookup->rdtypeset = 1;
631 			fatalexit = 3;
632 			break;
633 		case 'v':
634 		case 'd':
635 			short_form = 0;
636 			break;
637 		case 'r':
638 			lookup->recurse = 0;
639 			break;
640 		case 't':
641 			if (strncasecmp(optarg,	"ixfr=", 5) == 0) {
642 				rdtype = dns_rdatatype_ixfr;
643 				/* XXXMPA add error checking */
644 				serial = strtoul(optarg + 5,
645 						 NULL, 10);
646 				result = ISC_R_SUCCESS;
647 			} else {
648 				tr.base = optarg;
649 				tr.length = strlen(optarg);
650 				result = dns_rdatatype_fromtext(&rdtype,
651 						   (isc_textregion_t *)&tr);
652 			}
653 
654 			if (result != ISC_R_SUCCESS) {
655 				fatalexit = 2;
656 				fatal("invalid type: %s\n", optarg);
657 			}
658 			if (!lookup->rdtypeset ||
659 			    lookup->rdtype != dns_rdatatype_axfr)
660 				lookup->rdtype = rdtype;
661 			lookup->rdtypeset = 1;
662 			if (rdtype == dns_rdatatype_axfr) {
663 				/* -l -t any -v */
664 				list_type = dns_rdatatype_any;
665 				short_form = 0;
666 				lookup->tcp_mode = 1;
667 			} else if (rdtype == dns_rdatatype_ixfr) {
668 				lookup->ixfr_serial = serial;
669 				lookup->tcp_mode = 1;
670 				list_type = rdtype;
671 			} else
672 				list_type = rdtype;
673 			list_addresses = 0;
674 			default_lookups = 0;
675 			break;
676 		case 'c':
677 			tr.base = optarg;
678 			tr.length = strlen(optarg);
679 			result = dns_rdataclass_fromtext(&rdclass,
680 						   (isc_textregion_t *)&tr);
681 
682 			if (result != ISC_R_SUCCESS) {
683 				fatalexit = 2;
684 				fatal("invalid class: %s\n", optarg);
685 			} else {
686 				lookup->rdclass = rdclass;
687 				lookup->rdclassset = 1;
688 			}
689 			default_lookups = 0;
690 			break;
691 		case 'a':
692 			if (!lookup->rdtypeset ||
693 			    lookup->rdtype != dns_rdatatype_axfr)
694 				lookup->rdtype = dns_rdatatype_any;
695 			list_type = dns_rdatatype_any;
696 			list_addresses = 0;
697 			lookup->rdtypeset = 1;
698 			short_form = 0;
699 			default_lookups = 0;
700 			break;
701 		case 'i':
702 			lookup->ip6_int = 1;
703 			break;
704 		case 'n':
705 			/* deprecated */
706 			break;
707 		case 'm':
708 			/* Handled by pre_parse_args(). */
709 			break;
710 		case 'w':
711 			/*
712 			 * The timer routines are coded such that
713 			 * timeout==MAXINT doesn't enable the timer
714 			 */
715 			timeout = INT_MAX;
716 			break;
717 		case 'W':
718 			timeout = strtonum(optarg, 0, INT_MAX, &errstr);
719 			if (errstr != NULL)
720 				errx(1, "timeout is %s: %s", errstr, optarg);
721 			if (timeout < 1)
722 				timeout = 1;
723 			break;
724 		case 'R':
725 			tries = strtonum(optarg, INT_MIN, INT_MAX - 1, &errstr);
726 			if (errstr != NULL)
727 				errx(1, "retries is %s: %s", errstr, optarg);
728 			tries++;
729 			if (tries < 2)
730 				tries = 2;
731 			break;
732 		case 'T':
733 			lookup->tcp_mode = 1;
734 			break;
735 		case 'C':
736 			debug("showing all SOAs");
737 			lookup->rdtype = dns_rdatatype_ns;
738 			lookup->rdtypeset = 1;
739 			lookup->rdclass = dns_rdataclass_in;
740 			lookup->rdclassset = 1;
741 			lookup->ns_search_only = 1;
742 			lookup->trace_root = 1;
743 			lookup->identify_previous_line = 1;
744 			default_lookups = 0;
745 			break;
746 		case 'N':
747 			debug("setting NDOTS to %s", optarg);
748 			ndots = strtonum(optarg, 0, INT_MAX, &errstr);
749 			if (errstr != NULL)
750 				errx(1, "ndots is %s: %s", errstr, optarg);
751 			break;
752 		case 'D':
753 			/* Handled by pre_parse_args(). */
754 			break;
755 		case '4':
756 			/* Handled by pre_parse_args(). */
757 			break;
758 		case '6':
759 			/* Handled by pre_parse_args(). */
760 			break;
761 		case 's':
762 			lookup->servfail_stops = 1;
763 			break;
764 		default:
765 			show_usage();
766 		}
767 	}
768 
769 	lookup->retries = tries;
770 
771 	argc -= optind;
772 	argv += optind;
773 
774 	if (argc == 0)
775 		show_usage();
776 
777 	strlcpy(hostname, argv[0], sizeof(hostname));
778 
779 	if (argc >= 2) {
780 		isc_result_t res;
781 
782 		if ((res = set_nameserver(argv[1])))
783 			fatal("couldn't get address for '%s': %s",
784 			    argv[1], isc_result_totext(res));
785 		debug("server is %s", *argv + 1);
786 		listed_server = 1;
787 	} else
788 		check_ra = 1;
789 
790 	lookup->pending = 0;
791 	if (get_reverse(store, sizeof(store), hostname,
792 			lookup->ip6_int, 1) == ISC_R_SUCCESS) {
793 		strlcpy(lookup->textname, store, sizeof(lookup->textname));
794 		lookup->rdtype = dns_rdatatype_ptr;
795 		lookup->rdtypeset = 1;
796 		default_lookups = 0;
797 	} else {
798 		strlcpy(lookup->textname, hostname, sizeof(lookup->textname));
799 		usesearch = 1;
800 	}
801 	lookup->new_search = 1;
802 	ISC_LIST_APPEND(lookup_list, lookup, link);
803 }
804 
805 int
806 host_main(int argc, char **argv) {
807 	isc_result_t result;
808 
809 	tries = 2;
810 
811 	ISC_LIST_INIT(lookup_list);
812 	ISC_LIST_INIT(server_list);
813 	ISC_LIST_INIT(root_hints_server_list);
814 	ISC_LIST_INIT(search_list);
815 
816 	fatalexit = 1;
817 
818 	/* setup dighost callbacks */
819 	dighost_printmessage = printmessage;
820 	dighost_received = received;
821 	dighost_trying = trying;
822 	dighost_shutdown = host_shutdown;
823 
824 	debug("main()");
825 	progname = argv[0];
826 	pre_parse_args(argc, argv);
827 	result = isc_app_start();
828 	check_result(result, "isc_app_start");
829 
830 	if (pledge("stdio rpath inet dns", NULL) == -1) {
831 		perror("pledge");
832 		exit(1);
833 	}
834 
835 	setup_libs();
836 
837 	if (pledge("stdio inet dns", NULL) == -1) {
838 		perror("pledge");
839 		exit(1);
840 	}
841 
842 	parse_args(argc, argv);
843 	setup_system(ipv4only, ipv6only);
844 	result = isc_app_onrun(global_task, onrun_callback, NULL);
845 	check_result(result, "isc_app_onrun");
846 	isc_app_run();
847 	cancel_all();
848 	destroy_libs();
849 	return ((seen_error == 0) ? 0 : 1);
850 }
851