1135446Strhodes/*
2262706Serwin * Copyright (C) 2004-2007, 2009-2013  Internet Systems Consortium, Inc. ("ISC")
3135446Strhodes * Copyright (C) 2000-2003  Internet Software Consortium.
4135446Strhodes *
5174187Sdougb * Permission to use, copy, modify, and/or distribute this software for any
6135446Strhodes * purpose with or without fee is hereby granted, provided that the above
7135446Strhodes * copyright notice and this permission notice appear in all copies.
8135446Strhodes *
9135446Strhodes * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10135446Strhodes * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11135446Strhodes * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12135446Strhodes * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13135446Strhodes * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14135446Strhodes * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15135446Strhodes * PERFORMANCE OF THIS SOFTWARE.
16135446Strhodes */
17135446Strhodes
18254897Serwin/* $Id: host.c,v 1.127 2011/03/11 06:11:20 marka Exp $ */
19135446Strhodes
20170222Sdougb/*! \file */
21170222Sdougb
22135446Strhodes#include <config.h>
23174187Sdougb#include <stdlib.h>
24135446Strhodes#include <limits.h>
25135446Strhodes
26174187Sdougb#ifdef HAVE_LOCALE_H
27174187Sdougb#include <locale.h>
28174187Sdougb#endif
29174187Sdougb
30174187Sdougb#ifdef WITH_IDN
31174187Sdougb#include <idn/result.h>
32174187Sdougb#include <idn/log.h>
33174187Sdougb#include <idn/resconf.h>
34174187Sdougb#include <idn/api.h>
35174187Sdougb#endif
36174187Sdougb
37135446Strhodes#include <isc/app.h>
38135446Strhodes#include <isc/commandline.h>
39135446Strhodes#include <isc/netaddr.h>
40135446Strhodes#include <isc/print.h>
41135446Strhodes#include <isc/string.h>
42135446Strhodes#include <isc/util.h>
43135446Strhodes#include <isc/task.h>
44135446Strhodes#include <isc/stdlib.h>
45135446Strhodes
46135446Strhodes#include <dns/byaddr.h>
47135446Strhodes#include <dns/fixedname.h>
48135446Strhodes#include <dns/message.h>
49135446Strhodes#include <dns/name.h>
50135446Strhodes#include <dns/rdata.h>
51135446Strhodes#include <dns/rdataclass.h>
52135446Strhodes#include <dns/rdataset.h>
53135446Strhodes#include <dns/rdatatype.h>
54165071Sdougb#include <dns/rdatastruct.h>
55135446Strhodes
56135446Strhodes#include <dig/dig.h>
57135446Strhodes
58135446Strhodesstatic isc_boolean_t short_form = ISC_TRUE, listed_server = ISC_FALSE;
59135446Strhodesstatic isc_boolean_t default_lookups = ISC_TRUE;
60135446Strhodesstatic int seen_error = -1;
61135446Strhodesstatic isc_boolean_t list_addresses = ISC_TRUE;
62135446Strhodesstatic dns_rdatatype_t list_type = dns_rdatatype_a;
63165071Sdougbstatic isc_boolean_t printed_server = ISC_FALSE;
64135446Strhodes
65135446Strhodesstatic const char *opcodetext[] = {
66135446Strhodes	"QUERY",
67135446Strhodes	"IQUERY",
68135446Strhodes	"STATUS",
69135446Strhodes	"RESERVED3",
70135446Strhodes	"NOTIFY",
71135446Strhodes	"UPDATE",
72135446Strhodes	"RESERVED6",
73135446Strhodes	"RESERVED7",
74135446Strhodes	"RESERVED8",
75135446Strhodes	"RESERVED9",
76135446Strhodes	"RESERVED10",
77135446Strhodes	"RESERVED11",
78135446Strhodes	"RESERVED12",
79135446Strhodes	"RESERVED13",
80135446Strhodes	"RESERVED14",
81135446Strhodes	"RESERVED15"
82135446Strhodes};
83135446Strhodes
84135446Strhodesstatic const char *rcodetext[] = {
85135446Strhodes	"NOERROR",
86135446Strhodes	"FORMERR",
87135446Strhodes	"SERVFAIL",
88135446Strhodes	"NXDOMAIN",
89135446Strhodes	"NOTIMP",
90135446Strhodes	"REFUSED",
91135446Strhodes	"YXDOMAIN",
92135446Strhodes	"YXRRSET",
93135446Strhodes	"NXRRSET",
94135446Strhodes	"NOTAUTH",
95135446Strhodes	"NOTZONE",
96135446Strhodes	"RESERVED11",
97135446Strhodes	"RESERVED12",
98135446Strhodes	"RESERVED13",
99135446Strhodes	"RESERVED14",
100135446Strhodes	"RESERVED15",
101135446Strhodes	"BADVERS"
102135446Strhodes};
103135446Strhodes
104135446Strhodesstruct rtype {
105135446Strhodes	unsigned int type;
106135446Strhodes	const char *text;
107135446Strhodes};
108135446Strhodes
109135446Strhodesstruct rtype rtypes[] = {
110135446Strhodes	{ 1, 	"has address" },
111135446Strhodes	{ 2, 	"name server" },
112135446Strhodes	{ 5, 	"is an alias for" },
113135446Strhodes	{ 11,	"has well known services" },
114135446Strhodes	{ 12,	"domain name pointer" },
115135446Strhodes	{ 13,	"host information" },
116135446Strhodes	{ 15,	"mail is handled by" },
117135446Strhodes	{ 16,	"descriptive text" },
118135446Strhodes	{ 19,	"x25 address" },
119135446Strhodes	{ 20,	"ISDN address" },
120135446Strhodes	{ 24,	"has signature" },
121135446Strhodes	{ 25,	"has key" },
122135446Strhodes	{ 28,	"has IPv6 address" },
123135446Strhodes	{ 29,	"location" },
124135446Strhodes	{ 0, NULL }
125135446Strhodes};
126135446Strhodes
127193149Sdougbstatic char *
128193149Sdougbrcode_totext(dns_rcode_t rcode)
129193149Sdougb{
130193149Sdougb	static char buf[sizeof("?65535")];
131193149Sdougb	union {
132193149Sdougb		const char *consttext;
133193149Sdougb		char *deconsttext;
134193149Sdougb	} totext;
135193149Sdougb
136193149Sdougb	if (rcode >= (sizeof(rcodetext)/sizeof(rcodetext[0]))) {
137193149Sdougb		snprintf(buf, sizeof(buf), "?%u", rcode);
138193149Sdougb		totext.deconsttext = buf;
139193149Sdougb	} else
140193149Sdougb		totext.consttext = rcodetext[rcode];
141193149Sdougb	return totext.deconsttext;
142193149Sdougb}
143193149Sdougb
144224092SdougbISC_PLATFORM_NORETURN_PRE static void
145224092Sdougbshow_usage(void) ISC_PLATFORM_NORETURN_POST;
146224092Sdougb
147135446Strhodesstatic void
148135446Strhodesshow_usage(void) {
149135446Strhodes	fputs(
150135446Strhodes"Usage: host [-aCdlriTwv] [-c class] [-N ndots] [-t type] [-W time]\n"
151170222Sdougb"            [-R number] [-m flag] hostname [server]\n"
152170222Sdougb"       -a is equivalent to -v -t ANY\n"
153135446Strhodes"       -c specifies query class for non-IN data\n"
154135446Strhodes"       -C compares SOA records on authoritative nameservers\n"
155135446Strhodes"       -d is equivalent to -v\n"
156135446Strhodes"       -l lists all hosts in a domain, using AXFR\n"
157135446Strhodes"       -i IP6.INT reverse lookups\n"
158135446Strhodes"       -N changes the number of dots allowed before root lookup is done\n"
159135446Strhodes"       -r disables recursive processing\n"
160135446Strhodes"       -R specifies number of retries for UDP packets\n"
161170222Sdougb"       -s a SERVFAIL response should stop query\n"
162135446Strhodes"       -t specifies the query type\n"
163135446Strhodes"       -T enables TCP/IP mode\n"
164135446Strhodes"       -v enables verbose output\n"
165135446Strhodes"       -w specifies to wait forever for a reply\n"
166135446Strhodes"       -W specifies how long to wait for a reply\n"
167135446Strhodes"       -4 use IPv4 query transport only\n"
168170222Sdougb"       -6 use IPv6 query transport only\n"
169170222Sdougb"       -m set memory debugging flag (trace|record|usage)\n", stderr);
170135446Strhodes	exit(1);
171135446Strhodes}
172135446Strhodes
173135446Strhodesvoid
174135446Strhodesdighost_shutdown(void) {
175135446Strhodes	isc_app_shutdown();
176135446Strhodes}
177135446Strhodes
178135446Strhodesvoid
179135446Strhodesreceived(int bytes, isc_sockaddr_t *from, dig_query_t *query) {
180135446Strhodes	isc_time_t now;
181135446Strhodes	int diff;
182135446Strhodes
183135446Strhodes	if (!short_form) {
184135446Strhodes		char fromtext[ISC_SOCKADDR_FORMATSIZE];
185135446Strhodes		isc_sockaddr_format(from, fromtext, sizeof(fromtext));
186135446Strhodes		TIME_NOW(&now);
187135446Strhodes		diff = (int) isc_time_microdiff(&now, &query->time_sent);
188135446Strhodes		printf("Received %u bytes from %s in %d ms\n",
189135446Strhodes		       bytes, fromtext, diff/1000);
190135446Strhodes	}
191135446Strhodes}
192135446Strhodes
193135446Strhodesvoid
194135446Strhodestrying(char *frm, dig_lookup_t *lookup) {
195135446Strhodes	UNUSED(lookup);
196135446Strhodes
197135446Strhodes	if (!short_form)
198135446Strhodes		printf("Trying \"%s\"\n", frm);
199135446Strhodes}
200135446Strhodes
201135446Strhodesstatic void
202135446Strhodessay_message(dns_name_t *name, const char *msg, dns_rdata_t *rdata,
203135446Strhodes	    dig_query_t *query)
204135446Strhodes{
205135446Strhodes	isc_buffer_t *b = NULL;
206135446Strhodes	char namestr[DNS_NAME_FORMATSIZE];
207135446Strhodes	isc_region_t r;
208135446Strhodes	isc_result_t result;
209135446Strhodes	unsigned int bufsize = BUFSIZ;
210135446Strhodes
211135446Strhodes	dns_name_format(name, namestr, sizeof(namestr));
212135446Strhodes retry:
213135446Strhodes	result = isc_buffer_allocate(mctx, &b, bufsize);
214135446Strhodes	check_result(result, "isc_buffer_allocate");
215135446Strhodes	result = dns_rdata_totext(rdata, NULL, b);
216135446Strhodes	if (result == ISC_R_NOSPACE) {
217135446Strhodes		isc_buffer_free(&b);
218135446Strhodes		bufsize *= 2;
219135446Strhodes		goto retry;
220135446Strhodes	}
221135446Strhodes	check_result(result, "dns_rdata_totext");
222135446Strhodes	isc_buffer_usedregion(b, &r);
223135446Strhodes	if (query->lookup->identify_previous_line) {
224135446Strhodes		printf("Nameserver %s:\n\t",
225135446Strhodes			query->servname);
226135446Strhodes	}
227135446Strhodes	printf("%s %s %.*s", namestr,
228135446Strhodes	       msg, (int)r.length, (char *)r.base);
229135446Strhodes	if (query->lookup->identify) {
230135446Strhodes		printf(" on server %s", query->servname);
231135446Strhodes	}
232135446Strhodes	printf("\n");
233135446Strhodes	isc_buffer_free(&b);
234135446Strhodes}
235135446Strhodes#ifdef DIG_SIGCHASE
236135446Strhodes/* Just for compatibility : not use in host program */
237135446Strhodesisc_result_t
238135446Strhodesprintrdataset(dns_name_t *owner_name, dns_rdataset_t *rdataset,
239135446Strhodes	      isc_buffer_t *target)
240135446Strhodes{
241135446Strhodes  UNUSED(owner_name);
242135446Strhodes  UNUSED(rdataset);
243135446Strhodes  UNUSED(target);
244135446Strhodes  return(ISC_FALSE);
245135446Strhodes}
246135446Strhodes#endif
247135446Strhodesstatic isc_result_t
248135446Strhodesprintsection(dns_message_t *msg, dns_section_t sectionid,
249135446Strhodes	     const char *section_name, isc_boolean_t headers,
250135446Strhodes	     dig_query_t *query)
251135446Strhodes{
252135446Strhodes	dns_name_t *name, *print_name;
253135446Strhodes	dns_rdataset_t *rdataset;
254135446Strhodes	dns_rdata_t rdata = DNS_RDATA_INIT;
255135446Strhodes	isc_buffer_t target;
256135446Strhodes	isc_result_t result, loopresult;
257135446Strhodes	isc_region_t r;
258135446Strhodes	dns_name_t empty_name;
259135446Strhodes	char t[4096];
260135446Strhodes	isc_boolean_t first;
261135446Strhodes	isc_boolean_t no_rdata;
262135446Strhodes
263135446Strhodes	if (sectionid == DNS_SECTION_QUESTION)
264135446Strhodes		no_rdata = ISC_TRUE;
265135446Strhodes	else
266135446Strhodes		no_rdata = ISC_FALSE;
267135446Strhodes
268135446Strhodes	if (headers)
269135446Strhodes		printf(";; %s SECTION:\n", section_name);
270135446Strhodes
271135446Strhodes	dns_name_init(&empty_name, NULL);
272135446Strhodes
273135446Strhodes	result = dns_message_firstname(msg, sectionid);
274135446Strhodes	if (result == ISC_R_NOMORE)
275135446Strhodes		return (ISC_R_SUCCESS);
276135446Strhodes	else if (result != ISC_R_SUCCESS)
277135446Strhodes		return (result);
278135446Strhodes
279135446Strhodes	for (;;) {
280135446Strhodes		name = NULL;
281135446Strhodes		dns_message_currentname(msg, sectionid, &name);
282135446Strhodes
283135446Strhodes		isc_buffer_init(&target, t, sizeof(t));
284135446Strhodes		first = ISC_TRUE;
285135446Strhodes		print_name = name;
286135446Strhodes
287135446Strhodes		for (rdataset = ISC_LIST_HEAD(name->list);
288135446Strhodes		     rdataset != NULL;
289135446Strhodes		     rdataset = ISC_LIST_NEXT(rdataset, link)) {
290135446Strhodes			if (query->lookup->rdtype == dns_rdatatype_axfr &&
291135446Strhodes			    !((!list_addresses &&
292135446Strhodes			       (list_type == dns_rdatatype_any ||
293193149Sdougb				rdataset->type == list_type)) ||
294135446Strhodes			      (list_addresses &&
295135446Strhodes			       (rdataset->type == dns_rdatatype_a ||
296193149Sdougb				rdataset->type == dns_rdatatype_aaaa ||
297135446Strhodes				rdataset->type == dns_rdatatype_ns ||
298135446Strhodes				rdataset->type == dns_rdatatype_ptr))))
299135446Strhodes				continue;
300135446Strhodes			if (!short_form) {
301135446Strhodes				result = dns_rdataset_totext(rdataset,
302135446Strhodes							     print_name,
303135446Strhodes							     ISC_FALSE,
304135446Strhodes							     no_rdata,
305135446Strhodes							     &target);
306135446Strhodes				if (result != ISC_R_SUCCESS)
307135446Strhodes					return (result);
308135446Strhodes#ifdef USEINITALWS
309135446Strhodes				if (first) {
310135446Strhodes					print_name = &empty_name;
311135446Strhodes					first = ISC_FALSE;
312135446Strhodes				}
313135446Strhodes#else
314135446Strhodes				UNUSED(first); /* Shut up compiler. */
315135446Strhodes#endif
316135446Strhodes			} else {
317135446Strhodes				loopresult = dns_rdataset_first(rdataset);
318135446Strhodes				while (loopresult == ISC_R_SUCCESS) {
319135446Strhodes					struct rtype *t;
320135446Strhodes					const char *rtt;
321135446Strhodes					char typebuf[DNS_RDATATYPE_FORMATSIZE];
322135446Strhodes					char typebuf2[DNS_RDATATYPE_FORMATSIZE
323135446Strhodes						     + 20];
324135446Strhodes					dns_rdataset_current(rdataset, &rdata);
325135446Strhodes
326135446Strhodes					for (t = rtypes; t->text != NULL; t++) {
327135446Strhodes						if (t->type == rdata.type) {
328135446Strhodes							rtt = t->text;
329135446Strhodes							goto found;
330135446Strhodes						}
331135446Strhodes					}
332135446Strhodes
333135446Strhodes					dns_rdatatype_format(rdata.type,
334135446Strhodes							     typebuf,
335135446Strhodes							     sizeof(typebuf));
336135446Strhodes					snprintf(typebuf2, sizeof(typebuf2),
337135446Strhodes						 "has %s record", typebuf);
338135446Strhodes					rtt = typebuf2;
339135446Strhodes				found:
340135446Strhodes					say_message(print_name, rtt,
341135446Strhodes						    &rdata, query);
342135446Strhodes					dns_rdata_reset(&rdata);
343135446Strhodes					loopresult =
344135446Strhodes						dns_rdataset_next(rdataset);
345135446Strhodes				}
346135446Strhodes			}
347135446Strhodes		}
348135446Strhodes		if (!short_form) {
349135446Strhodes			isc_buffer_usedregion(&target, &r);
350135446Strhodes			if (no_rdata)
351135446Strhodes				printf(";%.*s", (int)r.length,
352135446Strhodes				       (char *)r.base);
353135446Strhodes			else
354135446Strhodes				printf("%.*s", (int)r.length, (char *)r.base);
355135446Strhodes		}
356135446Strhodes
357135446Strhodes		result = dns_message_nextname(msg, sectionid);
358135446Strhodes		if (result == ISC_R_NOMORE)
359135446Strhodes			break;
360135446Strhodes		else if (result != ISC_R_SUCCESS)
361135446Strhodes			return (result);
362135446Strhodes	}
363135446Strhodes
364135446Strhodes	return (ISC_R_SUCCESS);
365135446Strhodes}
366135446Strhodes
367135446Strhodesstatic isc_result_t
368135446Strhodesprintrdata(dns_message_t *msg, dns_rdataset_t *rdataset, dns_name_t *owner,
369135446Strhodes	   const char *set_name, isc_boolean_t headers)
370135446Strhodes{
371135446Strhodes	isc_buffer_t target;
372135446Strhodes	isc_result_t result;
373135446Strhodes	isc_region_t r;
374135446Strhodes	char t[4096];
375135446Strhodes
376135446Strhodes	UNUSED(msg);
377135446Strhodes	if (headers)
378135446Strhodes		printf(";; %s SECTION:\n", set_name);
379135446Strhodes
380135446Strhodes	isc_buffer_init(&target, t, sizeof(t));
381135446Strhodes
382135446Strhodes	result = dns_rdataset_totext(rdataset, owner, ISC_FALSE, ISC_FALSE,
383135446Strhodes				     &target);
384135446Strhodes	if (result != ISC_R_SUCCESS)
385135446Strhodes		return (result);
386135446Strhodes	isc_buffer_usedregion(&target, &r);
387135446Strhodes	printf("%.*s", (int)r.length, (char *)r.base);
388135446Strhodes
389135446Strhodes	return (ISC_R_SUCCESS);
390135446Strhodes}
391135446Strhodes
392165071Sdougbstatic void
393165071Sdougbchase_cnamechain(dns_message_t *msg, dns_name_t *qname) {
394165071Sdougb	isc_result_t result;
395165071Sdougb	dns_rdataset_t *rdataset;
396165071Sdougb	dns_rdata_cname_t cname;
397165071Sdougb	dns_rdata_t rdata = DNS_RDATA_INIT;
398165071Sdougb	unsigned int i = msg->counts[DNS_SECTION_ANSWER];
399165071Sdougb
400193149Sdougb	while (i-- > 0) {
401165071Sdougb		rdataset = NULL;
402165071Sdougb		result = dns_message_findname(msg, DNS_SECTION_ANSWER, qname,
403165071Sdougb					      dns_rdatatype_cname, 0, NULL,
404165071Sdougb					      &rdataset);
405165071Sdougb		if (result != ISC_R_SUCCESS)
406165071Sdougb			return;
407165071Sdougb		result = dns_rdataset_first(rdataset);
408165071Sdougb		check_result(result, "dns_rdataset_first");
409165071Sdougb		dns_rdata_reset(&rdata);
410165071Sdougb		dns_rdataset_current(rdataset, &rdata);
411165071Sdougb		result = dns_rdata_tostruct(&rdata, &cname, NULL);
412165071Sdougb		check_result(result, "dns_rdata_tostruct");
413165071Sdougb		dns_name_copy(&cname.cname, qname, NULL);
414165071Sdougb		dns_rdata_freestruct(&cname);
415165071Sdougb	}
416165071Sdougb}
417165071Sdougb
418135446Strhodesisc_result_t
419135446Strhodesprintmessage(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers) {
420135446Strhodes	isc_boolean_t did_flag = ISC_FALSE;
421135446Strhodes	dns_rdataset_t *opt, *tsig = NULL;
422135446Strhodes	dns_name_t *tsigname;
423135446Strhodes	isc_result_t result = ISC_R_SUCCESS;
424135446Strhodes	int force_error;
425135446Strhodes
426135446Strhodes	UNUSED(headers);
427135446Strhodes
428135446Strhodes	/*
429135446Strhodes	 * We get called multiple times.
430135446Strhodes	 * Preserve any existing error status.
431135446Strhodes	 */
432135446Strhodes	force_error = (seen_error == 1) ? 1 : 0;
433135446Strhodes	seen_error = 1;
434165071Sdougb	if (listed_server && !printed_server) {
435135446Strhodes		char sockstr[ISC_SOCKADDR_FORMATSIZE];
436135446Strhodes
437135446Strhodes		printf("Using domain server:\n");
438143731Sdougb		printf("Name: %s\n", query->userarg);
439135446Strhodes		isc_sockaddr_format(&query->sockaddr, sockstr,
440135446Strhodes				    sizeof(sockstr));
441135446Strhodes		printf("Address: %s\n", sockstr);
442135446Strhodes		printf("Aliases: \n\n");
443165071Sdougb		printed_server = ISC_TRUE;
444135446Strhodes	}
445135446Strhodes
446135446Strhodes	if (msg->rcode != 0) {
447135446Strhodes		char namestr[DNS_NAME_FORMATSIZE];
448135446Strhodes		dns_name_format(query->lookup->name, namestr, sizeof(namestr));
449254402Serwin
450254402Serwin		if (query->lookup->identify_previous_line)
451254402Serwin			printf("Nameserver %s:\n\t%s not found: %d(%s)\n",
452254402Serwin			       query->servname,
453254402Serwin			       (msg->rcode != dns_rcode_nxdomain) ? namestr :
454254402Serwin			       query->lookup->textname, msg->rcode,
455254402Serwin			       rcode_totext(msg->rcode));
456254402Serwin		else
457254402Serwin			printf("Host %s not found: %d(%s)\n",
458254402Serwin			       (msg->rcode != dns_rcode_nxdomain) ? namestr :
459254402Serwin			       query->lookup->textname, msg->rcode,
460254402Serwin			       rcode_totext(msg->rcode));
461135446Strhodes		return (ISC_R_SUCCESS);
462135446Strhodes	}
463135446Strhodes
464135446Strhodes	if (default_lookups && query->lookup->rdtype == dns_rdatatype_a) {
465135446Strhodes		char namestr[DNS_NAME_FORMATSIZE];
466135446Strhodes		dig_lookup_t *lookup;
467165071Sdougb		dns_fixedname_t fixed;
468165071Sdougb		dns_name_t *name;
469135446Strhodes
470135446Strhodes		/* Add AAAA and MX lookups. */
471165071Sdougb		dns_fixedname_init(&fixed);
472165071Sdougb		name = dns_fixedname_name(&fixed);
473165071Sdougb		dns_name_copy(query->lookup->name, name, NULL);
474165071Sdougb		chase_cnamechain(msg, name);
475165071Sdougb		dns_name_format(name, namestr, sizeof(namestr));
476135446Strhodes		lookup = clone_lookup(query->lookup, ISC_FALSE);
477135446Strhodes		if (lookup != NULL) {
478135446Strhodes			strncpy(lookup->textname, namestr,
479135446Strhodes				sizeof(lookup->textname));
480135446Strhodes			lookup->textname[sizeof(lookup->textname)-1] = 0;
481135446Strhodes			lookup->rdtype = dns_rdatatype_aaaa;
482193149Sdougb			lookup->rdtypeset = ISC_TRUE;
483135446Strhodes			lookup->origin = NULL;
484135446Strhodes			lookup->retries = tries;
485135446Strhodes			ISC_LIST_APPEND(lookup_list, lookup, link);
486135446Strhodes		}
487135446Strhodes		lookup = clone_lookup(query->lookup, ISC_FALSE);
488135446Strhodes		if (lookup != NULL) {
489135446Strhodes			strncpy(lookup->textname, namestr,
490135446Strhodes				sizeof(lookup->textname));
491135446Strhodes			lookup->textname[sizeof(lookup->textname)-1] = 0;
492135446Strhodes			lookup->rdtype = dns_rdatatype_mx;
493193149Sdougb			lookup->rdtypeset = ISC_TRUE;
494135446Strhodes			lookup->origin = NULL;
495135446Strhodes			lookup->retries = tries;
496135446Strhodes			ISC_LIST_APPEND(lookup_list, lookup, link);
497135446Strhodes		}
498135446Strhodes	}
499135446Strhodes
500135446Strhodes	if (!short_form) {
501135446Strhodes		printf(";; ->>HEADER<<- opcode: %s, status: %s, id: %u\n",
502193149Sdougb		       opcodetext[msg->opcode], rcode_totext(msg->rcode),
503135446Strhodes		       msg->id);
504135446Strhodes		printf(";; flags: ");
505135446Strhodes		if ((msg->flags & DNS_MESSAGEFLAG_QR) != 0) {
506135446Strhodes			printf("qr");
507135446Strhodes			did_flag = ISC_TRUE;
508135446Strhodes		}
509135446Strhodes		if ((msg->flags & DNS_MESSAGEFLAG_AA) != 0) {
510135446Strhodes			printf("%saa", did_flag ? " " : "");
511135446Strhodes			did_flag = ISC_TRUE;
512135446Strhodes		}
513135446Strhodes		if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0) {
514135446Strhodes			printf("%stc", did_flag ? " " : "");
515135446Strhodes			did_flag = ISC_TRUE;
516135446Strhodes		}
517135446Strhodes		if ((msg->flags & DNS_MESSAGEFLAG_RD) != 0) {
518135446Strhodes			printf("%srd", did_flag ? " " : "");
519135446Strhodes			did_flag = ISC_TRUE;
520135446Strhodes		}
521135446Strhodes		if ((msg->flags & DNS_MESSAGEFLAG_RA) != 0) {
522135446Strhodes			printf("%sra", did_flag ? " " : "");
523135446Strhodes			did_flag = ISC_TRUE;
524135446Strhodes		}
525135446Strhodes		if ((msg->flags & DNS_MESSAGEFLAG_AD) != 0) {
526135446Strhodes			printf("%sad", did_flag ? " " : "");
527135446Strhodes			did_flag = ISC_TRUE;
528135446Strhodes		}
529135446Strhodes		if ((msg->flags & DNS_MESSAGEFLAG_CD) != 0) {
530135446Strhodes			printf("%scd", did_flag ? " " : "");
531135446Strhodes			did_flag = ISC_TRUE;
532225361Sdougb			POST(did_flag);
533135446Strhodes		}
534135446Strhodes		printf("; QUERY: %u, ANSWER: %u, "
535135446Strhodes		       "AUTHORITY: %u, ADDITIONAL: %u\n",
536135446Strhodes		       msg->counts[DNS_SECTION_QUESTION],
537135446Strhodes		       msg->counts[DNS_SECTION_ANSWER],
538135446Strhodes		       msg->counts[DNS_SECTION_AUTHORITY],
539135446Strhodes		       msg->counts[DNS_SECTION_ADDITIONAL]);
540135446Strhodes		opt = dns_message_getopt(msg);
541135446Strhodes		if (opt != NULL)
542135446Strhodes			printf(";; EDNS: version: %u, udp=%u\n",
543135446Strhodes			       (unsigned int)((opt->ttl & 0x00ff0000) >> 16),
544135446Strhodes			       (unsigned int)opt->rdclass);
545135446Strhodes		tsigname = NULL;
546135446Strhodes		tsig = dns_message_gettsig(msg, &tsigname);
547135446Strhodes		if (tsig != NULL)
548135446Strhodes			printf(";; PSEUDOSECTIONS: TSIG\n");
549135446Strhodes	}
550135446Strhodes	if (! ISC_LIST_EMPTY(msg->sections[DNS_SECTION_QUESTION]) &&
551135446Strhodes	    !short_form) {
552135446Strhodes		printf("\n");
553135446Strhodes		result = printsection(msg, DNS_SECTION_QUESTION, "QUESTION",
554135446Strhodes				      ISC_TRUE, query);
555135446Strhodes		if (result != ISC_R_SUCCESS)
556135446Strhodes			return (result);
557135446Strhodes	}
558135446Strhodes	if (! ISC_LIST_EMPTY(msg->sections[DNS_SECTION_ANSWER])) {
559135446Strhodes		if (!short_form)
560135446Strhodes			printf("\n");
561135446Strhodes		result = printsection(msg, DNS_SECTION_ANSWER, "ANSWER",
562135446Strhodes				      ISC_TF(!short_form), query);
563135446Strhodes		if (result != ISC_R_SUCCESS)
564135446Strhodes			return (result);
565135446Strhodes	}
566135446Strhodes
567135446Strhodes	if (! ISC_LIST_EMPTY(msg->sections[DNS_SECTION_AUTHORITY]) &&
568135446Strhodes	    !short_form) {
569135446Strhodes		printf("\n");
570135446Strhodes		result = printsection(msg, DNS_SECTION_AUTHORITY, "AUTHORITY",
571135446Strhodes				      ISC_TRUE, query);
572135446Strhodes		if (result != ISC_R_SUCCESS)
573135446Strhodes			return (result);
574135446Strhodes	}
575135446Strhodes	if (! ISC_LIST_EMPTY(msg->sections[DNS_SECTION_ADDITIONAL]) &&
576135446Strhodes	    !short_form) {
577135446Strhodes		printf("\n");
578135446Strhodes		result = printsection(msg, DNS_SECTION_ADDITIONAL,
579135446Strhodes				      "ADDITIONAL", ISC_TRUE, query);
580135446Strhodes		if (result != ISC_R_SUCCESS)
581135446Strhodes			return (result);
582135446Strhodes	}
583135446Strhodes	if ((tsig != NULL) && !short_form) {
584135446Strhodes		printf("\n");
585135446Strhodes		result = printrdata(msg, tsig, tsigname,
586135446Strhodes				    "PSEUDOSECTION TSIG", ISC_TRUE);
587135446Strhodes		if (result != ISC_R_SUCCESS)
588135446Strhodes			return (result);
589135446Strhodes	}
590135446Strhodes	if (!short_form)
591135446Strhodes		printf("\n");
592135446Strhodes
593135446Strhodes	if (short_form && !default_lookups &&
594135446Strhodes	    ISC_LIST_EMPTY(msg->sections[DNS_SECTION_ANSWER])) {
595135446Strhodes		char namestr[DNS_NAME_FORMATSIZE];
596135446Strhodes		char typestr[DNS_RDATATYPE_FORMATSIZE];
597135446Strhodes		dns_name_format(query->lookup->name, namestr, sizeof(namestr));
598135446Strhodes		dns_rdatatype_format(query->lookup->rdtype, typestr,
599135446Strhodes				     sizeof(typestr));
600135446Strhodes		printf("%s has no %s record\n", namestr, typestr);
601135446Strhodes	}
602135446Strhodes	seen_error = force_error;
603135446Strhodes	return (result);
604135446Strhodes}
605135446Strhodes
606170222Sdougbstatic const char * optstring = "46ac:dilnm:rst:vwCDN:R:TW:";
607170222Sdougb
608135446Strhodesstatic void
609170222Sdougbpre_parse_args(int argc, char **argv) {
610170222Sdougb	int c;
611170222Sdougb
612170222Sdougb	while ((c = isc_commandline_parse(argc, argv, optstring)) != -1) {
613170222Sdougb		switch (c) {
614170222Sdougb		case 'm':
615174187Sdougb			memdebugging = ISC_TRUE;
616170222Sdougb			if (strcasecmp("trace", isc_commandline_argument) == 0)
617170222Sdougb				isc_mem_debugging |= ISC_MEM_DEBUGTRACE;
618170222Sdougb			else if (!strcasecmp("record",
619170222Sdougb					     isc_commandline_argument) == 0)
620170222Sdougb				isc_mem_debugging |= ISC_MEM_DEBUGRECORD;
621170222Sdougb			else if (strcasecmp("usage",
622170222Sdougb					    isc_commandline_argument) == 0)
623170222Sdougb				isc_mem_debugging |= ISC_MEM_DEBUGUSAGE;
624170222Sdougb			break;
625170222Sdougb
626170222Sdougb		case '4': break;
627170222Sdougb		case '6': break;
628170222Sdougb		case 'a': break;
629170222Sdougb		case 'c': break;
630170222Sdougb		case 'd': break;
631170222Sdougb		case 'i': break;
632170222Sdougb		case 'l': break;
633170222Sdougb		case 'n': break;
634170222Sdougb		case 'r': break;
635170222Sdougb		case 's': break;
636170222Sdougb		case 't': break;
637170222Sdougb		case 'v': break;
638170222Sdougb		case 'w': break;
639170222Sdougb		case 'C': break;
640216175Sdougb		case 'D':
641262706Serwin			if (debugging)
642262706Serwin				debugtiming = ISC_TRUE;
643216175Sdougb			debugging = ISC_TRUE;
644216175Sdougb			break;
645170222Sdougb		case 'N': break;
646170222Sdougb		case 'R': break;
647170222Sdougb		case 'T': break;
648170222Sdougb		case 'W': break;
649170222Sdougb		default:
650170222Sdougb			show_usage();
651170222Sdougb		}
652170222Sdougb	}
653170222Sdougb	isc_commandline_reset = ISC_TRUE;
654170222Sdougb	isc_commandline_index = 1;
655170222Sdougb}
656170222Sdougb
657170222Sdougbstatic void
658135446Strhodesparse_args(isc_boolean_t is_batchfile, int argc, char **argv) {
659135446Strhodes	char hostname[MXNAME];
660135446Strhodes	dig_lookup_t *lookup;
661135446Strhodes	int c;
662135446Strhodes	char store[MXNAME];
663135446Strhodes	isc_textregion_t tr;
664135446Strhodes	isc_result_t result = ISC_R_SUCCESS;
665135446Strhodes	dns_rdatatype_t rdtype;
666135446Strhodes	dns_rdataclass_t rdclass;
667135446Strhodes	isc_uint32_t serial = 0;
668135446Strhodes
669135446Strhodes	UNUSED(is_batchfile);
670135446Strhodes
671135446Strhodes	lookup = make_empty_lookup();
672135446Strhodes
673170222Sdougb	lookup->servfail_stops = ISC_FALSE;
674170222Sdougb	lookup->comments = ISC_FALSE;
675170222Sdougb
676170222Sdougb	while ((c = isc_commandline_parse(argc, argv, optstring)) != -1) {
677135446Strhodes		switch (c) {
678135446Strhodes		case 'l':
679135446Strhodes			lookup->tcp_mode = ISC_TRUE;
680135446Strhodes			lookup->rdtype = dns_rdatatype_axfr;
681135446Strhodes			lookup->rdtypeset = ISC_TRUE;
682135446Strhodes			fatalexit = 3;
683135446Strhodes			break;
684135446Strhodes		case 'v':
685135446Strhodes		case 'd':
686135446Strhodes			short_form = ISC_FALSE;
687135446Strhodes			break;
688135446Strhodes		case 'r':
689135446Strhodes			lookup->recurse = ISC_FALSE;
690135446Strhodes			break;
691135446Strhodes		case 't':
692135446Strhodes			if (strncasecmp(isc_commandline_argument,
693135446Strhodes					"ixfr=", 5) == 0) {
694135446Strhodes				rdtype = dns_rdatatype_ixfr;
695135446Strhodes				/* XXXMPA add error checking */
696135446Strhodes				serial = strtoul(isc_commandline_argument + 5,
697135446Strhodes						 NULL, 10);
698135446Strhodes				result = ISC_R_SUCCESS;
699135446Strhodes			} else {
700135446Strhodes				tr.base = isc_commandline_argument;
701135446Strhodes				tr.length = strlen(isc_commandline_argument);
702135446Strhodes				result = dns_rdatatype_fromtext(&rdtype,
703135446Strhodes						   (isc_textregion_t *)&tr);
704135446Strhodes			}
705135446Strhodes
706135446Strhodes			if (result != ISC_R_SUCCESS) {
707135446Strhodes				fatalexit = 2;
708135446Strhodes				fatal("invalid type: %s\n",
709135446Strhodes				      isc_commandline_argument);
710135446Strhodes			}
711135446Strhodes			if (!lookup->rdtypeset ||
712135446Strhodes			    lookup->rdtype != dns_rdatatype_axfr)
713135446Strhodes				lookup->rdtype = rdtype;
714135446Strhodes			lookup->rdtypeset = ISC_TRUE;
715174187Sdougb#ifdef WITH_IDN
716174187Sdougb			idnoptions = 0;
717174187Sdougb#endif
718135446Strhodes			if (rdtype == dns_rdatatype_axfr) {
719135446Strhodes				/* -l -t any -v */
720135446Strhodes				list_type = dns_rdatatype_any;
721135446Strhodes				short_form = ISC_FALSE;
722135446Strhodes				lookup->tcp_mode = ISC_TRUE;
723135446Strhodes			} else if (rdtype == dns_rdatatype_ixfr) {
724135446Strhodes				lookup->ixfr_serial = serial;
725193149Sdougb				lookup->tcp_mode = ISC_TRUE;
726135446Strhodes				list_type = rdtype;
727174187Sdougb#ifdef WITH_IDN
728174187Sdougb			} else if (rdtype == dns_rdatatype_a ||
729174187Sdougb				   rdtype == dns_rdatatype_aaaa ||
730174187Sdougb				   rdtype == dns_rdatatype_mx) {
731174187Sdougb				idnoptions = IDN_ASCCHECK;
732174187Sdougb				list_type = rdtype;
733174187Sdougb#endif
734135446Strhodes			} else
735135446Strhodes				list_type = rdtype;
736135446Strhodes			list_addresses = ISC_FALSE;
737153816Sdougb			default_lookups = ISC_FALSE;
738135446Strhodes			break;
739135446Strhodes		case 'c':
740135446Strhodes			tr.base = isc_commandline_argument;
741135446Strhodes			tr.length = strlen(isc_commandline_argument);
742135446Strhodes			result = dns_rdataclass_fromtext(&rdclass,
743135446Strhodes						   (isc_textregion_t *)&tr);
744135446Strhodes
745135446Strhodes			if (result != ISC_R_SUCCESS) {
746135446Strhodes				fatalexit = 2;
747135446Strhodes				fatal("invalid class: %s\n",
748135446Strhodes				      isc_commandline_argument);
749135446Strhodes			} else {
750135446Strhodes				lookup->rdclass = rdclass;
751135446Strhodes				lookup->rdclassset = ISC_TRUE;
752135446Strhodes			}
753135446Strhodes			default_lookups = ISC_FALSE;
754135446Strhodes			break;
755135446Strhodes		case 'a':
756135446Strhodes			if (!lookup->rdtypeset ||
757135446Strhodes			    lookup->rdtype != dns_rdatatype_axfr)
758135446Strhodes				lookup->rdtype = dns_rdatatype_any;
759135446Strhodes			list_type = dns_rdatatype_any;
760135446Strhodes			list_addresses = ISC_FALSE;
761135446Strhodes			lookup->rdtypeset = ISC_TRUE;
762135446Strhodes			short_form = ISC_FALSE;
763135446Strhodes			default_lookups = ISC_FALSE;
764135446Strhodes			break;
765135446Strhodes		case 'i':
766135446Strhodes			lookup->ip6_int = ISC_TRUE;
767135446Strhodes			break;
768135446Strhodes		case 'n':
769135446Strhodes			/* deprecated */
770135446Strhodes			break;
771170222Sdougb		case 'm':
772170222Sdougb			/* Handled by pre_parse_args(). */
773170222Sdougb			break;
774135446Strhodes		case 'w':
775135446Strhodes			/*
776135446Strhodes			 * The timer routines are coded such that
777135446Strhodes			 * timeout==MAXINT doesn't enable the timer
778135446Strhodes			 */
779135446Strhodes			timeout = INT_MAX;
780135446Strhodes			break;
781135446Strhodes		case 'W':
782135446Strhodes			timeout = atoi(isc_commandline_argument);
783135446Strhodes			if (timeout < 1)
784135446Strhodes				timeout = 1;
785135446Strhodes			break;
786135446Strhodes		case 'R':
787135446Strhodes			tries = atoi(isc_commandline_argument) + 1;
788135446Strhodes			if (tries < 2)
789135446Strhodes				tries = 2;
790135446Strhodes			break;
791135446Strhodes		case 'T':
792135446Strhodes			lookup->tcp_mode = ISC_TRUE;
793135446Strhodes			break;
794135446Strhodes		case 'C':
795135446Strhodes			debug("showing all SOAs");
796135446Strhodes			lookup->rdtype = dns_rdatatype_ns;
797135446Strhodes			lookup->rdtypeset = ISC_TRUE;
798135446Strhodes			lookup->rdclass = dns_rdataclass_in;
799135446Strhodes			lookup->rdclassset = ISC_TRUE;
800135446Strhodes			lookup->ns_search_only = ISC_TRUE;
801135446Strhodes			lookup->trace_root = ISC_TRUE;
802135446Strhodes			lookup->identify_previous_line = ISC_TRUE;
803135446Strhodes			default_lookups = ISC_FALSE;
804135446Strhodes			break;
805135446Strhodes		case 'N':
806135446Strhodes			debug("setting NDOTS to %s",
807135446Strhodes			      isc_commandline_argument);
808135446Strhodes			ndots = atoi(isc_commandline_argument);
809135446Strhodes			break;
810135446Strhodes		case 'D':
811216175Sdougb			/* Handled by pre_parse_args(). */
812135446Strhodes			break;
813135446Strhodes		case '4':
814135446Strhodes			if (have_ipv4) {
815135446Strhodes				isc_net_disableipv6();
816135446Strhodes				have_ipv6 = ISC_FALSE;
817135446Strhodes			} else
818135446Strhodes				fatal("can't find IPv4 networking");
819135446Strhodes			break;
820135446Strhodes		case '6':
821135446Strhodes			if (have_ipv6) {
822135446Strhodes				isc_net_disableipv4();
823135446Strhodes				have_ipv4 = ISC_FALSE;
824135446Strhodes			} else
825135446Strhodes				fatal("can't find IPv6 networking");
826135446Strhodes			break;
827170222Sdougb		case 's':
828170222Sdougb			lookup->servfail_stops = ISC_TRUE;
829170222Sdougb			break;
830135446Strhodes		}
831135446Strhodes	}
832135446Strhodes
833135446Strhodes	lookup->retries = tries;
834135446Strhodes
835135446Strhodes	if (isc_commandline_index >= argc)
836135446Strhodes		show_usage();
837135446Strhodes
838225361Sdougb	strlcpy(hostname, argv[isc_commandline_index], sizeof(hostname));
839225361Sdougb
840135446Strhodes	if (argc > isc_commandline_index + 1) {
841135446Strhodes		set_nameserver(argv[isc_commandline_index+1]);
842135446Strhodes		debug("server is %s", argv[isc_commandline_index+1]);
843135446Strhodes		listed_server = ISC_TRUE;
844170222Sdougb	} else
845170222Sdougb		check_ra = ISC_TRUE;
846135446Strhodes
847135446Strhodes	lookup->pending = ISC_FALSE;
848135446Strhodes	if (get_reverse(store, sizeof(store), hostname,
849135446Strhodes			lookup->ip6_int, ISC_TRUE) == ISC_R_SUCCESS) {
850135446Strhodes		strncpy(lookup->textname, store, sizeof(lookup->textname));
851135446Strhodes		lookup->textname[sizeof(lookup->textname)-1] = 0;
852135446Strhodes		lookup->rdtype = dns_rdatatype_ptr;
853135446Strhodes		lookup->rdtypeset = ISC_TRUE;
854135446Strhodes		default_lookups = ISC_FALSE;
855135446Strhodes	} else {
856135446Strhodes		strncpy(lookup->textname, hostname, sizeof(lookup->textname));
857135446Strhodes		lookup->textname[sizeof(lookup->textname)-1]=0;
858204619Sdougb		usesearch = ISC_TRUE;
859135446Strhodes	}
860135446Strhodes	lookup->new_search = ISC_TRUE;
861135446Strhodes	ISC_LIST_APPEND(lookup_list, lookup, link);
862135446Strhodes}
863135446Strhodes
864135446Strhodesint
865135446Strhodesmain(int argc, char **argv) {
866135446Strhodes	isc_result_t result;
867135446Strhodes
868135446Strhodes	tries = 2;
869135446Strhodes
870135446Strhodes	ISC_LIST_INIT(lookup_list);
871135446Strhodes	ISC_LIST_INIT(server_list);
872135446Strhodes	ISC_LIST_INIT(search_list);
873193149Sdougb
874135446Strhodes	fatalexit = 1;
875174187Sdougb#ifdef WITH_IDN
876174187Sdougb	idnoptions = IDN_ASCCHECK;
877174187Sdougb#endif
878135446Strhodes
879135446Strhodes	debug("main()");
880135446Strhodes	progname = argv[0];
881170222Sdougb	pre_parse_args(argc, argv);
882135446Strhodes	result = isc_app_start();
883135446Strhodes	check_result(result, "isc_app_start");
884135446Strhodes	setup_libs();
885135446Strhodes	parse_args(ISC_FALSE, argc, argv);
886135446Strhodes	setup_system();
887135446Strhodes	result = isc_app_onrun(mctx, global_task, onrun_callback, NULL);
888135446Strhodes	check_result(result, "isc_app_onrun");
889135446Strhodes	isc_app_run();
890135446Strhodes	cancel_all();
891135446Strhodes	destroy_libs();
892135446Strhodes	isc_app_finish();
893135446Strhodes	return ((seen_error == 0) ? 0 : 1);
894135446Strhodes}
895