1135446Strhodes/*
2254402Serwin * Copyright (C) 2004-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: dig.c,v 1.245 2011/12/07 17:23:28 each Exp $ */
19135446Strhodes
20170222Sdougb/*! \file */
21170222Sdougb
22135446Strhodes#include <config.h>
23135446Strhodes#include <stdlib.h>
24135446Strhodes#include <time.h>
25135446Strhodes#include <ctype.h>
26135446Strhodes
27135446Strhodes#include <isc/app.h>
28135446Strhodes#include <isc/netaddr.h>
29135446Strhodes#include <isc/parseint.h>
30135446Strhodes#include <isc/print.h>
31135446Strhodes#include <isc/string.h>
32135446Strhodes#include <isc/util.h>
33135446Strhodes#include <isc/task.h>
34135446Strhodes
35135446Strhodes#include <dns/byaddr.h>
36135446Strhodes#include <dns/fixedname.h>
37135446Strhodes#include <dns/masterdump.h>
38135446Strhodes#include <dns/message.h>
39135446Strhodes#include <dns/name.h>
40135446Strhodes#include <dns/rdata.h>
41135446Strhodes#include <dns/rdataset.h>
42135446Strhodes#include <dns/rdatatype.h>
43135446Strhodes#include <dns/rdataclass.h>
44135446Strhodes#include <dns/result.h>
45170222Sdougb#include <dns/tsig.h>
46135446Strhodes
47135446Strhodes#include <dig/dig.h>
48135446Strhodes
49135446Strhodes#define ADD_STRING(b, s) { 				\
50135446Strhodes	if (strlen(s) >= isc_buffer_availablelength(b)) \
51186462Sdougb		return (ISC_R_NOSPACE); 		\
52135446Strhodes	else 						\
53135446Strhodes		isc_buffer_putstr(b, s); 		\
54135446Strhodes}
55135446Strhodes
56143731Sdougb#define DIG_MAX_ADDRESSES 20
57135446Strhodes
58135446Strhodesdig_lookup_t *default_lookup = NULL;
59135446Strhodes
60135446Strhodesstatic char *batchname = NULL;
61135446Strhodesstatic FILE *batchfp = NULL;
62135446Strhodesstatic char *argv0;
63143731Sdougbstatic int addresscount = 0;
64135446Strhodes
65135446Strhodesstatic char domainopt[DNS_NAME_MAXTEXT];
66135446Strhodes
67135446Strhodesstatic isc_boolean_t short_form = ISC_FALSE, printcmd = ISC_TRUE,
68135446Strhodes	ip6_int = ISC_FALSE, plusquest = ISC_FALSE, pluscomm = ISC_FALSE,
69224092Sdougb	multiline = ISC_FALSE, nottl = ISC_FALSE, noclass = ISC_FALSE,
70254897Serwin	onesoa = ISC_FALSE, rrcomments = ISC_FALSE;
71254897Serwinstatic isc_uint32_t splitwidth = 0xffffffff;
72135446Strhodes
73170222Sdougb/*% opcode text */
74186462Sdougbstatic const char * const opcodetext[] = {
75135446Strhodes	"QUERY",
76135446Strhodes	"IQUERY",
77135446Strhodes	"STATUS",
78135446Strhodes	"RESERVED3",
79135446Strhodes	"NOTIFY",
80135446Strhodes	"UPDATE",
81135446Strhodes	"RESERVED6",
82135446Strhodes	"RESERVED7",
83135446Strhodes	"RESERVED8",
84135446Strhodes	"RESERVED9",
85135446Strhodes	"RESERVED10",
86135446Strhodes	"RESERVED11",
87135446Strhodes	"RESERVED12",
88135446Strhodes	"RESERVED13",
89135446Strhodes	"RESERVED14",
90135446Strhodes	"RESERVED15"
91135446Strhodes};
92135446Strhodes
93170222Sdougb/*% return code text */
94186462Sdougbstatic const char * const rcodetext[] = {
95135446Strhodes	"NOERROR",
96135446Strhodes	"FORMERR",
97135446Strhodes	"SERVFAIL",
98135446Strhodes	"NXDOMAIN",
99135446Strhodes	"NOTIMP",
100135446Strhodes	"REFUSED",
101135446Strhodes	"YXDOMAIN",
102135446Strhodes	"YXRRSET",
103135446Strhodes	"NXRRSET",
104135446Strhodes	"NOTAUTH",
105135446Strhodes	"NOTZONE",
106135446Strhodes	"RESERVED11",
107135446Strhodes	"RESERVED12",
108135446Strhodes	"RESERVED13",
109135446Strhodes	"RESERVED14",
110135446Strhodes	"RESERVED15",
111135446Strhodes	"BADVERS"
112135446Strhodes};
113135446Strhodes
114193149Sdougb/*% safe rcodetext[] */
115193149Sdougbstatic char *
116193149Sdougbrcode_totext(dns_rcode_t rcode)
117193149Sdougb{
118193149Sdougb	static char buf[sizeof("?65535")];
119193149Sdougb	union {
120193149Sdougb		const char *consttext;
121193149Sdougb		char *deconsttext;
122193149Sdougb	} totext;
123193149Sdougb
124193149Sdougb	if (rcode >= (sizeof(rcodetext)/sizeof(rcodetext[0]))) {
125193149Sdougb		snprintf(buf, sizeof(buf), "?%u", rcode);
126193149Sdougb		totext.deconsttext = buf;
127193149Sdougb	} else
128193149Sdougb		totext.consttext = rcodetext[rcode];
129193149Sdougb	return totext.deconsttext;
130193149Sdougb}
131193149Sdougb
132170222Sdougb/*% print usage */
133135446Strhodesstatic void
134135446Strhodesprint_usage(FILE *fp) {
135135446Strhodes	fputs(
136135446Strhodes"Usage:  dig [@global-server] [domain] [q-type] [q-class] {q-opt}\n"
137135446Strhodes"            {global-d-opt} host [@local-server] {local-d-opt}\n"
138135446Strhodes"            [ host [@local-server] {local-d-opt} [...]]\n", fp);
139135446Strhodes}
140135446Strhodes
141224092SdougbISC_PLATFORM_NORETURN_PRE static void
142224092Sdougbusage(void) ISC_PLATFORM_NORETURN_POST;
143224092Sdougb
144135446Strhodesstatic void
145135446Strhodesusage(void) {
146135446Strhodes	print_usage(stderr);
147135446Strhodes	fputs("\nUse \"dig -h\" (or \"dig -h | more\") "
148135446Strhodes	      "for complete list of options\n", stderr);
149135446Strhodes	exit(1);
150135446Strhodes}
151135446Strhodes
152170222Sdougb/*% version */
153135446Strhodesstatic void
154135446Strhodesversion(void) {
155135446Strhodes	fputs("DiG " VERSION "\n", stderr);
156135446Strhodes}
157135446Strhodes
158170222Sdougb/*% help */
159135446Strhodesstatic void
160135446Strhodeshelp(void) {
161135446Strhodes	print_usage(stdout);
162135446Strhodes	fputs(
163135446Strhodes"Where:  domain	  is in the Domain Name System\n"
164135446Strhodes"        q-class  is one of (in,hs,ch,...) [default: in]\n"
165135446Strhodes"        q-type   is one of (a,any,mx,ns,soa,hinfo,axfr,txt,...) [default:a]\n"
166135446Strhodes"                 (Use ixfr=version for type ixfr)\n"
167135446Strhodes"        q-opt    is one of:\n"
168186462Sdougb"                 -x dot-notation     (shortcut for reverse lookups)\n"
169186462Sdougb"                 -i                  (use IP6.INT for IPv6 reverse lookups)\n"
170135446Strhodes"                 -f filename         (batch mode)\n"
171135446Strhodes"                 -b address[#port]   (bind to source address/port)\n"
172135446Strhodes"                 -p port             (specify port number)\n"
173170222Sdougb"                 -q name             (specify query name)\n"
174135446Strhodes"                 -t type             (specify query type)\n"
175135446Strhodes"                 -c class            (specify query class)\n"
176135446Strhodes"                 -k keyfile          (specify tsig key file)\n"
177170222Sdougb"                 -y [hmac:]name:key  (specify named base64 tsig key)\n"
178135446Strhodes"                 -4                  (use IPv4 query transport only)\n"
179135446Strhodes"                 -6                  (use IPv6 query transport only)\n"
180186462Sdougb"                 -m                  (enable memory usage debugging)\n"
181135446Strhodes"        d-opt    is of the form +keyword[=value], where keyword is:\n"
182135446Strhodes"                 +[no]vc             (TCP mode)\n"
183135446Strhodes"                 +[no]tcp            (TCP mode, alternate syntax)\n"
184135446Strhodes"                 +time=###           (Set query timeout) [5]\n"
185135446Strhodes"                 +tries=###          (Set number of UDP attempts) [3]\n"
186135446Strhodes"                 +retry=###          (Set number of UDP retries) [2]\n"
187135446Strhodes"                 +domain=###         (Set default domainname)\n"
188135446Strhodes"                 +bufsize=###        (Set EDNS0 Max UDP packet size)\n"
189135446Strhodes"                 +ndots=###          (Set NDOTS value)\n"
190254402Serwin"                 +[no]edns[=###]     (Set EDNS version) [0]\n"
191135446Strhodes"                 +[no]search         (Set whether to use searchlist)\n"
192170222Sdougb"                 +[no]showsearch     (Search with intermediate results)\n"
193135446Strhodes"                 +[no]defname        (Ditto)\n"
194135446Strhodes"                 +[no]recurse        (Recursive mode)\n"
195135446Strhodes"                 +[no]ignore         (Don't revert to TCP for TC responses.)"
196135446Strhodes"\n"
197135446Strhodes"                 +[no]fail           (Don't try next server on SERVFAIL)\n"
198135446Strhodes"                 +[no]besteffort     (Try to parse even illegal messages)\n"
199135446Strhodes"                 +[no]aaonly         (Set AA flag in query (+[no]aaflag))\n"
200135446Strhodes"                 +[no]adflag         (Set AD flag in query)\n"
201135446Strhodes"                 +[no]cdflag         (Set CD flag in query)\n"
202135446Strhodes"                 +[no]cl             (Control display of class in records)\n"
203135446Strhodes"                 +[no]cmd            (Control display of command line)\n"
204135446Strhodes"                 +[no]comments       (Control display of comment lines)\n"
205254897Serwin"                 +[no]rrcomments     (Control display of per-record "
206254897Serwin				       "comments)\n"
207135446Strhodes"                 +[no]question       (Control display of question)\n"
208135446Strhodes"                 +[no]answer         (Control display of answer)\n"
209135446Strhodes"                 +[no]authority      (Control display of authority)\n"
210135446Strhodes"                 +[no]additional     (Control display of additional)\n"
211135446Strhodes"                 +[no]stats          (Control display of statistics)\n"
212135446Strhodes"                 +[no]short          (Disable everything except short\n"
213135446Strhodes"                                      form of answer)\n"
214135446Strhodes"                 +[no]ttlid          (Control display of ttls in records)\n"
215135446Strhodes"                 +[no]all            (Set or clear all display flags)\n"
216135446Strhodes"                 +[no]qr             (Print question before sending)\n"
217135446Strhodes"                 +[no]nssearch       (Search all authoritative nameservers)\n"
218135446Strhodes"                 +[no]identify       (ID responders in short answers)\n"
219254897Serwin"                 +[no]trace          (Trace delegation down from root [+dnssec])\n"
220135446Strhodes"                 +[no]dnssec         (Request DNSSEC records)\n"
221193149Sdougb"                 +[no]nsid           (Request Name Server ID)\n"
222135446Strhodes#ifdef DIG_SIGCHASE
223135446Strhodes"                 +[no]sigchase       (Chase DNSSEC signatures)\n"
224135446Strhodes"                 +trusted-key=####   (Trusted Key when chasing DNSSEC sigs)\n"
225135446Strhodes#if DIG_SIGCHASE_TD
226135446Strhodes"                 +[no]topdown        (Do DNSSEC validation top down mode)\n"
227135446Strhodes#endif
228135446Strhodes#endif
229254897Serwin"                 +[no]split=##       (Split hex/base64 fields into chunks)\n"
230135446Strhodes"                 +[no]multiline      (Print records in an expanded format)\n"
231224092Sdougb"                 +[no]onesoa         (AXFR prints only one soa record)\n"
232262706Serwin"                 +[no]keepopen       (Keep the TCP socket open between queries)\n"
233135446Strhodes"        global d-opts and servers (before host name) affect all queries.\n"
234135446Strhodes"        local d-opts and servers (after host name) affect only that lookup.\n"
235135446Strhodes"        -h                           (print help and exit)\n"
236135446Strhodes"        -v                           (print version and exit)\n",
237135446Strhodes	stdout);
238135446Strhodes}
239135446Strhodes
240170222Sdougb/*%
241135446Strhodes * Callback from dighost.c to print the received message.
242135446Strhodes */
243135446Strhodesvoid
244135446Strhodesreceived(int bytes, isc_sockaddr_t *from, dig_query_t *query) {
245135446Strhodes	isc_uint64_t diff;
246135446Strhodes	isc_time_t now;
247135446Strhodes	time_t tnow;
248254402Serwin	struct tm tmnow;
249254402Serwin	char time_str[100];
250135446Strhodes	char fromtext[ISC_SOCKADDR_FORMATSIZE];
251135446Strhodes
252135446Strhodes	isc_sockaddr_format(from, fromtext, sizeof(fromtext));
253135446Strhodes
254135446Strhodes	TIME_NOW(&now);
255135446Strhodes
256135446Strhodes	if (query->lookup->stats && !short_form) {
257135446Strhodes		diff = isc_time_microdiff(&now, &query->time_sent);
258135446Strhodes		printf(";; Query time: %ld msec\n", (long int)diff/1000);
259135446Strhodes		printf(";; SERVER: %s(%s)\n", fromtext, query->servname);
260135446Strhodes		time(&tnow);
261254402Serwin		tmnow  = *localtime(&tnow);
262254402Serwin		if (strftime(time_str, sizeof(time_str),
263254402Serwin			     "%a %b %d %H:%M:%S %Z %Y", &tmnow) > 0U)
264254402Serwin			printf(";; WHEN: %s\n", time_str);
265135446Strhodes		if (query->lookup->doing_xfr) {
266170222Sdougb			printf(";; XFR size: %u records (messages %u, "
267170222Sdougb			       "bytes %" ISC_PRINT_QUADFORMAT "u)\n",
268170222Sdougb			       query->rr_count, query->msg_count,
269170222Sdougb			       query->byte_count);
270135446Strhodes		} else {
271170222Sdougb			printf(";; MSG SIZE  rcvd: %u\n", bytes);
272135446Strhodes		}
273135446Strhodes		if (key != NULL) {
274135446Strhodes			if (!validated)
275135446Strhodes				puts(";; WARNING -- Some TSIG could not "
276135446Strhodes				     "be validated");
277135446Strhodes		}
278135446Strhodes		if ((key == NULL) && (keysecret[0] != 0)) {
279135446Strhodes			puts(";; WARNING -- TSIG key was not used.");
280135446Strhodes		}
281135446Strhodes		puts("");
282135446Strhodes	} else if (query->lookup->identify && !short_form) {
283135446Strhodes		diff = isc_time_microdiff(&now, &query->time_sent);
284170222Sdougb		printf(";; Received %" ISC_PRINT_QUADFORMAT "u bytes "
285170222Sdougb		       "from %s(%s) in %d ms\n\n",
286170222Sdougb		       query->lookup->doing_xfr ?
287170222Sdougb				query->byte_count : (isc_uint64_t)bytes,
288254402Serwin		       fromtext, query->userarg,
289135446Strhodes		       (int)diff/1000);
290135446Strhodes	}
291135446Strhodes}
292135446Strhodes
293135446Strhodes/*
294135446Strhodes * Callback from dighost.c to print that it is trying a server.
295135446Strhodes * Not used in dig.
296135446Strhodes * XXX print_trying
297135446Strhodes */
298135446Strhodesvoid
299135446Strhodestrying(char *frm, dig_lookup_t *lookup) {
300135446Strhodes	UNUSED(frm);
301135446Strhodes	UNUSED(lookup);
302135446Strhodes}
303135446Strhodes
304170222Sdougb/*%
305135446Strhodes * Internal print routine used to print short form replies.
306135446Strhodes */
307135446Strhodesstatic isc_result_t
308135446Strhodessay_message(dns_rdata_t *rdata, dig_query_t *query, isc_buffer_t *buf) {
309135446Strhodes	isc_result_t result;
310135446Strhodes	isc_uint64_t diff;
311135446Strhodes	isc_time_t now;
312135446Strhodes	char store[sizeof("12345678901234567890")];
313135446Strhodes
314135446Strhodes	if (query->lookup->trace || query->lookup->ns_search_only) {
315135446Strhodes		result = dns_rdatatype_totext(rdata->type, buf);
316135446Strhodes		if (result != ISC_R_SUCCESS)
317135446Strhodes			return (result);
318135446Strhodes		ADD_STRING(buf, " ");
319135446Strhodes	}
320135446Strhodes	result = dns_rdata_totext(rdata, NULL, buf);
321218384Sdougb	if (result == ISC_R_NOSPACE)
322218384Sdougb		return (result);
323135446Strhodes	check_result(result, "dns_rdata_totext");
324135446Strhodes	if (query->lookup->identify) {
325135446Strhodes		TIME_NOW(&now);
326135446Strhodes		diff = isc_time_microdiff(&now, &query->time_sent);
327135446Strhodes		ADD_STRING(buf, " from server ");
328135446Strhodes		ADD_STRING(buf, query->servname);
329135446Strhodes		snprintf(store, 19, " in %d ms.", (int)diff/1000);
330135446Strhodes		ADD_STRING(buf, store);
331135446Strhodes	}
332135446Strhodes	ADD_STRING(buf, "\n");
333135446Strhodes	return (ISC_R_SUCCESS);
334135446Strhodes}
335135446Strhodes
336170222Sdougb/*%
337135446Strhodes * short_form message print handler.  Calls above say_message()
338135446Strhodes */
339135446Strhodesstatic isc_result_t
340135446Strhodesshort_answer(dns_message_t *msg, dns_messagetextflag_t flags,
341135446Strhodes	     isc_buffer_t *buf, dig_query_t *query)
342135446Strhodes{
343135446Strhodes	dns_name_t *name;
344135446Strhodes	dns_rdataset_t *rdataset;
345135446Strhodes	isc_result_t result, loopresult;
346135446Strhodes	dns_name_t empty_name;
347135446Strhodes	dns_rdata_t rdata = DNS_RDATA_INIT;
348135446Strhodes
349135446Strhodes	UNUSED(flags);
350135446Strhodes
351135446Strhodes	dns_name_init(&empty_name, NULL);
352135446Strhodes	result = dns_message_firstname(msg, DNS_SECTION_ANSWER);
353135446Strhodes	if (result == ISC_R_NOMORE)
354135446Strhodes		return (ISC_R_SUCCESS);
355135446Strhodes	else if (result != ISC_R_SUCCESS)
356135446Strhodes		return (result);
357135446Strhodes
358135446Strhodes	for (;;) {
359135446Strhodes		name = NULL;
360135446Strhodes		dns_message_currentname(msg, DNS_SECTION_ANSWER, &name);
361135446Strhodes
362135446Strhodes		for (rdataset = ISC_LIST_HEAD(name->list);
363135446Strhodes		     rdataset != NULL;
364135446Strhodes		     rdataset = ISC_LIST_NEXT(rdataset, link)) {
365135446Strhodes			loopresult = dns_rdataset_first(rdataset);
366135446Strhodes			while (loopresult == ISC_R_SUCCESS) {
367135446Strhodes				dns_rdataset_current(rdataset, &rdata);
368135446Strhodes				result = say_message(&rdata, query,
369135446Strhodes						     buf);
370218384Sdougb				if (result == ISC_R_NOSPACE)
371218384Sdougb					return (result);
372135446Strhodes				check_result(result, "say_message");
373135446Strhodes				loopresult = dns_rdataset_next(rdataset);
374135446Strhodes				dns_rdata_reset(&rdata);
375135446Strhodes			}
376135446Strhodes		}
377135446Strhodes		result = dns_message_nextname(msg, DNS_SECTION_ANSWER);
378135446Strhodes		if (result == ISC_R_NOMORE)
379135446Strhodes			break;
380135446Strhodes		else if (result != ISC_R_SUCCESS)
381135446Strhodes			return (result);
382135446Strhodes	}
383135446Strhodes
384135446Strhodes	return (ISC_R_SUCCESS);
385135446Strhodes}
386135446Strhodes#ifdef DIG_SIGCHASE
387135446Strhodesisc_result_t
388135446Strhodesprintrdataset(dns_name_t *owner_name, dns_rdataset_t *rdataset,
389135446Strhodes	      isc_buffer_t *target)
390135446Strhodes{
391135446Strhodes	isc_result_t result;
392135446Strhodes	dns_master_style_t *style = NULL;
393135446Strhodes	unsigned int styleflags = 0;
394135446Strhodes
395135446Strhodes	if (rdataset == NULL || owner_name == NULL || target == NULL)
396135446Strhodes		return(ISC_FALSE);
397135446Strhodes
398135446Strhodes	styleflags |= DNS_STYLEFLAG_REL_OWNER;
399135446Strhodes	if (nottl)
400135446Strhodes		styleflags |= DNS_STYLEFLAG_NO_TTL;
401135446Strhodes	if (noclass)
402135446Strhodes		styleflags |= DNS_STYLEFLAG_NO_CLASS;
403254897Serwin	if (rrcomments)
404254897Serwin		styleflags |= DNS_STYLEFLAG_RRCOMMENT;
405135446Strhodes	if (multiline) {
406135446Strhodes		styleflags |= DNS_STYLEFLAG_OMIT_OWNER;
407135446Strhodes		styleflags |= DNS_STYLEFLAG_OMIT_CLASS;
408135446Strhodes		styleflags |= DNS_STYLEFLAG_REL_DATA;
409135446Strhodes		styleflags |= DNS_STYLEFLAG_OMIT_TTL;
410135446Strhodes		styleflags |= DNS_STYLEFLAG_TTL;
411135446Strhodes		styleflags |= DNS_STYLEFLAG_MULTILINE;
412135446Strhodes		styleflags |= DNS_STYLEFLAG_COMMENT;
413254897Serwin		styleflags |= DNS_STYLEFLAG_RRCOMMENT;
414135446Strhodes	}
415254897Serwin
416135446Strhodes	if (multiline || (nottl && noclass))
417254897Serwin		result = dns_master_stylecreate2(&style, styleflags,
418254897Serwin						24, 24, 24, 32, 80, 8,
419254897Serwin						splitwidth, mctx);
420135446Strhodes	else if (nottl || noclass)
421254897Serwin		result = dns_master_stylecreate2(&style, styleflags,
422254897Serwin						24, 24, 32, 40, 80, 8,
423254897Serwin						splitwidth, mctx);
424186462Sdougb	else
425254897Serwin		result = dns_master_stylecreate2(&style, styleflags,
426254897Serwin						24, 32, 40, 48, 80, 8,
427254897Serwin						splitwidth, mctx);
428135446Strhodes	check_result(result, "dns_master_stylecreate");
429135446Strhodes
430135446Strhodes	result = dns_master_rdatasettotext(owner_name, rdataset, style, target);
431135446Strhodes
432135446Strhodes	if (style != NULL)
433135446Strhodes		dns_master_styledestroy(&style, mctx);
434186462Sdougb
435135446Strhodes	return(result);
436135446Strhodes}
437135446Strhodes#endif
438135446Strhodes
439135446Strhodes/*
440135446Strhodes * Callback from dighost.c to print the reply from a server
441135446Strhodes */
442135446Strhodesisc_result_t
443135446Strhodesprintmessage(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers) {
444135446Strhodes	isc_result_t result;
445135446Strhodes	dns_messagetextflag_t flags;
446135446Strhodes	isc_buffer_t *buf = NULL;
447135446Strhodes	unsigned int len = OUTPUTBUF;
448135446Strhodes	dns_master_style_t *style = NULL;
449135446Strhodes	unsigned int styleflags = 0;
450135446Strhodes
451135446Strhodes	styleflags |= DNS_STYLEFLAG_REL_OWNER;
452254897Serwin	if (query->lookup->comments)
453254897Serwin		styleflags |= DNS_STYLEFLAG_COMMENT;
454254897Serwin	if (rrcomments)
455254897Serwin		styleflags |= DNS_STYLEFLAG_RRCOMMENT;
456135446Strhodes	if (nottl)
457135446Strhodes		styleflags |= DNS_STYLEFLAG_NO_TTL;
458135446Strhodes	if (noclass)
459135446Strhodes		styleflags |= DNS_STYLEFLAG_NO_CLASS;
460135446Strhodes	if (multiline) {
461135446Strhodes		styleflags |= DNS_STYLEFLAG_OMIT_OWNER;
462135446Strhodes		styleflags |= DNS_STYLEFLAG_OMIT_CLASS;
463135446Strhodes		styleflags |= DNS_STYLEFLAG_REL_DATA;
464135446Strhodes		styleflags |= DNS_STYLEFLAG_OMIT_TTL;
465135446Strhodes		styleflags |= DNS_STYLEFLAG_TTL;
466135446Strhodes		styleflags |= DNS_STYLEFLAG_MULTILINE;
467254897Serwin		styleflags |= DNS_STYLEFLAG_RRCOMMENT;
468135446Strhodes	}
469135446Strhodes	if (multiline || (nottl && noclass))
470254897Serwin		result = dns_master_stylecreate2(&style, styleflags,
471254897Serwin						 24, 24, 24, 32, 80, 8,
472254897Serwin						 splitwidth, mctx);
473135446Strhodes	else if (nottl || noclass)
474254897Serwin		result = dns_master_stylecreate2(&style, styleflags,
475254897Serwin						 24, 24, 32, 40, 80, 8,
476254897Serwin						 splitwidth, mctx);
477186462Sdougb	else
478254897Serwin		result = dns_master_stylecreate2(&style, styleflags,
479254897Serwin						 24, 32, 40, 48, 80, 8,
480254897Serwin						 splitwidth, mctx);
481135446Strhodes	check_result(result, "dns_master_stylecreate");
482135446Strhodes
483135446Strhodes	if (query->lookup->cmdline[0] != 0) {
484135446Strhodes		if (!short_form)
485135446Strhodes			fputs(query->lookup->cmdline, stdout);
486135446Strhodes		query->lookup->cmdline[0]=0;
487135446Strhodes	}
488135446Strhodes	debug("printmessage(%s %s %s)", headers ? "headers" : "noheaders",
489135446Strhodes	      query->lookup->comments ? "comments" : "nocomments",
490135446Strhodes	      short_form ? "short_form" : "long_form");
491135446Strhodes
492135446Strhodes	flags = 0;
493135446Strhodes	if (!headers) {
494135446Strhodes		flags |= DNS_MESSAGETEXTFLAG_NOHEADERS;
495135446Strhodes		flags |= DNS_MESSAGETEXTFLAG_NOCOMMENTS;
496135446Strhodes	}
497224092Sdougb	if (onesoa && query->lookup->rdtype == dns_rdatatype_axfr)
498224092Sdougb		flags |= (query->msg_count == 0) ? DNS_MESSAGETEXTFLAG_ONESOA :
499224092Sdougb						   DNS_MESSAGETEXTFLAG_OMITSOA;
500135446Strhodes	if (!query->lookup->comments)
501135446Strhodes		flags |= DNS_MESSAGETEXTFLAG_NOCOMMENTS;
502135446Strhodes
503135446Strhodes	result = isc_buffer_allocate(mctx, &buf, len);
504135446Strhodes	check_result(result, "isc_buffer_allocate");
505135446Strhodes
506135446Strhodes	if (query->lookup->comments && !short_form) {
507135446Strhodes		if (query->lookup->cmdline[0] != 0)
508135446Strhodes			printf("; %s\n", query->lookup->cmdline);
509135446Strhodes		if (msg == query->lookup->sendmsg)
510135446Strhodes			printf(";; Sending:\n");
511135446Strhodes		else
512135446Strhodes			printf(";; Got answer:\n");
513135446Strhodes
514135446Strhodes		if (headers) {
515135446Strhodes			printf(";; ->>HEADER<<- opcode: %s, status: %s, "
516135446Strhodes			       "id: %u\n",
517193149Sdougb			       opcodetext[msg->opcode],
518193149Sdougb			       rcode_totext(msg->rcode),
519135446Strhodes			       msg->id);
520135446Strhodes			printf(";; flags:");
521135446Strhodes			if ((msg->flags & DNS_MESSAGEFLAG_QR) != 0)
522135446Strhodes				printf(" qr");
523135446Strhodes			if ((msg->flags & DNS_MESSAGEFLAG_AA) != 0)
524135446Strhodes				printf(" aa");
525135446Strhodes			if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0)
526135446Strhodes				printf(" tc");
527135446Strhodes			if ((msg->flags & DNS_MESSAGEFLAG_RD) != 0)
528135446Strhodes				printf(" rd");
529135446Strhodes			if ((msg->flags & DNS_MESSAGEFLAG_RA) != 0)
530135446Strhodes				printf(" ra");
531135446Strhodes			if ((msg->flags & DNS_MESSAGEFLAG_AD) != 0)
532135446Strhodes				printf(" ad");
533135446Strhodes			if ((msg->flags & DNS_MESSAGEFLAG_CD) != 0)
534135446Strhodes				printf(" cd");
535218384Sdougb			if ((msg->flags & 0x0040U) != 0)
536218384Sdougb				printf("; MBZ: 0x4");
537135446Strhodes
538135446Strhodes			printf("; QUERY: %u, ANSWER: %u, "
539135446Strhodes			       "AUTHORITY: %u, ADDITIONAL: %u\n",
540135446Strhodes			       msg->counts[DNS_SECTION_QUESTION],
541135446Strhodes			       msg->counts[DNS_SECTION_ANSWER],
542135446Strhodes			       msg->counts[DNS_SECTION_AUTHORITY],
543135446Strhodes			       msg->counts[DNS_SECTION_ADDITIONAL]);
544170222Sdougb
545170222Sdougb			if (msg != query->lookup->sendmsg &&
546170222Sdougb			    (msg->flags & DNS_MESSAGEFLAG_RD) != 0 &&
547170222Sdougb			    (msg->flags & DNS_MESSAGEFLAG_RA) == 0)
548170222Sdougb				printf(";; WARNING: recursion requested "
549170222Sdougb				       "but not available\n");
550135446Strhodes		}
551254402Serwin		if (msg != query->lookup->sendmsg &&
552254402Serwin		    query->lookup->edns != -1 && msg->opt == NULL &&
553254402Serwin		    (msg->rcode == dns_rcode_formerr ||
554254402Serwin		     msg->rcode == dns_rcode_notimp))
555254402Serwin			printf("\n;; WARNING: EDNS query returned status "
556262706Serwin			       "%s - retry with '%s+noedns'\n",
557262706Serwin			       rcode_totext(msg->rcode),
558262706Serwin			       query->lookup->dnssec ? "+nodnssec ": "");
559170222Sdougb		if (msg != query->lookup->sendmsg && extrabytes != 0U)
560262706Serwin			printf(";; WARNING: Message has %u extra byte%s at "
561170222Sdougb			       "end\n", extrabytes, extrabytes != 0 ? "s" : "");
562135446Strhodes	}
563135446Strhodes
564135446Strhodesrepopulate_buffer:
565135446Strhodes
566135446Strhodes	if (query->lookup->comments && headers && !short_form) {
567135446Strhodes		result = dns_message_pseudosectiontotext(msg,
568135446Strhodes			 DNS_PSEUDOSECTION_OPT,
569135446Strhodes			 style, flags, buf);
570135446Strhodes		if (result == ISC_R_NOSPACE) {
571135446Strhodesbuftoosmall:
572135446Strhodes			len += OUTPUTBUF;
573135446Strhodes			isc_buffer_free(&buf);
574135446Strhodes			result = isc_buffer_allocate(mctx, &buf, len);
575135446Strhodes			if (result == ISC_R_SUCCESS)
576135446Strhodes				goto repopulate_buffer;
577135446Strhodes			else
578135446Strhodes				goto cleanup;
579135446Strhodes		}
580135446Strhodes		check_result(result,
581135446Strhodes		     "dns_message_pseudosectiontotext");
582135446Strhodes	}
583135446Strhodes
584135446Strhodes	if (query->lookup->section_question && headers) {
585135446Strhodes		if (!short_form) {
586135446Strhodes			result = dns_message_sectiontotext(msg,
587135446Strhodes						       DNS_SECTION_QUESTION,
588135446Strhodes						       style, flags, buf);
589135446Strhodes			if (result == ISC_R_NOSPACE)
590135446Strhodes				goto buftoosmall;
591135446Strhodes			check_result(result, "dns_message_sectiontotext");
592135446Strhodes		}
593135446Strhodes	}
594135446Strhodes	if (query->lookup->section_answer) {
595135446Strhodes		if (!short_form) {
596135446Strhodes			result = dns_message_sectiontotext(msg,
597135446Strhodes						       DNS_SECTION_ANSWER,
598135446Strhodes						       style, flags, buf);
599135446Strhodes			if (result == ISC_R_NOSPACE)
600135446Strhodes				goto buftoosmall;
601135446Strhodes			check_result(result, "dns_message_sectiontotext");
602135446Strhodes		} else {
603135446Strhodes			result = short_answer(msg, flags, buf, query);
604135446Strhodes			if (result == ISC_R_NOSPACE)
605135446Strhodes				goto buftoosmall;
606135446Strhodes			check_result(result, "short_answer");
607135446Strhodes		}
608135446Strhodes	}
609135446Strhodes	if (query->lookup->section_authority) {
610135446Strhodes		if (!short_form) {
611135446Strhodes			result = dns_message_sectiontotext(msg,
612135446Strhodes						       DNS_SECTION_AUTHORITY,
613135446Strhodes						       style, flags, buf);
614135446Strhodes			if (result == ISC_R_NOSPACE)
615135446Strhodes				goto buftoosmall;
616135446Strhodes			check_result(result, "dns_message_sectiontotext");
617135446Strhodes		}
618135446Strhodes	}
619135446Strhodes	if (query->lookup->section_additional) {
620135446Strhodes		if (!short_form) {
621135446Strhodes			result = dns_message_sectiontotext(msg,
622135446Strhodes						      DNS_SECTION_ADDITIONAL,
623135446Strhodes						      style, flags, buf);
624135446Strhodes			if (result == ISC_R_NOSPACE)
625135446Strhodes				goto buftoosmall;
626135446Strhodes			check_result(result, "dns_message_sectiontotext");
627135446Strhodes			/*
628135446Strhodes			 * Only print the signature on the first record.
629135446Strhodes			 */
630135446Strhodes			if (headers) {
631135446Strhodes				result = dns_message_pseudosectiontotext(
632135446Strhodes						   msg,
633135446Strhodes						   DNS_PSEUDOSECTION_TSIG,
634135446Strhodes						   style, flags, buf);
635135446Strhodes				if (result == ISC_R_NOSPACE)
636135446Strhodes					goto buftoosmall;
637135446Strhodes				check_result(result,
638135446Strhodes					  "dns_message_pseudosectiontotext");
639135446Strhodes				result = dns_message_pseudosectiontotext(
640135446Strhodes						   msg,
641135446Strhodes						   DNS_PSEUDOSECTION_SIG0,
642135446Strhodes						   style, flags, buf);
643135446Strhodes				if (result == ISC_R_NOSPACE)
644135446Strhodes					goto buftoosmall;
645135446Strhodes				check_result(result,
646135446Strhodes					   "dns_message_pseudosectiontotext");
647135446Strhodes			}
648135446Strhodes		}
649135446Strhodes	}
650153816Sdougb
651135446Strhodes	if (headers && query->lookup->comments && !short_form)
652135446Strhodes		printf("\n");
653135446Strhodes
654135446Strhodes	printf("%.*s", (int)isc_buffer_usedlength(buf),
655135446Strhodes	       (char *)isc_buffer_base(buf));
656135446Strhodes	isc_buffer_free(&buf);
657135446Strhodes
658135446Strhodescleanup:
659135446Strhodes	if (style != NULL)
660135446Strhodes		dns_master_styledestroy(&style, mctx);
661135446Strhodes	return (result);
662135446Strhodes}
663135446Strhodes
664170222Sdougb/*%
665135446Strhodes * print the greeting message when the program first starts up.
666135446Strhodes */
667135446Strhodesstatic void
668135446Strhodesprintgreeting(int argc, char **argv, dig_lookup_t *lookup) {
669135446Strhodes	int i;
670135446Strhodes	int remaining;
671135446Strhodes	static isc_boolean_t first = ISC_TRUE;
672135446Strhodes	char append[MXNAME];
673135446Strhodes
674135446Strhodes	if (printcmd) {
675135446Strhodes		lookup->cmdline[sizeof(lookup->cmdline) - 1] = 0;
676135446Strhodes		snprintf(lookup->cmdline, sizeof(lookup->cmdline),
677135446Strhodes			 "%s; <<>> DiG " VERSION " <<>>",
678135446Strhodes			 first?"\n":"");
679135446Strhodes		i = 1;
680135446Strhodes		while (i < argc) {
681135446Strhodes			snprintf(append, sizeof(append), " %s", argv[i++]);
682135446Strhodes			remaining = sizeof(lookup->cmdline) -
683135446Strhodes				    strlen(lookup->cmdline) - 1;
684135446Strhodes			strncat(lookup->cmdline, append, remaining);
685135446Strhodes		}
686135446Strhodes		remaining = sizeof(lookup->cmdline) -
687135446Strhodes			    strlen(lookup->cmdline) - 1;
688135446Strhodes		strncat(lookup->cmdline, "\n", remaining);
689143731Sdougb		if (first && addresscount != 0) {
690143731Sdougb			snprintf(append, sizeof(append),
691143731Sdougb				 "; (%d server%s found)\n",
692143731Sdougb				 addresscount,
693143731Sdougb				 addresscount > 1 ? "s" : "");
694143731Sdougb			remaining = sizeof(lookup->cmdline) -
695143731Sdougb				    strlen(lookup->cmdline) - 1;
696143731Sdougb			strncat(lookup->cmdline, append, remaining);
697143731Sdougb		}
698135446Strhodes		if (first) {
699186462Sdougb			snprintf(append, sizeof(append),
700193149Sdougb				 ";; global options:%s%s\n",
701193149Sdougb				 short_form ? " +short" : "",
702193149Sdougb				 printcmd ? " +cmd" : "");
703135446Strhodes			first = ISC_FALSE;
704135446Strhodes			remaining = sizeof(lookup->cmdline) -
705135446Strhodes				    strlen(lookup->cmdline) - 1;
706135446Strhodes			strncat(lookup->cmdline, append, remaining);
707135446Strhodes		}
708135446Strhodes	}
709135446Strhodes}
710135446Strhodes
711170222Sdougb/*%
712135446Strhodes * We're not using isc_commandline_parse() here since the command line
713135446Strhodes * syntax of dig is quite a bit different from that which can be described
714135446Strhodes * by that routine.
715135446Strhodes * XXX doc options
716135446Strhodes */
717135446Strhodes
718135446Strhodesstatic void
719135446Strhodesplus_option(char *option, isc_boolean_t is_batchfile,
720135446Strhodes	    dig_lookup_t *lookup)
721135446Strhodes{
722224092Sdougb	isc_result_t result;
723135446Strhodes	char option_store[256];
724135446Strhodes	char *cmd, *value, *ptr;
725224092Sdougb	isc_uint32_t num;
726135446Strhodes	isc_boolean_t state = ISC_TRUE;
727135446Strhodes#ifdef DIG_SIGCHASE
728135446Strhodes	size_t n;
729135446Strhodes#endif
730135446Strhodes
731135446Strhodes	strncpy(option_store, option, sizeof(option_store));
732135446Strhodes	option_store[sizeof(option_store)-1]=0;
733135446Strhodes	ptr = option_store;
734135446Strhodes	cmd = next_token(&ptr,"=");
735135446Strhodes	if (cmd == NULL) {
736135446Strhodes		printf(";; Invalid option %s\n", option_store);
737135446Strhodes		return;
738135446Strhodes	}
739135446Strhodes	value = ptr;
740135446Strhodes	if (strncasecmp(cmd, "no", 2)==0) {
741135446Strhodes		cmd += 2;
742135446Strhodes		state = ISC_FALSE;
743135446Strhodes	}
744135446Strhodes
745135446Strhodes#define FULLCHECK(A) \
746135446Strhodes	do { \
747135446Strhodes		size_t _l = strlen(cmd); \
748135446Strhodes		if (_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) \
749135446Strhodes			goto invalid_option; \
750135446Strhodes	} while (0)
751135446Strhodes#define FULLCHECK2(A, B) \
752135446Strhodes	do { \
753135446Strhodes		size_t _l = strlen(cmd); \
754135446Strhodes		if ((_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) && \
755135446Strhodes		    (_l >= sizeof(B) || strncasecmp(cmd, B, _l) != 0)) \
756135446Strhodes			goto invalid_option; \
757135446Strhodes	} while (0)
758135446Strhodes
759135446Strhodes	switch (cmd[0]) {
760135446Strhodes	case 'a':
761135446Strhodes		switch (cmd[1]) {
762135446Strhodes		case 'a': /* aaonly / aaflag */
763135446Strhodes			FULLCHECK2("aaonly", "aaflag");
764135446Strhodes			lookup->aaonly = state;
765135446Strhodes			break;
766186462Sdougb		case 'd':
767135446Strhodes			switch (cmd[2]) {
768135446Strhodes			case 'd': /* additional */
769135446Strhodes				FULLCHECK("additional");
770135446Strhodes				lookup->section_additional = state;
771135446Strhodes				break;
772135446Strhodes			case 'f': /* adflag */
773224092Sdougb			case '\0': /* +ad is a synonym for +adflag */
774135446Strhodes				FULLCHECK("adflag");
775135446Strhodes				lookup->adflag = state;
776135446Strhodes				break;
777135446Strhodes			default:
778135446Strhodes				goto invalid_option;
779135446Strhodes			}
780135446Strhodes			break;
781135446Strhodes		case 'l': /* all */
782135446Strhodes			FULLCHECK("all");
783135446Strhodes			lookup->section_question = state;
784135446Strhodes			lookup->section_authority = state;
785135446Strhodes			lookup->section_answer = state;
786135446Strhodes			lookup->section_additional = state;
787135446Strhodes			lookup->comments = state;
788254897Serwin			rrcomments = state;
789135446Strhodes			lookup->stats = state;
790135446Strhodes			printcmd = state;
791135446Strhodes			break;
792135446Strhodes		case 'n': /* answer */
793135446Strhodes			FULLCHECK("answer");
794135446Strhodes			lookup->section_answer = state;
795135446Strhodes			break;
796135446Strhodes		case 'u': /* authority */
797135446Strhodes			FULLCHECK("authority");
798135446Strhodes			lookup->section_authority = state;
799135446Strhodes			break;
800135446Strhodes		default:
801135446Strhodes			goto invalid_option;
802135446Strhodes		}
803135446Strhodes		break;
804135446Strhodes	case 'b':
805135446Strhodes		switch (cmd[1]) {
806135446Strhodes		case 'e':/* besteffort */
807135446Strhodes			FULLCHECK("besteffort");
808135446Strhodes			lookup->besteffort = state;
809135446Strhodes			break;
810135446Strhodes		case 'u':/* bufsize */
811135446Strhodes			FULLCHECK("bufsize");
812135446Strhodes			if (value == NULL)
813135446Strhodes				goto need_value;
814135446Strhodes			if (!state)
815135446Strhodes				goto invalid_option;
816224092Sdougb			result = parse_uint(&num, value, COMMSIZE,
817224092Sdougb					    "buffer size");
818224092Sdougb			if (result != ISC_R_SUCCESS)
819224092Sdougb				fatal("Couldn't parse buffer size");
820224092Sdougb			lookup->udpsize = num;
821135446Strhodes			break;
822135446Strhodes		default:
823135446Strhodes			goto invalid_option;
824135446Strhodes		}
825135446Strhodes		break;
826135446Strhodes	case 'c':
827135446Strhodes		switch (cmd[1]) {
828135446Strhodes		case 'd':/* cdflag */
829224092Sdougb			switch (cmd[2]) {
830224092Sdougb			case 'f': /* cdflag */
831224092Sdougb			case '\0': /* +cd is a synonym for +cdflag */
832224092Sdougb				FULLCHECK("cdflag");
833224092Sdougb				lookup->cdflag = state;
834224092Sdougb				break;
835224092Sdougb			default:
836224092Sdougb				goto invalid_option;
837224092Sdougb			}
838135446Strhodes			break;
839135446Strhodes		case 'l': /* cl */
840135446Strhodes			FULLCHECK("cl");
841153816Sdougb			noclass = ISC_TF(!state);
842135446Strhodes			break;
843135446Strhodes		case 'm': /* cmd */
844135446Strhodes			FULLCHECK("cmd");
845135446Strhodes			printcmd = state;
846135446Strhodes			break;
847135446Strhodes		case 'o': /* comments */
848135446Strhodes			FULLCHECK("comments");
849135446Strhodes			lookup->comments = state;
850135446Strhodes			if (lookup == default_lookup)
851135446Strhodes				pluscomm = state;
852135446Strhodes			break;
853135446Strhodes		default:
854135446Strhodes			goto invalid_option;
855135446Strhodes		}
856135446Strhodes		break;
857135446Strhodes	case 'd':
858135446Strhodes		switch (cmd[1]) {
859135446Strhodes		case 'e': /* defname */
860135446Strhodes			FULLCHECK("defname");
861193149Sdougb			if (!lookup->trace) {
862193149Sdougb				usesearch = state;
863193149Sdougb			}
864135446Strhodes			break;
865186462Sdougb		case 'n': /* dnssec */
866135446Strhodes			FULLCHECK("dnssec");
867170222Sdougb			if (state && lookup->edns == -1)
868170222Sdougb				lookup->edns = 0;
869135446Strhodes			lookup->dnssec = state;
870135446Strhodes			break;
871186462Sdougb		case 'o': /* domain */
872135446Strhodes			FULLCHECK("domain");
873135446Strhodes			if (value == NULL)
874135446Strhodes				goto need_value;
875135446Strhodes			if (!state)
876135446Strhodes				goto invalid_option;
877135446Strhodes			strncpy(domainopt, value, sizeof(domainopt));
878135446Strhodes			domainopt[sizeof(domainopt)-1] = '\0';
879135446Strhodes			break;
880135446Strhodes		default:
881135446Strhodes			goto invalid_option;
882135446Strhodes		}
883135446Strhodes		break;
884170222Sdougb	case 'e':
885170222Sdougb		FULLCHECK("edns");
886170222Sdougb		if (!state) {
887170222Sdougb			lookup->edns = -1;
888170222Sdougb			break;
889170222Sdougb		}
890254402Serwin		if (value == NULL) {
891254402Serwin			lookup->edns = 0;
892254402Serwin			break;
893254402Serwin		}
894224092Sdougb		result = parse_uint(&num, value, 255, "edns");
895224092Sdougb		if (result != ISC_R_SUCCESS)
896224092Sdougb			fatal("Couldn't parse edns");
897224092Sdougb		lookup->edns = num;
898170222Sdougb		break;
899135446Strhodes	case 'f': /* fail */
900135446Strhodes		FULLCHECK("fail");
901135446Strhodes		lookup->servfail_stops = state;
902135446Strhodes		break;
903135446Strhodes	case 'i':
904135446Strhodes		switch (cmd[1]) {
905135446Strhodes		case 'd': /* identify */
906135446Strhodes			FULLCHECK("identify");
907135446Strhodes			lookup->identify = state;
908135446Strhodes			break;
909135446Strhodes		case 'g': /* ignore */
910193149Sdougb		default: /* Inherits default for compatibility */
911135446Strhodes			FULLCHECK("ignore");
912135446Strhodes			lookup->ignore = ISC_TRUE;
913135446Strhodes		}
914135446Strhodes		break;
915262706Serwin	case 'k':
916262706Serwin		FULLCHECK("keepopen");
917262706Serwin		keep_open = state;
918262706Serwin		break;
919135446Strhodes	case 'm': /* multiline */
920135446Strhodes		FULLCHECK("multiline");
921135446Strhodes		multiline = state;
922135446Strhodes		break;
923135446Strhodes	case 'n':
924135446Strhodes		switch (cmd[1]) {
925135446Strhodes		case 'd': /* ndots */
926135446Strhodes			FULLCHECK("ndots");
927135446Strhodes			if (value == NULL)
928135446Strhodes				goto need_value;
929135446Strhodes			if (!state)
930135446Strhodes				goto invalid_option;
931224092Sdougb			result = parse_uint(&num, value, MAXNDOTS, "ndots");
932224092Sdougb			if (result != ISC_R_SUCCESS)
933224092Sdougb				fatal("Couldn't parse ndots");
934224092Sdougb			ndots = num;
935135446Strhodes			break;
936193149Sdougb		case 's':
937193149Sdougb			switch (cmd[2]) {
938193149Sdougb			case 'i': /* nsid */
939193149Sdougb				FULLCHECK("nsid");
940193149Sdougb				if (state && lookup->edns == -1)
941193149Sdougb					lookup->edns = 0;
942193149Sdougb				lookup->nsid = state;
943193149Sdougb				break;
944193149Sdougb			case 's': /* nssearch */
945193149Sdougb				FULLCHECK("nssearch");
946193149Sdougb				lookup->ns_search_only = state;
947193149Sdougb				if (state) {
948193149Sdougb					lookup->trace_root = ISC_TRUE;
949193149Sdougb					lookup->recurse = ISC_TRUE;
950193149Sdougb					lookup->identify = ISC_TRUE;
951193149Sdougb					lookup->stats = ISC_FALSE;
952193149Sdougb					lookup->comments = ISC_FALSE;
953254897Serwin					rrcomments = ISC_FALSE;
954193149Sdougb					lookup->section_additional = ISC_FALSE;
955193149Sdougb					lookup->section_authority = ISC_FALSE;
956193149Sdougb					lookup->section_question = ISC_FALSE;
957193149Sdougb					lookup->rdtype = dns_rdatatype_ns;
958193149Sdougb					lookup->rdtypeset = ISC_TRUE;
959193149Sdougb					short_form = ISC_TRUE;
960193149Sdougb				}
961193149Sdougb				break;
962193149Sdougb			default:
963193149Sdougb				goto invalid_option;
964135446Strhodes			}
965135446Strhodes			break;
966135446Strhodes		default:
967135446Strhodes			goto invalid_option;
968135446Strhodes		}
969135446Strhodes		break;
970224092Sdougb	case 'o':
971224092Sdougb		FULLCHECK("onesoa");
972224092Sdougb		onesoa = state;
973224092Sdougb		break;
974186462Sdougb	case 'q':
975135446Strhodes		switch (cmd[1]) {
976135446Strhodes		case 'r': /* qr */
977135446Strhodes			FULLCHECK("qr");
978135446Strhodes			qr = state;
979135446Strhodes			break;
980135446Strhodes		case 'u': /* question */
981135446Strhodes			FULLCHECK("question");
982135446Strhodes			lookup->section_question = state;
983135446Strhodes			if (lookup == default_lookup)
984135446Strhodes				plusquest = state;
985135446Strhodes			break;
986135446Strhodes		default:
987135446Strhodes			goto invalid_option;
988135446Strhodes		}
989135446Strhodes		break;
990135446Strhodes	case 'r':
991135446Strhodes		switch (cmd[1]) {
992135446Strhodes		case 'e':
993135446Strhodes			switch (cmd[2]) {
994135446Strhodes			case 'c': /* recurse */
995135446Strhodes				FULLCHECK("recurse");
996135446Strhodes				lookup->recurse = state;
997135446Strhodes				break;
998135446Strhodes			case 't': /* retry / retries */
999135446Strhodes				FULLCHECK2("retry", "retries");
1000135446Strhodes				if (value == NULL)
1001135446Strhodes					goto need_value;
1002135446Strhodes				if (!state)
1003135446Strhodes					goto invalid_option;
1004224092Sdougb				result = parse_uint(&lookup->retries, value,
1005224092Sdougb						    MAXTRIES - 1, "retries");
1006224092Sdougb				if (result != ISC_R_SUCCESS)
1007224092Sdougb					fatal("Couldn't parse retries");
1008135446Strhodes				lookup->retries++;
1009135446Strhodes				break;
1010135446Strhodes			default:
1011135446Strhodes				goto invalid_option;
1012135446Strhodes			}
1013135446Strhodes			break;
1014254897Serwin		case 'r': /* rrcomments */
1015254897Serwin			FULLCHECK("rrcomments");
1016254897Serwin			rrcomments = state;
1017254897Serwin			break;
1018135446Strhodes		default:
1019135446Strhodes			goto invalid_option;
1020135446Strhodes		}
1021135446Strhodes		break;
1022135446Strhodes	case 's':
1023135446Strhodes		switch (cmd[1]) {
1024135446Strhodes		case 'e': /* search */
1025135446Strhodes			FULLCHECK("search");
1026193149Sdougb			if (!lookup->trace) {
1027193149Sdougb				usesearch = state;
1028193149Sdougb			}
1029135446Strhodes			break;
1030170222Sdougb		case 'h':
1031170222Sdougb			if (cmd[2] != 'o')
1032170222Sdougb				goto invalid_option;
1033170222Sdougb			switch (cmd[3]) {
1034170222Sdougb			case 'r': /* short */
1035170222Sdougb				FULLCHECK("short");
1036170222Sdougb				short_form = state;
1037170222Sdougb				if (state) {
1038170222Sdougb					printcmd = ISC_FALSE;
1039170222Sdougb					lookup->section_additional = ISC_FALSE;
1040170222Sdougb					lookup->section_answer = ISC_TRUE;
1041170222Sdougb					lookup->section_authority = ISC_FALSE;
1042170222Sdougb					lookup->section_question = ISC_FALSE;
1043170222Sdougb					lookup->comments = ISC_FALSE;
1044254897Serwin					rrcomments = ISC_FALSE;
1045170222Sdougb					lookup->stats = ISC_FALSE;
1046170222Sdougb				}
1047170222Sdougb				break;
1048170222Sdougb			case 'w': /* showsearch */
1049170222Sdougb				FULLCHECK("showsearch");
1050193149Sdougb				if (!lookup->trace) {
1051193149Sdougb					showsearch = state;
1052193149Sdougb					usesearch = state;
1053193149Sdougb				}
1054170222Sdougb				break;
1055170222Sdougb			default:
1056170222Sdougb				goto invalid_option;
1057135446Strhodes			}
1058135446Strhodes			break;
1059135446Strhodes#ifdef DIG_SIGCHASE
1060135446Strhodes		case 'i': /* sigchase */
1061186462Sdougb			FULLCHECK("sigchase");
1062135446Strhodes			lookup->sigchase = state;
1063135446Strhodes			if (lookup->sigchase)
1064135446Strhodes				lookup->dnssec = ISC_TRUE;
1065186462Sdougb			break;
1066135446Strhodes#endif
1067254897Serwin		case 'p': /* split */
1068254897Serwin			FULLCHECK("split");
1069254897Serwin			if (value != NULL && !state)
1070254897Serwin				goto invalid_option;
1071254897Serwin			if (!state) {
1072254897Serwin				splitwidth = 0;
1073254897Serwin				break;
1074254897Serwin			} else if (value == NULL)
1075254897Serwin				break;
1076254897Serwin
1077254897Serwin			result = parse_uint(&splitwidth, value,
1078254897Serwin					    1023, "split");
1079254897Serwin			if (splitwidth % 4 != 0) {
1080254897Serwin				splitwidth = ((splitwidth + 3) / 4) * 4;
1081254897Serwin				fprintf(stderr, ";; Warning, split must be "
1082254897Serwin						"a multiple of 4; adjusting "
1083254897Serwin						"to %d\n", splitwidth);
1084254897Serwin			}
1085254897Serwin			/*
1086254897Serwin			 * There is an adjustment done in the
1087254897Serwin			 * totext_<rrtype>() functions which causes
1088254897Serwin			 * splitwidth to shrink.  This is okay when we're
1089254897Serwin			 * using the default width but incorrect in this
1090254897Serwin			 * case, so we correct for it
1091254897Serwin			 */
1092254897Serwin			if (splitwidth)
1093254897Serwin				splitwidth += 3;
1094254897Serwin			if (result != ISC_R_SUCCESS)
1095254897Serwin				fatal("Couldn't parse retries");
1096254897Serwin			break;
1097135446Strhodes		case 't': /* stats */
1098135446Strhodes			FULLCHECK("stats");
1099135446Strhodes			lookup->stats = state;
1100135446Strhodes			break;
1101135446Strhodes		default:
1102135446Strhodes			goto invalid_option;
1103135446Strhodes		}
1104135446Strhodes		break;
1105135446Strhodes	case 't':
1106135446Strhodes		switch (cmd[1]) {
1107135446Strhodes		case 'c': /* tcp */
1108135446Strhodes			FULLCHECK("tcp");
1109135446Strhodes			if (!is_batchfile)
1110135446Strhodes				lookup->tcp_mode = state;
1111135446Strhodes			break;
1112135446Strhodes		case 'i': /* timeout */
1113135446Strhodes			FULLCHECK("timeout");
1114135446Strhodes			if (value == NULL)
1115135446Strhodes				goto need_value;
1116135446Strhodes			if (!state)
1117135446Strhodes				goto invalid_option;
1118224092Sdougb			result = parse_uint(&timeout, value, MAXTIMEOUT,
1119224092Sdougb					    "timeout");
1120224092Sdougb			if (result != ISC_R_SUCCESS)
1121224092Sdougb				fatal("Couldn't parse timeout");
1122135446Strhodes			if (timeout == 0)
1123135446Strhodes				timeout = 1;
1124135446Strhodes			break;
1125135446Strhodes#if DIG_SIGCHASE_TD
1126186462Sdougb		case 'o': /* topdown */
1127135446Strhodes			FULLCHECK("topdown");
1128135446Strhodes			lookup->do_topdown = state;
1129135446Strhodes			break;
1130135446Strhodes#endif
1131135446Strhodes		case 'r':
1132135446Strhodes			switch (cmd[2]) {
1133135446Strhodes			case 'a': /* trace */
1134135446Strhodes				FULLCHECK("trace");
1135135446Strhodes				lookup->trace = state;
1136135446Strhodes				lookup->trace_root = state;
1137135446Strhodes				if (state) {
1138135446Strhodes					lookup->recurse = ISC_FALSE;
1139135446Strhodes					lookup->identify = ISC_TRUE;
1140135446Strhodes					lookup->comments = ISC_FALSE;
1141254897Serwin					rrcomments = ISC_FALSE;
1142135446Strhodes					lookup->stats = ISC_FALSE;
1143135446Strhodes					lookup->section_additional = ISC_FALSE;
1144135446Strhodes					lookup->section_authority = ISC_TRUE;
1145143731Sdougb					lookup->section_question = ISC_FALSE;
1146254897Serwin					lookup->dnssec = ISC_TRUE;
1147193149Sdougb					usesearch = ISC_FALSE;
1148135446Strhodes				}
1149135446Strhodes				break;
1150135446Strhodes			case 'i': /* tries */
1151135446Strhodes				FULLCHECK("tries");
1152135446Strhodes				if (value == NULL)
1153135446Strhodes					goto need_value;
1154135446Strhodes				if (!state)
1155135446Strhodes					goto invalid_option;
1156224092Sdougb				result = parse_uint(&lookup->retries, value,
1157224092Sdougb						    MAXTRIES, "tries");
1158224092Sdougb				if (result != ISC_R_SUCCESS)
1159224092Sdougb					fatal("Couldn't parse tries");
1160135446Strhodes				if (lookup->retries == 0)
1161135446Strhodes					lookup->retries = 1;
1162135446Strhodes				break;
1163135446Strhodes#ifdef DIG_SIGCHASE
1164135446Strhodes			case 'u': /* trusted-key */
1165143731Sdougb				FULLCHECK("trusted-key");
1166186462Sdougb				if (value == NULL)
1167135446Strhodes					goto need_value;
1168135446Strhodes				if (!state)
1169135446Strhodes					goto invalid_option;
1170135446Strhodes				n = strlcpy(trustedkey, ptr,
1171135446Strhodes					    sizeof(trustedkey));
1172135446Strhodes				if (n >= sizeof(trustedkey))
1173135446Strhodes					fatal("trusted key too large");
1174135446Strhodes				break;
1175135446Strhodes#endif
1176135446Strhodes			default:
1177135446Strhodes				goto invalid_option;
1178135446Strhodes			}
1179135446Strhodes			break;
1180135446Strhodes		case 't': /* ttlid */
1181135446Strhodes			FULLCHECK("ttlid");
1182153816Sdougb			nottl = ISC_TF(!state);
1183135446Strhodes			break;
1184135446Strhodes		default:
1185135446Strhodes			goto invalid_option;
1186135446Strhodes		}
1187135446Strhodes		break;
1188135446Strhodes	case 'v':
1189135446Strhodes		FULLCHECK("vc");
1190135446Strhodes		if (!is_batchfile)
1191135446Strhodes			lookup->tcp_mode = state;
1192135446Strhodes		break;
1193135446Strhodes	default:
1194135446Strhodes	invalid_option:
1195135446Strhodes	need_value:
1196135446Strhodes		fprintf(stderr, "Invalid option: +%s\n",
1197135446Strhodes			 option);
1198135446Strhodes		usage();
1199135446Strhodes	}
1200135446Strhodes	return;
1201135446Strhodes}
1202135446Strhodes
1203170222Sdougb/*%
1204170222Sdougb * #ISC_TRUE returned if value was used
1205135446Strhodes */
1206135446Strhodesstatic const char *single_dash_opts = "46dhimnv";
1207135446Strhodesstatic const char *dash_opts = "46bcdfhikmnptvyx";
1208135446Strhodesstatic isc_boolean_t
1209135446Strhodesdash_option(char *option, char *next, dig_lookup_t **lookup,
1210174187Sdougb	    isc_boolean_t *open_type_class, isc_boolean_t *need_clone,
1211174187Sdougb	    isc_boolean_t config_only, int argc, char **argv,
1212174187Sdougb	    isc_boolean_t *firstarg)
1213135446Strhodes{
1214170222Sdougb	char opt, *value, *ptr, *ptr2, *ptr3;
1215135446Strhodes	isc_result_t result;
1216135446Strhodes	isc_boolean_t value_from_next;
1217135446Strhodes	isc_textregion_t tr;
1218135446Strhodes	dns_rdatatype_t rdtype;
1219135446Strhodes	dns_rdataclass_t rdclass;
1220135446Strhodes	char textname[MXNAME];
1221135446Strhodes	struct in_addr in4;
1222135446Strhodes	struct in6_addr in6;
1223135446Strhodes	in_port_t srcport;
1224135446Strhodes	char *hash, *cmd;
1225224092Sdougb	isc_uint32_t num;
1226135446Strhodes
1227135446Strhodes	while (strpbrk(option, single_dash_opts) == &option[0]) {
1228135446Strhodes		/*
1229135446Strhodes		 * Since the -[46dhimnv] options do not take an argument,
1230135446Strhodes		 * account for them (in any number and/or combination)
1231135446Strhodes		 * if they appear as the first character(s) of a q-opt.
1232135446Strhodes		 */
1233135446Strhodes		opt = option[0];
1234135446Strhodes		switch (opt) {
1235135446Strhodes		case '4':
1236135446Strhodes			if (have_ipv4) {
1237135446Strhodes				isc_net_disableipv6();
1238135446Strhodes				have_ipv6 = ISC_FALSE;
1239135446Strhodes			} else {
1240135446Strhodes				fatal("can't find IPv4 networking");
1241224092Sdougb				/* NOTREACHED */
1242135446Strhodes				return (ISC_FALSE);
1243135446Strhodes			}
1244135446Strhodes			break;
1245135446Strhodes		case '6':
1246135446Strhodes			if (have_ipv6) {
1247135446Strhodes				isc_net_disableipv4();
1248135446Strhodes				have_ipv4 = ISC_FALSE;
1249135446Strhodes			} else {
1250135446Strhodes				fatal("can't find IPv6 networking");
1251224092Sdougb				/* NOTREACHED */
1252135446Strhodes				return (ISC_FALSE);
1253135446Strhodes			}
1254135446Strhodes			break;
1255135446Strhodes		case 'd':
1256135446Strhodes			ptr = strpbrk(&option[1], dash_opts);
1257135446Strhodes			if (ptr != &option[1]) {
1258135446Strhodes				cmd = option;
1259135446Strhodes				FULLCHECK("debug");
1260135446Strhodes				debugging = ISC_TRUE;
1261135446Strhodes				return (ISC_FALSE);
1262135446Strhodes			} else
1263135446Strhodes				debugging = ISC_TRUE;
1264135446Strhodes			break;
1265135446Strhodes		case 'h':
1266135446Strhodes			help();
1267135446Strhodes			exit(0);
1268135446Strhodes			break;
1269135446Strhodes		case 'i':
1270135446Strhodes			ip6_int = ISC_TRUE;
1271135446Strhodes			break;
1272135446Strhodes		case 'm': /* memdebug */
1273135446Strhodes			/* memdebug is handled in preparse_args() */
1274135446Strhodes			break;
1275135446Strhodes		case 'n':
1276135446Strhodes			/* deprecated */
1277135446Strhodes			break;
1278135446Strhodes		case 'v':
1279135446Strhodes			version();
1280135446Strhodes			exit(0);
1281135446Strhodes			break;
1282135446Strhodes		}
1283135446Strhodes		if (strlen(option) > 1U)
1284135446Strhodes			option = &option[1];
1285135446Strhodes		else
1286135446Strhodes			return (ISC_FALSE);
1287135446Strhodes	}
1288135446Strhodes	opt = option[0];
1289135446Strhodes	if (strlen(option) > 1U) {
1290135446Strhodes		value_from_next = ISC_FALSE;
1291135446Strhodes		value = &option[1];
1292135446Strhodes	} else {
1293135446Strhodes		value_from_next = ISC_TRUE;
1294135446Strhodes		value = next;
1295135446Strhodes	}
1296135446Strhodes	if (value == NULL)
1297135446Strhodes		goto invalid_option;
1298135446Strhodes	switch (opt) {
1299135446Strhodes	case 'b':
1300135446Strhodes		hash = strchr(value, '#');
1301135446Strhodes		if (hash != NULL) {
1302224092Sdougb			result = parse_uint(&num, hash + 1, MAXPORT,
1303224092Sdougb					    "port number");
1304224092Sdougb			if (result != ISC_R_SUCCESS)
1305224092Sdougb				fatal("Couldn't parse port number");
1306224092Sdougb			srcport = num;
1307135446Strhodes			*hash = '\0';
1308135446Strhodes		} else
1309135446Strhodes			srcport = 0;
1310135446Strhodes		if (have_ipv6 && inet_pton(AF_INET6, value, &in6) == 1) {
1311135446Strhodes			isc_sockaddr_fromin6(&bind_address, &in6, srcport);
1312135446Strhodes			isc_net_disableipv4();
1313135446Strhodes		} else if (have_ipv4 && inet_pton(AF_INET, value, &in4) == 1) {
1314135446Strhodes			isc_sockaddr_fromin(&bind_address, &in4, srcport);
1315135446Strhodes			isc_net_disableipv6();
1316135446Strhodes		} else {
1317135446Strhodes			if (hash != NULL)
1318135446Strhodes				*hash = '#';
1319135446Strhodes			fatal("invalid address %s", value);
1320135446Strhodes		}
1321135446Strhodes		if (hash != NULL)
1322135446Strhodes			*hash = '#';
1323135446Strhodes		specified_source = ISC_TRUE;
1324135446Strhodes		return (value_from_next);
1325135446Strhodes	case 'c':
1326135446Strhodes		if ((*lookup)->rdclassset) {
1327135446Strhodes			fprintf(stderr, ";; Warning, extra class option\n");
1328135446Strhodes		}
1329135446Strhodes		*open_type_class = ISC_FALSE;
1330135446Strhodes		tr.base = value;
1331135446Strhodes		tr.length = strlen(value);
1332135446Strhodes		result = dns_rdataclass_fromtext(&rdclass,
1333135446Strhodes						 (isc_textregion_t *)&tr);
1334135446Strhodes		if (result == ISC_R_SUCCESS) {
1335135446Strhodes			(*lookup)->rdclass = rdclass;
1336135446Strhodes			(*lookup)->rdclassset = ISC_TRUE;
1337135446Strhodes		} else
1338135446Strhodes			fprintf(stderr, ";; Warning, ignoring "
1339135446Strhodes				"invalid class %s\n",
1340135446Strhodes				value);
1341135446Strhodes		return (value_from_next);
1342135446Strhodes	case 'f':
1343135446Strhodes		batchname = value;
1344135446Strhodes		return (value_from_next);
1345135446Strhodes	case 'k':
1346135446Strhodes		strncpy(keyfile, value, sizeof(keyfile));
1347135446Strhodes		keyfile[sizeof(keyfile)-1]=0;
1348135446Strhodes		return (value_from_next);
1349135446Strhodes	case 'p':
1350224092Sdougb		result = parse_uint(&num, value, MAXPORT, "port number");
1351224092Sdougb		if (result != ISC_R_SUCCESS)
1352224092Sdougb			fatal("Couldn't parse port number");
1353224092Sdougb		port = num;
1354135446Strhodes		return (value_from_next);
1355170222Sdougb	case 'q':
1356170222Sdougb		if (!config_only) {
1357174187Sdougb			if (*need_clone)
1358174187Sdougb				(*lookup) = clone_lookup(default_lookup,
1359174187Sdougb							 ISC_TRUE);
1360174187Sdougb			*need_clone = ISC_TRUE;
1361186462Sdougb			strncpy((*lookup)->textname, value,
1362170222Sdougb				sizeof((*lookup)->textname));
1363170222Sdougb			(*lookup)->textname[sizeof((*lookup)->textname)-1]=0;
1364170222Sdougb			(*lookup)->trace_root = ISC_TF((*lookup)->trace  ||
1365170222Sdougb						     (*lookup)->ns_search_only);
1366170222Sdougb			(*lookup)->new_search = ISC_TRUE;
1367174187Sdougb			if (*firstarg) {
1368174187Sdougb				printgreeting(argc, argv, *lookup);
1369174187Sdougb				*firstarg = ISC_FALSE;
1370174187Sdougb			}
1371170222Sdougb			ISC_LIST_APPEND(lookup_list, (*lookup), link);
1372170222Sdougb			debug("looking up %s", (*lookup)->textname);
1373170222Sdougb		}
1374170222Sdougb		return (value_from_next);
1375135446Strhodes	case 't':
1376135446Strhodes		*open_type_class = ISC_FALSE;
1377135446Strhodes		if (strncasecmp(value, "ixfr=", 5) == 0) {
1378135446Strhodes			rdtype = dns_rdatatype_ixfr;
1379135446Strhodes			result = ISC_R_SUCCESS;
1380135446Strhodes		} else {
1381135446Strhodes			tr.base = value;
1382135446Strhodes			tr.length = strlen(value);
1383135446Strhodes			result = dns_rdatatype_fromtext(&rdtype,
1384135446Strhodes						(isc_textregion_t *)&tr);
1385135446Strhodes			if (result == ISC_R_SUCCESS &&
1386135446Strhodes			    rdtype == dns_rdatatype_ixfr) {
1387135446Strhodes				result = DNS_R_UNKNOWN;
1388135446Strhodes			}
1389135446Strhodes		}
1390135446Strhodes		if (result == ISC_R_SUCCESS) {
1391135446Strhodes			if ((*lookup)->rdtypeset) {
1392135446Strhodes				fprintf(stderr, ";; Warning, "
1393135446Strhodes						"extra type option\n");
1394135446Strhodes			}
1395135446Strhodes			if (rdtype == dns_rdatatype_ixfr) {
1396224092Sdougb				isc_uint32_t serial;
1397135446Strhodes				(*lookup)->rdtype = dns_rdatatype_ixfr;
1398135446Strhodes				(*lookup)->rdtypeset = ISC_TRUE;
1399224092Sdougb				result = parse_uint(&serial, &value[5],
1400224092Sdougb					   MAXSERIAL, "serial number");
1401224092Sdougb				if (result != ISC_R_SUCCESS)
1402224092Sdougb					fatal("Couldn't parse serial number");
1403224092Sdougb				(*lookup)->ixfr_serial = serial;
1404135446Strhodes				(*lookup)->section_question = plusquest;
1405135446Strhodes				(*lookup)->comments = pluscomm;
1406193149Sdougb				(*lookup)->tcp_mode = ISC_TRUE;
1407135446Strhodes			} else {
1408135446Strhodes				(*lookup)->rdtype = rdtype;
1409135446Strhodes				(*lookup)->rdtypeset = ISC_TRUE;
1410135446Strhodes				if (rdtype == dns_rdatatype_axfr) {
1411135446Strhodes					(*lookup)->section_question = plusquest;
1412135446Strhodes					(*lookup)->comments = pluscomm;
1413135446Strhodes				}
1414135446Strhodes				(*lookup)->ixfr_serial = ISC_FALSE;
1415135446Strhodes			}
1416135446Strhodes		} else
1417135446Strhodes			fprintf(stderr, ";; Warning, ignoring "
1418135446Strhodes				 "invalid type %s\n",
1419135446Strhodes				 value);
1420135446Strhodes		return (value_from_next);
1421135446Strhodes	case 'y':
1422170222Sdougb		ptr = next_token(&value,":");	/* hmac type or name */
1423135446Strhodes		if (ptr == NULL) {
1424135446Strhodes			usage();
1425135446Strhodes		}
1426170222Sdougb		ptr2 = next_token(&value, ":");	/* name or secret */
1427170222Sdougb		if (ptr2 == NULL)
1428170222Sdougb			usage();
1429170222Sdougb		ptr3 = next_token(&value,":"); /* secret or NULL */
1430186462Sdougb		if (ptr3 != NULL) {
1431224092Sdougb			parse_hmac(ptr);
1432170222Sdougb			ptr = ptr2;
1433170222Sdougb			ptr2 = ptr3;
1434170222Sdougb		} else  {
1435170222Sdougb			hmacname = DNS_TSIG_HMACMD5_NAME;
1436170222Sdougb			digestbits = 0;
1437170222Sdougb		}
1438135446Strhodes		strncpy(keynametext, ptr, sizeof(keynametext));
1439135446Strhodes		keynametext[sizeof(keynametext)-1]=0;
1440170222Sdougb		strncpy(keysecret, ptr2, sizeof(keysecret));
1441135446Strhodes		keysecret[sizeof(keysecret)-1]=0;
1442135446Strhodes		return (value_from_next);
1443135446Strhodes	case 'x':
1444174187Sdougb		if (*need_clone)
1445174187Sdougb			*lookup = clone_lookup(default_lookup, ISC_TRUE);
1446174187Sdougb		*need_clone = ISC_TRUE;
1447135446Strhodes		if (get_reverse(textname, sizeof(textname), value,
1448135446Strhodes				ip6_int, ISC_FALSE) == ISC_R_SUCCESS) {
1449135446Strhodes			strncpy((*lookup)->textname, textname,
1450135446Strhodes				sizeof((*lookup)->textname));
1451135446Strhodes			debug("looking up %s", (*lookup)->textname);
1452135446Strhodes			(*lookup)->trace_root = ISC_TF((*lookup)->trace  ||
1453135446Strhodes						(*lookup)->ns_search_only);
1454135446Strhodes			(*lookup)->ip6_int = ip6_int;
1455135446Strhodes			if (!(*lookup)->rdtypeset)
1456135446Strhodes				(*lookup)->rdtype = dns_rdatatype_ptr;
1457135446Strhodes			if (!(*lookup)->rdclassset)
1458135446Strhodes				(*lookup)->rdclass = dns_rdataclass_in;
1459135446Strhodes			(*lookup)->new_search = ISC_TRUE;
1460174187Sdougb			if (*firstarg) {
1461174187Sdougb				printgreeting(argc, argv, *lookup);
1462174187Sdougb				*firstarg = ISC_FALSE;
1463174187Sdougb			}
1464135446Strhodes			ISC_LIST_APPEND(lookup_list, *lookup, link);
1465135446Strhodes		} else {
1466135446Strhodes			fprintf(stderr, "Invalid IP address %s\n", value);
1467135446Strhodes			exit(1);
1468135446Strhodes		}
1469135446Strhodes		return (value_from_next);
1470135446Strhodes	invalid_option:
1471135446Strhodes	default:
1472135446Strhodes		fprintf(stderr, "Invalid option: -%s\n", option);
1473135446Strhodes		usage();
1474135446Strhodes	}
1475224092Sdougb	/* NOTREACHED */
1476135446Strhodes	return (ISC_FALSE);
1477135446Strhodes}
1478135446Strhodes
1479170222Sdougb/*%
1480135446Strhodes * Because we may be trying to do memory allocation recording, we're going
1481135446Strhodes * to need to parse the arguments for the -m *before* we start the main
1482135446Strhodes * argument parsing routine.
1483170222Sdougb *
1484135446Strhodes * I'd prefer not to have to do this, but I am not quite sure how else to
1485135446Strhodes * fix the problem.  Argument parsing in dig involves memory allocation
1486135446Strhodes * by its nature, so it can't be done in the main argument parser.
1487135446Strhodes */
1488135446Strhodesstatic void
1489135446Strhodespreparse_args(int argc, char **argv) {
1490135446Strhodes	int rc;
1491135446Strhodes	char **rv;
1492135446Strhodes	char *option;
1493135446Strhodes
1494135446Strhodes	rc = argc;
1495135446Strhodes	rv = argv;
1496135446Strhodes	for (rc--, rv++; rc > 0; rc--, rv++) {
1497135446Strhodes		if (rv[0][0] != '-')
1498135446Strhodes			continue;
1499135446Strhodes		option = &rv[0][1];
1500135446Strhodes		while (strpbrk(option, single_dash_opts) == &option[0]) {
1501135446Strhodes			if (option[0] == 'm') {
1502135446Strhodes				memdebugging = ISC_TRUE;
1503135446Strhodes				isc_mem_debugging = ISC_MEM_DEBUGTRACE |
1504135446Strhodes					ISC_MEM_DEBUGRECORD;
1505135446Strhodes				return;
1506135446Strhodes			}
1507135446Strhodes			option = &option[1];
1508135446Strhodes		}
1509135446Strhodes	}
1510135446Strhodes}
1511135446Strhodes
1512135446Strhodesstatic void
1513135446Strhodesparse_args(isc_boolean_t is_batchfile, isc_boolean_t config_only,
1514135446Strhodes	   int argc, char **argv) {
1515135446Strhodes	isc_result_t result;
1516135446Strhodes	isc_textregion_t tr;
1517135446Strhodes	isc_boolean_t firstarg = ISC_TRUE;
1518135446Strhodes	dig_lookup_t *lookup = NULL;
1519135446Strhodes	dns_rdatatype_t rdtype;
1520135446Strhodes	dns_rdataclass_t rdclass;
1521135446Strhodes	isc_boolean_t open_type_class = ISC_TRUE;
1522135446Strhodes	char batchline[MXNAME];
1523135446Strhodes	int bargc;
1524135446Strhodes	char *bargv[64];
1525135446Strhodes	int rc;
1526135446Strhodes	char **rv;
1527135446Strhodes#ifndef NOPOSIX
1528135446Strhodes	char *homedir;
1529135446Strhodes	char rcfile[256];
1530135446Strhodes#endif
1531135446Strhodes	char *input;
1532174187Sdougb	int i;
1533174187Sdougb	isc_boolean_t need_clone = ISC_TRUE;
1534135446Strhodes
1535135446Strhodes	/*
1536135446Strhodes	 * The semantics for parsing the args is a bit complex; if
1537135446Strhodes	 * we don't have a host yet, make the arg apply globally,
1538135446Strhodes	 * otherwise make it apply to the latest host.  This is
1539135446Strhodes	 * a bit different than the previous versions, but should
1540135446Strhodes	 * form a consistent user interface.
1541135446Strhodes	 *
1542135446Strhodes	 * First, create a "default lookup" which won't actually be used
1543135446Strhodes	 * anywhere, except for cloning into new lookups
1544135446Strhodes	 */
1545135446Strhodes
1546135446Strhodes	debug("parse_args()");
1547135446Strhodes	if (!is_batchfile) {
1548135446Strhodes		debug("making new lookup");
1549135446Strhodes		default_lookup = make_empty_lookup();
1550254897Serwin		default_lookup->adflag = ISC_TRUE;
1551254897Serwin		default_lookup->edns = 0;
1552135446Strhodes
1553135446Strhodes#ifndef NOPOSIX
1554135446Strhodes		/*
1555135446Strhodes		 * Treat ${HOME}/.digrc as a special batchfile
1556135446Strhodes		 */
1557135446Strhodes		INSIST(batchfp == NULL);
1558135446Strhodes		homedir = getenv("HOME");
1559135446Strhodes		if (homedir != NULL) {
1560135446Strhodes			unsigned int n;
1561135446Strhodes			n = snprintf(rcfile, sizeof(rcfile), "%s/.digrc",
1562186462Sdougb				     homedir);
1563135446Strhodes			if (n < sizeof(rcfile))
1564135446Strhodes				batchfp = fopen(rcfile, "r");
1565135446Strhodes		}
1566135446Strhodes		if (batchfp != NULL) {
1567135446Strhodes			while (fgets(batchline, sizeof(batchline),
1568135446Strhodes				     batchfp) != 0) {
1569135446Strhodes				debug("config line %s", batchline);
1570135446Strhodes				bargc = 1;
1571135446Strhodes				input = batchline;
1572135446Strhodes				bargv[bargc] = next_token(&input, " \t\r\n");
1573135446Strhodes				while ((bargv[bargc] != NULL) &&
1574135446Strhodes				       (bargc < 62)) {
1575135446Strhodes					bargc++;
1576135446Strhodes					bargv[bargc] =
1577135446Strhodes						next_token(&input, " \t\r\n");
1578135446Strhodes				}
1579135446Strhodes
1580135446Strhodes				bargv[0] = argv[0];
1581135446Strhodes				argv0 = argv[0];
1582135446Strhodes
1583174187Sdougb				for(i = 0; i < bargc; i++)
1584174187Sdougb					debug(".digrc argv %d: %s",
1585174187Sdougb					      i, bargv[i]);
1586135446Strhodes				parse_args(ISC_TRUE, ISC_TRUE, bargc,
1587135446Strhodes					   (char **)bargv);
1588135446Strhodes			}
1589135446Strhodes			fclose(batchfp);
1590135446Strhodes		}
1591135446Strhodes#endif
1592135446Strhodes	}
1593135446Strhodes
1594174187Sdougb	if (is_batchfile && !config_only) {
1595174187Sdougb		/* Processing '-f batchfile'. */
1596174187Sdougb		lookup = clone_lookup(default_lookup, ISC_TRUE);
1597174187Sdougb		need_clone = ISC_FALSE;
1598174187Sdougb	} else
1599174187Sdougb		lookup = default_lookup;
1600135446Strhodes
1601135446Strhodes	rc = argc;
1602135446Strhodes	rv = argv;
1603135446Strhodes	for (rc--, rv++; rc > 0; rc--, rv++) {
1604135446Strhodes		debug("main parsing %s", rv[0]);
1605135446Strhodes		if (strncmp(rv[0], "%", 1) == 0)
1606135446Strhodes			break;
1607135446Strhodes		if (strncmp(rv[0], "@", 1) == 0) {
1608234010Sdougb			addresscount = getaddresses(lookup, &rv[0][1], NULL);
1609135446Strhodes		} else if (rv[0][0] == '+') {
1610135446Strhodes			plus_option(&rv[0][1], is_batchfile,
1611135446Strhodes				    lookup);
1612135446Strhodes		} else if (rv[0][0] == '-') {
1613135446Strhodes			if (rc <= 1) {
1614135446Strhodes				if (dash_option(&rv[0][1], NULL,
1615170222Sdougb						&lookup, &open_type_class,
1616174187Sdougb						&need_clone, config_only,
1617174187Sdougb						argc, argv, &firstarg)) {
1618135446Strhodes					rc--;
1619135446Strhodes					rv++;
1620135446Strhodes				}
1621135446Strhodes			} else {
1622135446Strhodes				if (dash_option(&rv[0][1], rv[1],
1623170222Sdougb						&lookup, &open_type_class,
1624174187Sdougb						&need_clone, config_only,
1625174187Sdougb						argc, argv, &firstarg)) {
1626135446Strhodes					rc--;
1627135446Strhodes					rv++;
1628135446Strhodes				}
1629135446Strhodes			}
1630135446Strhodes		} else {
1631135446Strhodes			/*
1632135446Strhodes			 * Anything which isn't an option
1633135446Strhodes			 */
1634135446Strhodes			if (open_type_class) {
1635165071Sdougb				if (strncasecmp(rv[0], "ixfr=", 5) == 0) {
1636135446Strhodes					rdtype = dns_rdatatype_ixfr;
1637135446Strhodes					result = ISC_R_SUCCESS;
1638135446Strhodes				} else {
1639135446Strhodes					tr.base = rv[0];
1640135446Strhodes					tr.length = strlen(rv[0]);
1641135446Strhodes					result = dns_rdatatype_fromtext(&rdtype,
1642186462Sdougb						(isc_textregion_t *)&tr);
1643135446Strhodes					if (result == ISC_R_SUCCESS &&
1644135446Strhodes					    rdtype == dns_rdatatype_ixfr) {
1645135446Strhodes						fprintf(stderr, ";; Warning, "
1646135446Strhodes							"ixfr requires a "
1647135446Strhodes							"serial number\n");
1648135446Strhodes						continue;
1649135446Strhodes					}
1650135446Strhodes				}
1651135446Strhodes				if (result == ISC_R_SUCCESS) {
1652135446Strhodes					if (lookup->rdtypeset) {
1653135446Strhodes						fprintf(stderr, ";; Warning, "
1654135446Strhodes							"extra type option\n");
1655135446Strhodes					}
1656135446Strhodes					if (rdtype == dns_rdatatype_ixfr) {
1657224092Sdougb						isc_uint32_t serial;
1658135446Strhodes						lookup->rdtype =
1659135446Strhodes							dns_rdatatype_ixfr;
1660135446Strhodes						lookup->rdtypeset = ISC_TRUE;
1661224092Sdougb						result = parse_uint(&serial,
1662224092Sdougb								    &rv[0][5],
1663224092Sdougb								    MAXSERIAL,
1664224092Sdougb							      "serial number");
1665224092Sdougb						if (result != ISC_R_SUCCESS)
1666224092Sdougb							fatal("Couldn't parse "
1667224092Sdougb							      "serial number");
1668224092Sdougb						lookup->ixfr_serial = serial;
1669135446Strhodes						lookup->section_question =
1670135446Strhodes							plusquest;
1671135446Strhodes						lookup->comments = pluscomm;
1672193149Sdougb						lookup->tcp_mode = ISC_TRUE;
1673135446Strhodes					} else {
1674135446Strhodes						lookup->rdtype = rdtype;
1675135446Strhodes						lookup->rdtypeset = ISC_TRUE;
1676135446Strhodes						if (rdtype ==
1677135446Strhodes						    dns_rdatatype_axfr) {
1678135446Strhodes						    lookup->section_question =
1679135446Strhodes								plusquest;
1680135446Strhodes						    lookup->comments = pluscomm;
1681135446Strhodes						}
1682135446Strhodes						lookup->ixfr_serial = ISC_FALSE;
1683135446Strhodes					}
1684135446Strhodes					continue;
1685135446Strhodes				}
1686135446Strhodes				result = dns_rdataclass_fromtext(&rdclass,
1687135446Strhodes						     (isc_textregion_t *)&tr);
1688135446Strhodes				if (result == ISC_R_SUCCESS) {
1689135446Strhodes					if (lookup->rdclassset) {
1690135446Strhodes						fprintf(stderr, ";; Warning, "
1691135446Strhodes							"extra class option\n");
1692135446Strhodes					}
1693135446Strhodes					lookup->rdclass = rdclass;
1694135446Strhodes					lookup->rdclassset = ISC_TRUE;
1695135446Strhodes					continue;
1696135446Strhodes				}
1697135446Strhodes			}
1698174187Sdougb
1699135446Strhodes			if (!config_only) {
1700174187Sdougb				if (need_clone)
1701174187Sdougb					lookup = clone_lookup(default_lookup,
1702174187Sdougb								      ISC_TRUE);
1703174187Sdougb				need_clone = ISC_TRUE;
1704186462Sdougb				strncpy(lookup->textname, rv[0],
1705135446Strhodes					sizeof(lookup->textname));
1706135446Strhodes				lookup->textname[sizeof(lookup->textname)-1]=0;
1707135446Strhodes				lookup->trace_root = ISC_TF(lookup->trace  ||
1708135446Strhodes						     lookup->ns_search_only);
1709135446Strhodes				lookup->new_search = ISC_TRUE;
1710174187Sdougb				if (firstarg) {
1711174187Sdougb					printgreeting(argc, argv, lookup);
1712174187Sdougb					firstarg = ISC_FALSE;
1713174187Sdougb				}
1714135446Strhodes				ISC_LIST_APPEND(lookup_list, lookup, link);
1715135446Strhodes				debug("looking up %s", lookup->textname);
1716135446Strhodes			}
1717135446Strhodes			/* XXX Error message */
1718135446Strhodes		}
1719135446Strhodes	}
1720174187Sdougb
1721135446Strhodes	/*
1722135446Strhodes	 * If we have a batchfile, seed the lookup list with the
1723135446Strhodes	 * first entry, then trust the callback in dighost_shutdown
1724135446Strhodes	 * to get the rest
1725135446Strhodes	 */
1726135446Strhodes	if ((batchname != NULL) && !(is_batchfile)) {
1727135446Strhodes		if (strcmp(batchname, "-") == 0)
1728135446Strhodes			batchfp = stdin;
1729135446Strhodes		else
1730135446Strhodes			batchfp = fopen(batchname, "r");
1731135446Strhodes		if (batchfp == NULL) {
1732135446Strhodes			perror(batchname);
1733135446Strhodes			if (exitcode < 8)
1734135446Strhodes				exitcode = 8;
1735135446Strhodes			fatal("couldn't open specified batch file");
1736135446Strhodes		}
1737135446Strhodes		/* XXX Remove code dup from shutdown code */
1738135446Strhodes	next_line:
1739135446Strhodes		if (fgets(batchline, sizeof(batchline), batchfp) != 0) {
1740135446Strhodes			bargc = 1;
1741135446Strhodes			debug("batch line %s", batchline);
1742135446Strhodes			if (batchline[0] == '\r' || batchline[0] == '\n'
1743135446Strhodes			    || batchline[0] == '#' || batchline[0] == ';')
1744135446Strhodes				goto next_line;
1745135446Strhodes			input = batchline;
1746135446Strhodes			bargv[bargc] = next_token(&input, " \t\r\n");
1747135446Strhodes			while ((bargv[bargc] != NULL) && (bargc < 14)) {
1748135446Strhodes				bargc++;
1749135446Strhodes				bargv[bargc] = next_token(&input, " \t\r\n");
1750135446Strhodes			}
1751135446Strhodes
1752135446Strhodes			bargv[0] = argv[0];
1753135446Strhodes			argv0 = argv[0];
1754135446Strhodes
1755174187Sdougb			for(i = 0; i < bargc; i++)
1756174187Sdougb				debug("batch argv %d: %s", i, bargv[i]);
1757135446Strhodes			parse_args(ISC_TRUE, ISC_FALSE, bargc, (char **)bargv);
1758174187Sdougb			return;
1759135446Strhodes		}
1760174187Sdougb		return;
1761135446Strhodes	}
1762135446Strhodes	/*
1763135446Strhodes	 * If no lookup specified, search for root
1764135446Strhodes	 */
1765135446Strhodes	if ((lookup_list.head == NULL) && !config_only) {
1766174187Sdougb		if (need_clone)
1767174187Sdougb			lookup = clone_lookup(default_lookup, ISC_TRUE);
1768174187Sdougb		need_clone = ISC_TRUE;
1769135446Strhodes		lookup->trace_root = ISC_TF(lookup->trace ||
1770135446Strhodes					    lookup->ns_search_only);
1771135446Strhodes		lookup->new_search = ISC_TRUE;
1772135446Strhodes		strcpy(lookup->textname, ".");
1773135446Strhodes		lookup->rdtype = dns_rdatatype_ns;
1774135446Strhodes		lookup->rdtypeset = ISC_TRUE;
1775135446Strhodes		if (firstarg) {
1776135446Strhodes			printgreeting(argc, argv, lookup);
1777135446Strhodes			firstarg = ISC_FALSE;
1778135446Strhodes		}
1779135446Strhodes		ISC_LIST_APPEND(lookup_list, lookup, link);
1780135446Strhodes	}
1781174187Sdougb	if (!need_clone)
1782174187Sdougb		destroy_lookup(lookup);
1783135446Strhodes}
1784135446Strhodes
1785135446Strhodes/*
1786135446Strhodes * Callback from dighost.c to allow program-specific shutdown code.
1787135446Strhodes * Here, we're possibly reading from a batch file, then shutting down
1788135446Strhodes * for real if there's nothing in the batch file to read.
1789135446Strhodes */
1790135446Strhodesvoid
1791135446Strhodesdighost_shutdown(void) {
1792135446Strhodes	char batchline[MXNAME];
1793135446Strhodes	int bargc;
1794135446Strhodes	char *bargv[16];
1795135446Strhodes	char *input;
1796174187Sdougb	int i;
1797135446Strhodes
1798135446Strhodes	if (batchname == NULL) {
1799135446Strhodes		isc_app_shutdown();
1800135446Strhodes		return;
1801135446Strhodes	}
1802135446Strhodes
1803135446Strhodes	fflush(stdout);
1804135446Strhodes	if (feof(batchfp)) {
1805135446Strhodes		batchname = NULL;
1806135446Strhodes		isc_app_shutdown();
1807135446Strhodes		if (batchfp != stdin)
1808135446Strhodes			fclose(batchfp);
1809135446Strhodes		return;
1810135446Strhodes	}
1811135446Strhodes
1812135446Strhodes	if (fgets(batchline, sizeof(batchline), batchfp) != 0) {
1813135446Strhodes		debug("batch line %s", batchline);
1814135446Strhodes		bargc = 1;
1815135446Strhodes		input = batchline;
1816135446Strhodes		bargv[bargc] = next_token(&input, " \t\r\n");
1817135446Strhodes		while ((bargv[bargc] != NULL) && (bargc < 14)) {
1818135446Strhodes			bargc++;
1819135446Strhodes			bargv[bargc] = next_token(&input, " \t\r\n");
1820135446Strhodes		}
1821135446Strhodes
1822135446Strhodes		bargv[0] = argv0;
1823135446Strhodes
1824174187Sdougb		for(i = 0; i < bargc; i++)
1825174187Sdougb			debug("batch argv %d: %s", i, bargv[i]);
1826135446Strhodes		parse_args(ISC_TRUE, ISC_FALSE, bargc, (char **)bargv);
1827135446Strhodes		start_lookup();
1828135446Strhodes	} else {
1829135446Strhodes		batchname = NULL;
1830135446Strhodes		if (batchfp != stdin)
1831135446Strhodes			fclose(batchfp);
1832135446Strhodes		isc_app_shutdown();
1833135446Strhodes		return;
1834135446Strhodes	}
1835135446Strhodes}
1836135446Strhodes
1837170222Sdougb/*% Main processing routine for dig */
1838135446Strhodesint
1839135446Strhodesmain(int argc, char **argv) {
1840135446Strhodes	isc_result_t result;
1841135446Strhodes
1842135446Strhodes	ISC_LIST_INIT(lookup_list);
1843135446Strhodes	ISC_LIST_INIT(server_list);
1844135446Strhodes	ISC_LIST_INIT(search_list);
1845135446Strhodes
1846135446Strhodes	debug("main()");
1847135446Strhodes	preparse_args(argc, argv);
1848135446Strhodes	progname = argv[0];
1849135446Strhodes	result = isc_app_start();
1850135446Strhodes	check_result(result, "isc_app_start");
1851135446Strhodes	setup_libs();
1852135446Strhodes	parse_args(ISC_FALSE, ISC_FALSE, argc, argv);
1853135446Strhodes	setup_system();
1854135446Strhodes	if (domainopt[0] != '\0') {
1855135446Strhodes		set_search_domain(domainopt);
1856135446Strhodes		usesearch = ISC_TRUE;
1857135446Strhodes	}
1858135446Strhodes	result = isc_app_onrun(mctx, global_task, onrun_callback, NULL);
1859135446Strhodes	check_result(result, "isc_app_onrun");
1860135446Strhodes	isc_app_run();
1861174187Sdougb	destroy_lookup(default_lookup);
1862135446Strhodes	if (batchname != NULL) {
1863135446Strhodes		if (batchfp != stdin)
1864135446Strhodes			fclose(batchfp);
1865135446Strhodes		batchname = NULL;
1866135446Strhodes	}
1867135446Strhodes#ifdef DIG_SIGCHASE
1868135446Strhodes	clean_trustedkey();
1869135446Strhodes#endif
1870135446Strhodes	cancel_all();
1871135446Strhodes	destroy_libs();
1872135446Strhodes	isc_app_finish();
1873135446Strhodes	return (exitcode);
1874135446Strhodes}
1875