host.c 22.5 KB
Newer Older
1
/*
2
 * Copyright (C) 2000-2007, 2009-2016  Internet Systems Consortium, Inc. ("ISC")
3
 *
4 5 6
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 8
 */

9
/*! \file */
10

11
#include <config.h>
12
#include <stdlib.h>
13
#include <limits.h>
14

15 16 17 18 19 20 21 22 23 24 25
#ifdef HAVE_LOCALE_H
#include <locale.h>
#endif

#ifdef WITH_IDN
#include <idn/result.h>
#include <idn/log.h>
#include <idn/resconf.h>
#include <idn/api.h>
#endif

Michael Sawyer's avatar
Michael Sawyer committed
26 27
#include <isc/app.h>
#include <isc/commandline.h>
28
#include <isc/netaddr.h>
Andreas Gustafsson's avatar
Andreas Gustafsson committed
29
#include <isc/print.h>
Michael Sawyer's avatar
Michael Sawyer committed
30 31
#include <isc/string.h>
#include <isc/util.h>
32
#include <isc/task.h>
33
#include <isc/stdlib.h>
34

35 36
#include <dns/byaddr.h>
#include <dns/fixedname.h>
37 38 39
#include <dns/message.h>
#include <dns/name.h>
#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 46
#include <dig/dig.h>

Brian Wellington's avatar
Brian Wellington committed
47
static isc_boolean_t short_form = ISC_TRUE, listed_server = ISC_FALSE;
48
static isc_boolean_t default_lookups = ISC_TRUE;
49
static int seen_error = -1;
50 51
static isc_boolean_t list_addresses = ISC_TRUE;
static dns_rdatatype_t list_type = dns_rdatatype_a;
Mark Andrews's avatar
Mark Andrews committed
52
static isc_boolean_t printed_server = ISC_FALSE;
53
static isc_boolean_t ipv4only = ISC_FALSE, ipv6only = ISC_FALSE;
54

David Lawrence's avatar
David Lawrence committed
55
static const char *opcodetext[] = {
56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
	"QUERY",
	"IQUERY",
	"STATUS",
	"RESERVED3",
	"NOTIFY",
	"UPDATE",
	"RESERVED6",
	"RESERVED7",
	"RESERVED8",
	"RESERVED9",
	"RESERVED10",
	"RESERVED11",
	"RESERVED12",
	"RESERVED13",
	"RESERVED14",
	"RESERVED15"
};

David Lawrence's avatar
David Lawrence committed
74
static const char *rcodetext[] = {
75 76 77 78
	"NOERROR",
	"FORMERR",
	"SERVFAIL",
	"NXDOMAIN",
79
	"NOTIMP",
80 81 82 83 84 85 86 87 88 89 90 91 92 93
	"REFUSED",
	"YXDOMAIN",
	"YXRRSET",
	"NXRRSET",
	"NOTAUTH",
	"NOTZONE",
	"RESERVED11",
	"RESERVED12",
	"RESERVED13",
	"RESERVED14",
	"RESERVED15",
	"BADVERS"
};

94 95 96 97
struct rtype {
	unsigned int type;
	const char *text;
};
98

99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
struct rtype rtypes[] = {
	{ 1, 	"has address" },
	{ 2, 	"name server" },
	{ 5, 	"is an alias for" },
	{ 11,	"has well known services" },
	{ 12,	"domain name pointer" },
	{ 13,	"host information" },
	{ 15,	"mail is handled by" },
	{ 16,	"descriptive text" },
	{ 19,	"x25 address" },
	{ 20,	"ISDN address" },
	{ 24,	"has signature" },
	{ 25,	"has key" },
	{ 28,	"has IPv6 address" },
	{ 29,	"location" },
	{ 0, NULL }
};
116

117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
static char *
rcode_totext(dns_rcode_t rcode)
{
	static char buf[sizeof("?65535")];
	union {
		const char *consttext;
		char *deconsttext;
	} totext;

	if (rcode >= (sizeof(rcodetext)/sizeof(rcodetext[0]))) {
		snprintf(buf, sizeof(buf), "?%u", rcode);
		totext.deconsttext = buf;
	} else
		totext.consttext = rcodetext[rcode];
	return totext.deconsttext;
}

Francis Dupont's avatar
Francis Dupont committed
134 135 136
ISC_PLATFORM_NORETURN_PRE static void
show_usage(void) ISC_PLATFORM_NORETURN_POST;

137
static void
138
show_usage(void) {
139
	fputs(
140
"Usage: host [-aCdilrTvVw] [-c class] [-N ndots] [-t type] [-W time]\n"
141
"            [-R number] [-m flag] hostname [server]\n"
142
"       -a is equivalent to -v -t ANY\n"
143
"       -c specifies query class for non-IN data\n"
Brian Wellington's avatar
Brian Wellington committed
144
"       -C compares SOA records on authoritative nameservers\n"
145
"       -d is equivalent to -v\n"
146
"       -i IP6.INT reverse lookups\n"
147 148
"       -l lists all hosts in a domain, using AXFR\n"
"       -m set memory debugging flag (trace|record|usage)\n"
149 150 151
"       -N changes the number of dots allowed before root lookup is done\n"
"       -r disables recursive processing\n"
"       -R specifies number of retries for UDP packets\n"
152
"       -s a SERVFAIL response should stop query\n"
153 154 155
"       -t specifies the query type\n"
"       -T enables TCP/IP mode\n"
"       -v enables verbose output\n"
156
"       -V print version number and exit\n"
157
"       -w specifies to wait forever for a reply\n"
158 159
"       -W specifies how long to wait for a reply\n"
"       -4 use IPv4 query transport only\n"
160
"       -6 use IPv6 query transport only\n", stderr);
161
	exit(1);
162
}
163

164 165 166 167 168
void
dighost_shutdown(void) {
	isc_app_shutdown();
}

169
void
Andreas Gustafsson's avatar
style  
Andreas Gustafsson committed
170
received(int bytes, isc_sockaddr_t *from, dig_query_t *query) {
171 172 173
	isc_time_t now;
	int diff;

Michael Sawyer's avatar
Michael Sawyer committed
174
	if (!short_form) {
175 176
		char fromtext[ISC_SOCKADDR_FORMATSIZE];
		isc_sockaddr_format(from, fromtext, sizeof(fromtext));
177
		TIME_NOW(&now);
Danny Mayer's avatar
Danny Mayer committed
178
		diff = (int) isc_time_microdiff(&now, &query->time_sent);
179 180
		printf("Received %u bytes from %s in %d ms\n",
		       bytes, fromtext, diff/1000);
181
	}
182 183 184
}

void
Brian Wellington's avatar
Brian Wellington committed
185
trying(char *frm, dig_lookup_t *lookup) {
186
	UNUSED(lookup);
187 188

	if (!short_form)
Brian Wellington's avatar
Brian Wellington committed
189
		printf("Trying \"%s\"\n", frm);
190 191
}

192
static void
David Lawrence's avatar
David Lawrence committed
193
say_message(dns_name_t *name, const char *msg, dns_rdata_t *rdata,
194 195
	    dig_query_t *query)
{
Brian Wellington's avatar
Brian Wellington committed
196 197 198
	isc_buffer_t *b = NULL;
	char namestr[DNS_NAME_FORMATSIZE];
	isc_region_t r;
199
	isc_result_t result;
200
	unsigned int bufsize = BUFSIZ;
201

Brian Wellington's avatar
Brian Wellington committed
202
	dns_name_format(name, namestr, sizeof(namestr));
203 204 205
 retry:
	result = isc_buffer_allocate(mctx, &b, bufsize);
	check_result(result, "isc_buffer_allocate");
Brian Wellington's avatar
Brian Wellington committed
206
	result = dns_rdata_totext(rdata, NULL, b);
207 208 209 210 211
	if (result == ISC_R_NOSPACE) {
		isc_buffer_free(&b);
		bufsize *= 2;
		goto retry;
	}
212
	check_result(result, "dns_rdata_totext");
Brian Wellington's avatar
Brian Wellington committed
213
	isc_buffer_usedregion(b, &r);
Ben Cottrell's avatar
Ben Cottrell committed
214
	if (query->lookup->identify_previous_line) {
215
		printf("Nameserver %s:\n\t",
Ben Cottrell's avatar
Ben Cottrell committed
216 217
			query->servname);
	}
Brian Wellington's avatar
Brian Wellington committed
218 219
	printf("%s %s %.*s", namestr,
	       msg, (int)r.length, (char *)r.base);
220
	if (query->lookup->identify) {
221
		printf(" on server %s", query->servname);
222
	}
223
	printf("\n");
224 225
	isc_buffer_free(&b);
}
Mark Andrews's avatar
Mark Andrews committed
226
#ifdef DIG_SIGCHASE
227 228 229 230 231 232 233 234 235 236 237
/* Just for compatibility : not use in host program */
isc_result_t
printrdataset(dns_name_t *owner_name, dns_rdataset_t *rdataset,
	      isc_buffer_t *target)
{
  UNUSED(owner_name);
  UNUSED(rdataset);
  UNUSED(target);
  return(ISC_FALSE);
}
#endif
238
static isc_result_t
David Lawrence's avatar
David Lawrence committed
239 240 241
printsection(dns_message_t *msg, dns_section_t sectionid,
	     const char *section_name, isc_boolean_t headers,
	     dig_query_t *query)
242 243 244
{
	dns_name_t *name, *print_name;
	dns_rdataset_t *rdataset;
245
	dns_rdata_t rdata = DNS_RDATA_INIT;
246 247 248 249
	isc_buffer_t target;
	isc_result_t result, loopresult;
	isc_region_t r;
	dns_name_t empty_name;
250
	char tbuf[4096];
251 252
	isc_boolean_t first;
	isc_boolean_t no_rdata;
253

254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273
	if (sectionid == DNS_SECTION_QUESTION)
		no_rdata = ISC_TRUE;
	else
		no_rdata = ISC_FALSE;

	if (headers)
		printf(";; %s SECTION:\n", section_name);

	dns_name_init(&empty_name, NULL);

	result = dns_message_firstname(msg, sectionid);
	if (result == ISC_R_NOMORE)
		return (ISC_R_SUCCESS);
	else if (result != ISC_R_SUCCESS)
		return (result);

	for (;;) {
		name = NULL;
		dns_message_currentname(msg, sectionid, &name);

274
		isc_buffer_init(&target, tbuf, sizeof(tbuf));
275 276 277 278 279 280
		first = ISC_TRUE;
		print_name = name;

		for (rdataset = ISC_LIST_HEAD(name->list);
		     rdataset != NULL;
		     rdataset = ISC_LIST_NEXT(rdataset, link)) {
281 282 283
			if (query->lookup->rdtype == dns_rdatatype_axfr &&
			    !((!list_addresses &&
			       (list_type == dns_rdatatype_any ||
Automatic Updater's avatar
Automatic Updater committed
284
				rdataset->type == list_type)) ||
285 286
			      (list_addresses &&
			       (rdataset->type == dns_rdatatype_a ||
Automatic Updater's avatar
Automatic Updater committed
287
				rdataset->type == dns_rdatatype_aaaa ||
288 289 290
				rdataset->type == dns_rdatatype_ns ||
				rdataset->type == dns_rdatatype_ptr))))
				continue;
291 292 293 294 295 296 297 298 299 300 301 302 303
			if (!short_form) {
				result = dns_rdataset_totext(rdataset,
							     print_name,
							     ISC_FALSE,
							     no_rdata,
							     &target);
				if (result != ISC_R_SUCCESS)
					return (result);
#ifdef USEINITALWS
				if (first) {
					print_name = &empty_name;
					first = ISC_FALSE;
				}
304 305
#else
				UNUSED(first); /* Shut up compiler. */
306
#endif
307
			} else {
308 309
				loopresult = dns_rdataset_first(rdataset);
				while (loopresult == ISC_R_SUCCESS) {
310 311 312 313 314
					struct rtype *t;
					const char *rtt;
					char typebuf[DNS_RDATATYPE_FORMATSIZE];
					char typebuf2[DNS_RDATATYPE_FORMATSIZE
						     + 20];
315
					dns_rdataset_current(rdataset, &rdata);
316 317 318 319 320 321 322 323 324 325 326 327 328 329 330

					for (t = rtypes; t->text != NULL; t++) {
						if (t->type == rdata.type) {
							rtt = t->text;
							goto found;
						}
					}

					dns_rdatatype_format(rdata.type,
							     typebuf,
							     sizeof(typebuf));
					snprintf(typebuf2, sizeof(typebuf2),
						 "has %s record", typebuf);
					rtt = typebuf2;
				found:
Michael Sawyer's avatar
Michael Sawyer committed
331
					say_message(print_name, rtt,
332
						    &rdata, query);
333
					dns_rdata_reset(&rdata);
David Lawrence's avatar
David Lawrence committed
334 335
					loopresult =
						dns_rdataset_next(rdataset);
336 337 338 339
				}
			}
		}
		if (!short_form) {
340
			isc_buffer_usedregion(&target, &r);
341 342 343 344 345 346
			if (no_rdata)
				printf(";%.*s", (int)r.length,
				       (char *)r.base);
			else
				printf("%.*s", (int)r.length, (char *)r.base);
		}
347

348 349 350 351 352 353
		result = dns_message_nextname(msg, sectionid);
		if (result == ISC_R_NOMORE)
			break;
		else if (result != ISC_R_SUCCESS)
			return (result);
	}
354

355 356 357 358 359
	return (ISC_R_SUCCESS);
}

static isc_result_t
printrdata(dns_message_t *msg, dns_rdataset_t *rdataset, dns_name_t *owner,
David Lawrence's avatar
David Lawrence committed
360
	   const char *set_name, isc_boolean_t headers)
361 362 363 364
{
	isc_buffer_t target;
	isc_result_t result;
	isc_region_t r;
365
	char tbuf[4096];
366 367

	UNUSED(msg);
368
	if (headers)
369 370
		printf(";; %s SECTION:\n", set_name);

371
	isc_buffer_init(&target, tbuf, sizeof(tbuf));
372 373 374 375 376

	result = dns_rdataset_totext(rdataset, owner, ISC_FALSE, ISC_FALSE,
				     &target);
	if (result != ISC_R_SUCCESS)
		return (result);
377
	isc_buffer_usedregion(&target, &r);
378 379 380 381 382
	printf("%.*s", (int)r.length, (char *)r.base);

	return (ISC_R_SUCCESS);
}

383 384 385 386 387 388 389 390
static void
chase_cnamechain(dns_message_t *msg, dns_name_t *qname) {
	isc_result_t result;
	dns_rdataset_t *rdataset;
	dns_rdata_cname_t cname;
	dns_rdata_t rdata = DNS_RDATA_INIT;
	unsigned int i = msg->counts[DNS_SECTION_ANSWER];

Automatic Updater's avatar
Automatic Updater committed
391
	while (i-- > 0) {
392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408
		rdataset = NULL;
		result = dns_message_findname(msg, DNS_SECTION_ANSWER, qname,
					      dns_rdatatype_cname, 0, NULL,
					      &rdataset);
		if (result != ISC_R_SUCCESS)
			return;
		result = dns_rdataset_first(rdataset);
		check_result(result, "dns_rdataset_first");
		dns_rdata_reset(&rdata);
		dns_rdataset_current(rdataset, &rdata);
		result = dns_rdata_tostruct(&rdata, &cname, NULL);
		check_result(result, "dns_rdata_tostruct");
		dns_name_copy(&cname.cname, qname, NULL);
		dns_rdata_freestruct(&cname);
	}
}

409
isc_result_t
410
printmessage(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers) {
411
	isc_boolean_t did_flag = ISC_FALSE;
412
	dns_rdataset_t *opt, *tsig = NULL;
413
	dns_name_t *tsigname;
414
	isc_result_t result = ISC_R_SUCCESS;
415
	int force_error;
416

417
	UNUSED(headers);
418

419 420 421 422 423 424
	/*
	 * We get called multiple times.
	 * Preserve any existing error status.
	 */
	force_error = (seen_error == 1) ? 1 : 0;
	seen_error = 1;
425
	if (listed_server && !printed_server) {
Brian Wellington's avatar
Brian Wellington committed
426 427
		char sockstr[ISC_SOCKADDR_FORMATSIZE];

Michael Sawyer's avatar
Michael Sawyer committed
428
		printf("Using domain server:\n");
429
		printf("Name: %s\n", query->userarg);
Brian Wellington's avatar
Brian Wellington committed
430 431 432
		isc_sockaddr_format(&query->sockaddr, sockstr,
				    sizeof(sockstr));
		printf("Address: %s\n", sockstr);
Michael Sawyer's avatar
Michael Sawyer committed
433
		printf("Aliases: \n\n");
434
		printed_server = ISC_TRUE;
Michael Sawyer's avatar
Michael Sawyer committed
435 436
	}

437
	if (msg->rcode != 0) {
Brian Wellington's avatar
Brian Wellington committed
438 439
		char namestr[DNS_NAME_FORMATSIZE];
		dns_name_format(query->lookup->name, namestr, sizeof(namestr));
440 441 442 443 444 445 446 447 448 449 450 451

		if (query->lookup->identify_previous_line)
			printf("Nameserver %s:\n\t%s not found: %d(%s)\n",
			       query->servname,
			       (msg->rcode != dns_rcode_nxdomain) ? namestr :
			       query->lookup->textname, msg->rcode,
			       rcode_totext(msg->rcode));
		else
			printf("Host %s not found: %d(%s)\n",
			       (msg->rcode != dns_rcode_nxdomain) ? namestr :
			       query->lookup->textname, msg->rcode,
			       rcode_totext(msg->rcode));
452 453
		return (ISC_R_SUCCESS);
	}
454 455 456 457

	if (default_lookups && query->lookup->rdtype == dns_rdatatype_a) {
		char namestr[DNS_NAME_FORMATSIZE];
		dig_lookup_t *lookup;
458 459
		dns_fixedname_t fixed;
		dns_name_t *name;
460 461

		/* Add AAAA and MX lookups. */
462 463 464 465 466
		dns_fixedname_init(&fixed);
		name = dns_fixedname_name(&fixed);
		dns_name_copy(query->lookup->name, name, NULL);
		chase_cnamechain(msg, name);
		dns_name_format(name, namestr, sizeof(namestr));
467 468 469 470 471 472
		lookup = clone_lookup(query->lookup, ISC_FALSE);
		if (lookup != NULL) {
			strncpy(lookup->textname, namestr,
				sizeof(lookup->textname));
			lookup->textname[sizeof(lookup->textname)-1] = 0;
			lookup->rdtype = dns_rdatatype_aaaa;
Automatic Updater's avatar
Automatic Updater committed
473
			lookup->rdtypeset = ISC_TRUE;
474 475 476 477 478 479 480 481 482 483
			lookup->origin = NULL;
			lookup->retries = tries;
			ISC_LIST_APPEND(lookup_list, lookup, link);
		}
		lookup = clone_lookup(query->lookup, ISC_FALSE);
		if (lookup != NULL) {
			strncpy(lookup->textname, namestr,
				sizeof(lookup->textname));
			lookup->textname[sizeof(lookup->textname)-1] = 0;
			lookup->rdtype = dns_rdatatype_mx;
Automatic Updater's avatar
Automatic Updater committed
484
			lookup->rdtypeset = ISC_TRUE;
485 486 487 488 489 490
			lookup->origin = NULL;
			lookup->retries = tries;
			ISC_LIST_APPEND(lookup_list, lookup, link);
		}
	}

491 492
	if (!short_form) {
		printf(";; ->>HEADER<<- opcode: %s, status: %s, id: %u\n",
493
		       opcodetext[msg->opcode], rcode_totext(msg->rcode),
494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522
		       msg->id);
		printf(";; flags: ");
		if ((msg->flags & DNS_MESSAGEFLAG_QR) != 0) {
			printf("qr");
			did_flag = ISC_TRUE;
		}
		if ((msg->flags & DNS_MESSAGEFLAG_AA) != 0) {
			printf("%saa", did_flag ? " " : "");
			did_flag = ISC_TRUE;
		}
		if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0) {
			printf("%stc", did_flag ? " " : "");
			did_flag = ISC_TRUE;
		}
		if ((msg->flags & DNS_MESSAGEFLAG_RD) != 0) {
			printf("%srd", did_flag ? " " : "");
			did_flag = ISC_TRUE;
		}
		if ((msg->flags & DNS_MESSAGEFLAG_RA) != 0) {
			printf("%sra", did_flag ? " " : "");
			did_flag = ISC_TRUE;
		}
		if ((msg->flags & DNS_MESSAGEFLAG_AD) != 0) {
			printf("%sad", did_flag ? " " : "");
			did_flag = ISC_TRUE;
		}
		if ((msg->flags & DNS_MESSAGEFLAG_CD) != 0) {
			printf("%scd", did_flag ? " " : "");
			did_flag = ISC_TRUE;
523
			POST(did_flag);
524
		}
525 526
		printf("; QUERY: %u, ANSWER: %u, "
		       "AUTHORITY: %u, ADDITIONAL: %u\n",
527 528 529 530 531 532 533 534 535 536 537 538 539 540 541
		       msg->counts[DNS_SECTION_QUESTION],
		       msg->counts[DNS_SECTION_ANSWER],
		       msg->counts[DNS_SECTION_AUTHORITY],
		       msg->counts[DNS_SECTION_ADDITIONAL]);
		opt = dns_message_getopt(msg);
		if (opt != NULL)
			printf(";; EDNS: version: %u, udp=%u\n",
			       (unsigned int)((opt->ttl & 0x00ff0000) >> 16),
			       (unsigned int)opt->rdclass);
		tsigname = NULL;
		tsig = dns_message_gettsig(msg, &tsigname);
		if (tsig != NULL)
			printf(";; PSEUDOSECTIONS: TSIG\n");
	}
	if (! ISC_LIST_EMPTY(msg->sections[DNS_SECTION_QUESTION]) &&
542
	    !short_form) {
543 544
		printf("\n");
		result = printsection(msg, DNS_SECTION_QUESTION, "QUESTION",
545
				      ISC_TRUE, query);
546 547 548 549 550 551 552
		if (result != ISC_R_SUCCESS)
			return (result);
	}
	if (! ISC_LIST_EMPTY(msg->sections[DNS_SECTION_ANSWER])) {
		if (!short_form)
			printf("\n");
		result = printsection(msg, DNS_SECTION_ANSWER, "ANSWER",
553
				      ISC_TF(!short_form), query);
554 555 556
		if (result != ISC_R_SUCCESS)
			return (result);
	}
Michael Sawyer's avatar
Michael Sawyer committed
557

558
	if (! ISC_LIST_EMPTY(msg->sections[DNS_SECTION_AUTHORITY]) &&
559
	    !short_form) {
560 561
		printf("\n");
		result = printsection(msg, DNS_SECTION_AUTHORITY, "AUTHORITY",
562
				      ISC_TRUE, query);
563 564 565 566
		if (result != ISC_R_SUCCESS)
			return (result);
	}
	if (! ISC_LIST_EMPTY(msg->sections[DNS_SECTION_ADDITIONAL]) &&
567
	    !short_form) {
568 569
		printf("\n");
		result = printsection(msg, DNS_SECTION_ADDITIONAL,
570
				      "ADDITIONAL", ISC_TRUE, query);
571 572 573 574 575 576 577 578 579 580 581 582 583
		if (result != ISC_R_SUCCESS)
			return (result);
	}
	if ((tsig != NULL) && !short_form) {
		printf("\n");
		result = printrdata(msg, tsig, tsigname,
				    "PSEUDOSECTION TSIG", ISC_TRUE);
		if (result != ISC_R_SUCCESS)
			return (result);
	}
	if (!short_form)
		printf("\n");

584 585
	if (short_form && !default_lookups &&
	    ISC_LIST_EMPTY(msg->sections[DNS_SECTION_ANSWER])) {
586 587 588 589 590 591 592
		char namestr[DNS_NAME_FORMATSIZE];
		char typestr[DNS_RDATATYPE_FORMATSIZE];
		dns_name_format(query->lookup->name, namestr, sizeof(namestr));
		dns_rdatatype_format(query->lookup->rdtype, typestr,
				     sizeof(typestr));
		printf("%s has no %s record\n", namestr, typestr);
	}
593
	seen_error = force_error;
594 595 596
	return (result);
}

597 598 599 600 601 602 603
static const char * optstring = "46ac:dilnm:rst:vVwCDN:R:TW:";

/*% version */
static void
version(void) {
	fputs("host " VERSION "\n", stderr);
}
604 605 606 607 608 609 610 611

static void
pre_parse_args(int argc, char **argv) {
	int c;

	while ((c = isc_commandline_parse(argc, argv, optstring)) != -1) {
		switch (c) {
		case 'm':
612
			memdebugging = ISC_TRUE;
613 614
			if (strcasecmp("trace", isc_commandline_argument) == 0)
				isc_mem_debugging |= ISC_MEM_DEBUGTRACE;
Francis Dupont's avatar
Francis Dupont committed
615 616
			else if (strcasecmp("record",
					    isc_commandline_argument) == 0)
617 618 619 620 621 622
				isc_mem_debugging |= ISC_MEM_DEBUGRECORD;
			else if (strcasecmp("usage",
					    isc_commandline_argument) == 0)
				isc_mem_debugging |= ISC_MEM_DEBUGUSAGE;
			break;

623 624 625 626 627 628 629 630 631 632
		case '4':
			if (ipv6only)
				fatal("only one of -4 and -6 allowed");
			ipv4only = ISC_TRUE;
			break;
		case '6':
			if (ipv4only)
				fatal("only one of -4 and -6 allowed");
			ipv6only = ISC_TRUE;
			break;
633 634 635 636 637 638 639
		case 'a': break;
		case 'c': break;
		case 'd': break;
		case 'i': break;
		case 'l': break;
		case 'n': break;
		case 'r': break;
640
		case 's': break;
641 642
		case 't': break;
		case 'v': break;
Tinderbox User's avatar
Tinderbox User committed
643
		case 'V':
644 645 646
			  version();
			  exit(0);
			  break;
647 648
		case 'w': break;
		case 'C': break;
649
		case 'D':
650 651
			if (debugging)
				debugtiming = ISC_TRUE;
652 653
			debugging = ISC_TRUE;
			break;
654 655 656 657 658 659 660 661 662
		case 'N': break;
		case 'R': break;
		case 'T': break;
		case 'W': break;
		default:
			show_usage();
		}
	}
	isc_commandline_reset = ISC_TRUE;
663
	isc_commandline_index = 1;
664 665
}

666
static void
667 668 669
parse_args(isc_boolean_t is_batchfile, int argc, char **argv) {
	char hostname[MXNAME];
	dig_lookup_t *lookup;
670
	int c;
671
	char store[MXNAME];
672
	isc_textregion_t tr;
673
	isc_result_t result = ISC_R_SUCCESS;
674 675
	dns_rdatatype_t rdtype;
	dns_rdataclass_t rdclass;
676
	isc_uint32_t serial = 0;
677

678 679
	UNUSED(is_batchfile);

680 681
	lookup = make_empty_lookup();

682 683
	lookup->servfail_stops = ISC_FALSE;
	lookup->comments = ISC_FALSE;
684
	short_form = !verbose;
685

686
	while ((c = isc_commandline_parse(argc, argv, optstring)) != -1) {
687 688
		switch (c) {
		case 'l':
689 690
			lookup->tcp_mode = ISC_TRUE;
			lookup->rdtype = dns_rdatatype_axfr;
Michael Sawyer's avatar
Michael Sawyer committed
691
			lookup->rdtypeset = ISC_TRUE;
692
			fatalexit = 3;
693 694 695 696 697 698
			break;
		case 'v':
		case 'd':
			short_form = ISC_FALSE;
			break;
		case 'r':
699
			lookup->recurse = ISC_FALSE;
700 701
			break;
		case 't':
702 703 704 705 706 707 708 709 710 711 712
			if (strncasecmp(isc_commandline_argument,
					"ixfr=", 5) == 0) {
				rdtype = dns_rdatatype_ixfr;
				/* XXXMPA add error checking */
				serial = strtoul(isc_commandline_argument + 5,
						 NULL, 10);
				result = ISC_R_SUCCESS;
			} else {
				tr.base = isc_commandline_argument;
				tr.length = strlen(isc_commandline_argument);
				result = dns_rdatatype_fromtext(&rdtype,
713
						   (isc_textregion_t *)&tr);
714
			}
715

716 717 718 719
			if (result != ISC_R_SUCCESS) {
				fatalexit = 2;
				fatal("invalid type: %s\n",
				      isc_commandline_argument);
Michael Sawyer's avatar
Michael Sawyer committed
720
			}
721 722 723 724
			if (!lookup->rdtypeset ||
			    lookup->rdtype != dns_rdatatype_axfr)
				lookup->rdtype = rdtype;
			lookup->rdtypeset = ISC_TRUE;
725 726 727
#ifdef WITH_IDN
			idnoptions = 0;
#endif
728 729 730 731 732
			if (rdtype == dns_rdatatype_axfr) {
				/* -l -t any -v */
				list_type = dns_rdatatype_any;
				short_form = ISC_FALSE;
				lookup->tcp_mode = ISC_TRUE;
733 734
			} else if (rdtype == dns_rdatatype_ixfr) {
				lookup->ixfr_serial = serial;
735
				lookup->tcp_mode = ISC_TRUE;
736
				list_type = rdtype;
737 738 739 740 741 742 743
#ifdef WITH_IDN
			} else if (rdtype == dns_rdatatype_a ||
				   rdtype == dns_rdatatype_aaaa ||
				   rdtype == dns_rdatatype_mx) {
				idnoptions = IDN_ASCCHECK;
				list_type = rdtype;
#endif
744
			} else
745 746
				list_type = rdtype;
			list_addresses = ISC_FALSE;
747
			default_lookups = ISC_FALSE;
748
			break;
749
		case 'c':
750 751 752 753
			tr.base = isc_commandline_argument;
			tr.length = strlen(isc_commandline_argument);
			result = dns_rdataclass_fromtext(&rdclass,
						   (isc_textregion_t *)&tr);
754

755 756 757 758 759
			if (result != ISC_R_SUCCESS) {
				fatalexit = 2;
				fatal("invalid class: %s\n",
				      isc_commandline_argument);
			} else {
760
				lookup->rdclass = rdclass;
Michael Sawyer's avatar
Michael Sawyer committed
761 762
				lookup->rdclassset = ISC_TRUE;
			}
763
			default_lookups = ISC_FALSE;
764
			break;
765
		case 'a':
766 767 768
			if (!lookup->rdtypeset ||
			    lookup->rdtype != dns_rdatatype_axfr)
				lookup->rdtype = dns_rdatatype_any;
769 770 771
#ifdef WITH_IDN
			idnoptions = 0;
#endif
772 773
			list_type = dns_rdatatype_any;
			list_addresses = ISC_FALSE;
Michael Sawyer's avatar
Michael Sawyer committed
774
			lookup->rdtypeset = ISC_TRUE;
775
			short_form = ISC_FALSE;
776
			default_lookups = ISC_FALSE;
777
			break;
778 779 780
		case 'i':
			lookup->ip6_int = ISC_TRUE;
			break;
781
		case 'n':
782
			/* deprecated */
783
			break;
784 785 786
		case 'm':
			/* Handled by pre_parse_args(). */
			break;
787
		case 'w':
Michael Sawyer's avatar
Michael Sawyer committed
788 789 790 791
			/*
			 * The timer routines are coded such that
			 * timeout==MAXINT doesn't enable the timer
			 */
792
			timeout = INT_MAX;
793
			break;
794 795 796 797 798 799
		case 'W':
			timeout = atoi(isc_commandline_argument);
			if (timeout < 1)
				timeout = 1;
			break;
		case 'R':
800 801 802
			tries = atoi(isc_commandline_argument) + 1;
			if (tries < 2)
				tries = 2;
803
			break;
804
		case 'T':
805
			lookup->tcp_mode = ISC_TRUE;
806
			break;
807
		case 'C':
Michael Sawyer's avatar
Michael Sawyer committed
808
			debug("showing all SOAs");
809
			lookup->rdtype = dns_rdatatype_ns;
Michael Sawyer's avatar
Michael Sawyer committed
810
			lookup->rdtypeset = ISC_TRUE;
811
			lookup->rdclass = dns_rdataclass_in;
Michael Sawyer's avatar
Michael Sawyer committed
812
			lookup->rdclassset = ISC_TRUE;
813 814
			lookup->ns_search_only = ISC_TRUE;
			lookup->trace_root = ISC_TRUE;
Ben Cottrell's avatar
Ben Cottrell committed
815
			lookup->identify_previous_line = ISC_TRUE;
816
			default_lookups = ISC_FALSE;
817
			break;
818
		case 'N':
819
			debug("setting NDOTS to %s",
820
			      isc_commandline_argument);
821 822
			ndots = atoi(isc_commandline_argument);
			break;
Michael Sawyer's avatar
Michael Sawyer committed
823
		case 'D':
824
			/* Handled by pre_parse_args(). */
Michael Sawyer's avatar
Michael Sawyer committed
825
			break;
826
		case '4':
827
			/* Handled by pre_parse_args(). */
828 829
			break;
		case '6':
830
			/* Handled by pre_parse_args(). */
831
			break;
832 833 834
		case 's':
			lookup->servfail_stops = ISC_TRUE;
			break;
835 836
		}
	}
Andreas Gustafsson's avatar
style  
Andreas Gustafsson committed
837

838 839
	lookup->retries = tries;

Andreas Gustafsson's avatar
style  
Andreas Gustafsson committed
840
	if (isc_commandline_index >= argc)
841
		show_usage();
Andreas Gustafsson's avatar
style  
Andreas Gustafsson committed
842

843 844
	strlcpy(hostname, argv[isc_commandline_index], sizeof(hostname));

845
	if (argc > isc_commandline_index + 1) {
846 847
		set_nameserver(argv[isc_commandline_index+1]);
		debug("server is %s", argv[isc_commandline_index+1]);
Michael Sawyer's avatar
Michael Sawyer committed
848
		listed_server = ISC_TRUE;
849 850
	} else
		check_ra = ISC_TRUE;
851

852
	lookup->pending = ISC_FALSE;
853
	if (get_reverse(store, sizeof(store), hostname,
854
			lookup->ip6_int, ISC_TRUE) == ISC_R_SUCCESS) {
855 856
		strncpy(lookup->textname, store, sizeof(lookup->textname));
		lookup->textname[sizeof(lookup->textname)-1] = 0;
857
		lookup->rdtype = dns_rdatatype_ptr;
Michael Sawyer's avatar
Michael Sawyer committed
858
		lookup->rdtypeset = ISC_TRUE;
859
		default_lookups = ISC_FALSE;
860
	} else {
861 862
		strncpy(lookup->textname, hostname, sizeof(lookup->textname));
		lookup->textname[sizeof(lookup->textname)-1]=0;
863
		usesearch = ISC_TRUE;
864
	}
865
	lookup->new_search = ISC_TRUE;
866 867 868
	ISC_LIST_APPEND(lookup_list, lookup, link);
}

869 870
int
main(int argc, char **argv) {
871 872
	isc_result_t result;

873 874
	tries = 2;

875 876 877
	ISC_LIST_INIT(lookup_list);
	ISC_LIST_INIT(server_list);
	ISC_LIST_INIT(search_list);
Automatic Updater's avatar
Automatic Updater committed
878

879
	fatalexit = 1;
880 881 882
#ifdef WITH_IDN
	idnoptions = IDN_ASCCHECK;
#endif
883

Michael Sawyer's avatar
Michael Sawyer committed
884 885
	debug("main()");
	progname = argv[0];
886
	pre_parse_args(argc, argv);
887 888
	result = isc_app_start();
	check_result(result, "isc_app_start");
889
	setup_libs();
890
	setup_system(ipv4only, ipv6only);
891
	parse_args(ISC_FALSE, argc, argv);
892 893 894 895
	if (keyfile[0] != 0)
		setup_file_key();
	else if (keysecret[0] != 0)
		setup_text_key();
896 897
	result = isc_app_onrun(mctx, global_task, onrun_callback, NULL);
	check_result(result, "isc_app_onrun");
898
	isc_app_run();
899 900
	cancel_all();
	destroy_libs();
901
	isc_app_finish();
902
	return ((seen_error == 0) ? 0 : 1);
903
}