dig.c revision 165071
1/*
2 * Copyright (C) 2004-2006  Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2000-2003  Internet Software Consortium.
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
16 */
17
18/* $Id: dig.c,v 1.157.2.13.2.31 2006/07/22 23:52:57 marka Exp $ */
19
20#include <config.h>
21#include <stdlib.h>
22#include <time.h>
23#include <ctype.h>
24
25#include <isc/app.h>
26#include <isc/netaddr.h>
27#include <isc/parseint.h>
28#include <isc/print.h>
29#include <isc/string.h>
30#include <isc/util.h>
31#include <isc/task.h>
32
33#include <dns/byaddr.h>
34#include <dns/fixedname.h>
35#include <dns/masterdump.h>
36#include <dns/message.h>
37#include <dns/name.h>
38#include <dns/rdata.h>
39#include <dns/rdataset.h>
40#include <dns/rdatatype.h>
41#include <dns/rdataclass.h>
42#include <dns/result.h>
43
44#include <bind9/getaddresses.h>
45
46#include <dig/dig.h>
47
48#define ADD_STRING(b, s) { 				\
49	if (strlen(s) >= isc_buffer_availablelength(b)) \
50 		return (ISC_R_NOSPACE); 		\
51	else 						\
52		isc_buffer_putstr(b, s); 		\
53}
54
55#define DIG_MAX_ADDRESSES 20
56
57dig_lookup_t *default_lookup = NULL;
58
59static char *batchname = NULL;
60static FILE *batchfp = NULL;
61static char *argv0;
62static int addresscount = 0;
63
64static char domainopt[DNS_NAME_MAXTEXT];
65
66static isc_boolean_t short_form = ISC_FALSE, printcmd = ISC_TRUE,
67	ip6_int = ISC_FALSE, plusquest = ISC_FALSE, pluscomm = ISC_FALSE,
68	multiline = ISC_FALSE, nottl = ISC_FALSE, noclass = ISC_FALSE;
69
70static const char *opcodetext[] = {
71	"QUERY",
72	"IQUERY",
73	"STATUS",
74	"RESERVED3",
75	"NOTIFY",
76	"UPDATE",
77	"RESERVED6",
78	"RESERVED7",
79	"RESERVED8",
80	"RESERVED9",
81	"RESERVED10",
82	"RESERVED11",
83	"RESERVED12",
84	"RESERVED13",
85	"RESERVED14",
86	"RESERVED15"
87};
88
89static const char *rcodetext[] = {
90	"NOERROR",
91	"FORMERR",
92	"SERVFAIL",
93	"NXDOMAIN",
94	"NOTIMP",
95	"REFUSED",
96	"YXDOMAIN",
97	"YXRRSET",
98	"NXRRSET",
99	"NOTAUTH",
100	"NOTZONE",
101	"RESERVED11",
102	"RESERVED12",
103	"RESERVED13",
104	"RESERVED14",
105	"RESERVED15",
106	"BADVERS"
107};
108
109static void
110print_usage(FILE *fp) {
111	fputs(
112"Usage:  dig [@global-server] [domain] [q-type] [q-class] {q-opt}\n"
113"            {global-d-opt} host [@local-server] {local-d-opt}\n"
114"            [ host [@local-server] {local-d-opt} [...]]\n", fp);
115}
116
117static void
118usage(void) {
119	print_usage(stderr);
120	fputs("\nUse \"dig -h\" (or \"dig -h | more\") "
121	      "for complete list of options\n", stderr);
122	exit(1);
123}
124
125static void
126version(void) {
127	fputs("DiG " VERSION "\n", stderr);
128}
129
130static void
131help(void) {
132	print_usage(stdout);
133	fputs(
134"Where:  domain	  is in the Domain Name System\n"
135"        q-class  is one of (in,hs,ch,...) [default: in]\n"
136"        q-type   is one of (a,any,mx,ns,soa,hinfo,axfr,txt,...) [default:a]\n"
137"                 (Use ixfr=version for type ixfr)\n"
138"        q-opt    is one of:\n"
139"                 -x dot-notation     (shortcut for in-addr lookups)\n"
140"                 -i                  (IP6.INT reverse IPv6 lookups)\n"
141"                 -f filename         (batch mode)\n"
142"                 -b address[#port]   (bind to source address/port)\n"
143"                 -p port             (specify port number)\n"
144"                 -t type             (specify query type)\n"
145"                 -c class            (specify query class)\n"
146"                 -k keyfile          (specify tsig key file)\n"
147"                 -y name:key         (specify named base64 tsig key)\n"
148"                 -4                  (use IPv4 query transport only)\n"
149"                 -6                  (use IPv6 query transport only)\n"
150"        d-opt    is of the form +keyword[=value], where keyword is:\n"
151"                 +[no]vc             (TCP mode)\n"
152"                 +[no]tcp            (TCP mode, alternate syntax)\n"
153"                 +time=###           (Set query timeout) [5]\n"
154"                 +tries=###          (Set number of UDP attempts) [3]\n"
155"                 +retry=###          (Set number of UDP retries) [2]\n"
156"                 +domain=###         (Set default domainname)\n"
157"                 +bufsize=###        (Set EDNS0 Max UDP packet size)\n"
158"                 +ndots=###          (Set NDOTS value)\n"
159"                 +[no]search         (Set whether to use searchlist)\n"
160"                 +[no]defname        (Ditto)\n"
161"                 +[no]recurse        (Recursive mode)\n"
162"                 +[no]ignore         (Don't revert to TCP for TC responses.)"
163"\n"
164"                 +[no]fail           (Don't try next server on SERVFAIL)\n"
165"                 +[no]besteffort     (Try to parse even illegal messages)\n"
166"                 +[no]aaonly         (Set AA flag in query (+[no]aaflag))\n"
167"                 +[no]adflag         (Set AD flag in query)\n"
168"                 +[no]cdflag         (Set CD flag in query)\n"
169"                 +[no]cl             (Control display of class in records)\n"
170"                 +[no]cmd            (Control display of command line)\n"
171"                 +[no]comments       (Control display of comment lines)\n"
172"                 +[no]question       (Control display of question)\n"
173"                 +[no]answer         (Control display of answer)\n"
174"                 +[no]authority      (Control display of authority)\n"
175"                 +[no]additional     (Control display of additional)\n"
176"                 +[no]stats          (Control display of statistics)\n"
177"                 +[no]short          (Disable everything except short\n"
178"                                      form of answer)\n"
179"                 +[no]ttlid          (Control display of ttls in records)\n"
180"                 +[no]all            (Set or clear all display flags)\n"
181"                 +[no]qr             (Print question before sending)\n"
182"                 +[no]nssearch       (Search all authoritative nameservers)\n"
183"                 +[no]identify       (ID responders in short answers)\n"
184"                 +[no]trace          (Trace delegation down from root)\n"
185"                 +[no]dnssec         (Request DNSSEC records)\n"
186#ifdef DIG_SIGCHASE
187"                 +[no]sigchase       (Chase DNSSEC signatures)\n"
188"                 +trusted-key=####   (Trusted Key when chasing DNSSEC sigs)\n"
189#if DIG_SIGCHASE_TD
190"                 +[no]topdown        (Do DNSSEC validation top down mode)\n"
191#endif
192#endif
193"                 +[no]multiline      (Print records in an expanded format)\n"
194"        global d-opts and servers (before host name) affect all queries.\n"
195"        local d-opts and servers (after host name) affect only that lookup.\n"
196"        -h                           (print help and exit)\n"
197"        -v                           (print version and exit)\n",
198	stdout);
199}
200
201/*
202 * Callback from dighost.c to print the received message.
203 */
204void
205received(int bytes, isc_sockaddr_t *from, dig_query_t *query) {
206	isc_uint64_t diff;
207	isc_time_t now;
208	time_t tnow;
209	char fromtext[ISC_SOCKADDR_FORMATSIZE];
210
211	isc_sockaddr_format(from, fromtext, sizeof(fromtext));
212
213	TIME_NOW(&now);
214
215	if (query->lookup->stats && !short_form) {
216		diff = isc_time_microdiff(&now, &query->time_sent);
217		printf(";; Query time: %ld msec\n", (long int)diff/1000);
218		printf(";; SERVER: %s(%s)\n", fromtext, query->servname);
219		time(&tnow);
220		printf(";; WHEN: %s", ctime(&tnow));
221		if (query->lookup->doing_xfr) {
222			printf(";; XFR size: %u records (messages %u)\n",
223			       query->rr_count, query->msg_count);
224		} else {
225			printf(";; MSG SIZE  rcvd: %d\n", bytes);
226
227		}
228		if (key != NULL) {
229			if (!validated)
230				puts(";; WARNING -- Some TSIG could not "
231				     "be validated");
232		}
233		if ((key == NULL) && (keysecret[0] != 0)) {
234			puts(";; WARNING -- TSIG key was not used.");
235		}
236		puts("");
237	} else if (query->lookup->identify && !short_form) {
238		diff = isc_time_microdiff(&now, &query->time_sent);
239		printf(";; Received %u bytes from %s(%s) in %d ms\n\n",
240		       bytes, fromtext, query->servname,
241		       (int)diff/1000);
242	}
243}
244
245/*
246 * Callback from dighost.c to print that it is trying a server.
247 * Not used in dig.
248 * XXX print_trying
249 */
250void
251trying(char *frm, dig_lookup_t *lookup) {
252	UNUSED(frm);
253	UNUSED(lookup);
254}
255
256/*
257 * Internal print routine used to print short form replies.
258 */
259static isc_result_t
260say_message(dns_rdata_t *rdata, dig_query_t *query, isc_buffer_t *buf) {
261	isc_result_t result;
262	isc_uint64_t diff;
263	isc_time_t now;
264	char store[sizeof("12345678901234567890")];
265
266	if (query->lookup->trace || query->lookup->ns_search_only) {
267		result = dns_rdatatype_totext(rdata->type, buf);
268		if (result != ISC_R_SUCCESS)
269			return (result);
270		ADD_STRING(buf, " ");
271	}
272	result = dns_rdata_totext(rdata, NULL, buf);
273	check_result(result, "dns_rdata_totext");
274	if (query->lookup->identify) {
275		TIME_NOW(&now);
276		diff = isc_time_microdiff(&now, &query->time_sent);
277		ADD_STRING(buf, " from server ");
278		ADD_STRING(buf, query->servname);
279		snprintf(store, 19, " in %d ms.", (int)diff/1000);
280		ADD_STRING(buf, store);
281	}
282	ADD_STRING(buf, "\n");
283	return (ISC_R_SUCCESS);
284}
285
286/*
287 * short_form message print handler.  Calls above say_message()
288 */
289static isc_result_t
290short_answer(dns_message_t *msg, dns_messagetextflag_t flags,
291	     isc_buffer_t *buf, dig_query_t *query)
292{
293	dns_name_t *name;
294	dns_rdataset_t *rdataset;
295	isc_buffer_t target;
296	isc_result_t result, loopresult;
297	dns_name_t empty_name;
298	char t[4096];
299	dns_rdata_t rdata = DNS_RDATA_INIT;
300
301	UNUSED(flags);
302
303	dns_name_init(&empty_name, NULL);
304	result = dns_message_firstname(msg, DNS_SECTION_ANSWER);
305	if (result == ISC_R_NOMORE)
306		return (ISC_R_SUCCESS);
307	else if (result != ISC_R_SUCCESS)
308		return (result);
309
310	for (;;) {
311		name = NULL;
312		dns_message_currentname(msg, DNS_SECTION_ANSWER, &name);
313
314		isc_buffer_init(&target, t, sizeof(t));
315
316		for (rdataset = ISC_LIST_HEAD(name->list);
317		     rdataset != NULL;
318		     rdataset = ISC_LIST_NEXT(rdataset, link)) {
319			loopresult = dns_rdataset_first(rdataset);
320			while (loopresult == ISC_R_SUCCESS) {
321				dns_rdataset_current(rdataset, &rdata);
322				result = say_message(&rdata, query,
323						     buf);
324				check_result(result, "say_message");
325				loopresult = dns_rdataset_next(rdataset);
326				dns_rdata_reset(&rdata);
327			}
328		}
329		result = dns_message_nextname(msg, DNS_SECTION_ANSWER);
330		if (result == ISC_R_NOMORE)
331			break;
332		else if (result != ISC_R_SUCCESS)
333			return (result);
334	}
335
336	return (ISC_R_SUCCESS);
337}
338#ifdef DIG_SIGCHASE
339isc_result_t
340printrdataset(dns_name_t *owner_name, dns_rdataset_t *rdataset,
341	      isc_buffer_t *target)
342{
343	isc_result_t result;
344	dns_master_style_t *style = NULL;
345	unsigned int styleflags = 0;
346
347	if (rdataset == NULL || owner_name == NULL || target == NULL)
348		return(ISC_FALSE);
349
350	styleflags |= DNS_STYLEFLAG_REL_OWNER;
351	if (nottl)
352		styleflags |= DNS_STYLEFLAG_NO_TTL;
353	if (noclass)
354		styleflags |= DNS_STYLEFLAG_NO_CLASS;
355	if (multiline) {
356		styleflags |= DNS_STYLEFLAG_OMIT_OWNER;
357		styleflags |= DNS_STYLEFLAG_OMIT_CLASS;
358		styleflags |= DNS_STYLEFLAG_REL_DATA;
359		styleflags |= DNS_STYLEFLAG_OMIT_TTL;
360		styleflags |= DNS_STYLEFLAG_TTL;
361		styleflags |= DNS_STYLEFLAG_MULTILINE;
362		styleflags |= DNS_STYLEFLAG_COMMENT;
363	}
364	if (multiline || (nottl && noclass))
365		result = dns_master_stylecreate(&style, styleflags,
366						24, 24, 24, 32, 80, 8, mctx);
367	else if (nottl || noclass)
368		result = dns_master_stylecreate(&style, styleflags,
369						24, 24, 32, 40, 80, 8, mctx);
370	else
371		result = dns_master_stylecreate(&style, styleflags,
372						24, 32, 40, 48, 80, 8, mctx);
373	check_result(result, "dns_master_stylecreate");
374
375	result = dns_master_rdatasettotext(owner_name, rdataset, style, target);
376
377	if (style != NULL)
378		dns_master_styledestroy(&style, mctx);
379
380	return(result);
381}
382#endif
383
384/*
385 * Callback from dighost.c to print the reply from a server
386 */
387isc_result_t
388printmessage(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers) {
389	isc_result_t result;
390	dns_messagetextflag_t flags;
391	isc_buffer_t *buf = NULL;
392	unsigned int len = OUTPUTBUF;
393	dns_master_style_t *style = NULL;
394	unsigned int styleflags = 0;
395
396	styleflags |= DNS_STYLEFLAG_REL_OWNER;
397	if (nottl)
398		styleflags |= DNS_STYLEFLAG_NO_TTL;
399	if (noclass)
400		styleflags |= DNS_STYLEFLAG_NO_CLASS;
401	if (multiline) {
402		styleflags |= DNS_STYLEFLAG_OMIT_OWNER;
403		styleflags |= DNS_STYLEFLAG_OMIT_CLASS;
404		styleflags |= DNS_STYLEFLAG_REL_DATA;
405		styleflags |= DNS_STYLEFLAG_OMIT_TTL;
406		styleflags |= DNS_STYLEFLAG_TTL;
407		styleflags |= DNS_STYLEFLAG_MULTILINE;
408		styleflags |= DNS_STYLEFLAG_COMMENT;
409	}
410	if (multiline || (nottl && noclass))
411		result = dns_master_stylecreate(&style, styleflags,
412						24, 24, 24, 32, 80, 8, mctx);
413	else if (nottl || noclass)
414		result = dns_master_stylecreate(&style, styleflags,
415						24, 24, 32, 40, 80, 8, mctx);
416	else
417		result = dns_master_stylecreate(&style, styleflags,
418						24, 32, 40, 48, 80, 8, mctx);
419	check_result(result, "dns_master_stylecreate");
420
421	if (query->lookup->cmdline[0] != 0) {
422		if (!short_form)
423			fputs(query->lookup->cmdline, stdout);
424		query->lookup->cmdline[0]=0;
425	}
426	debug("printmessage(%s %s %s)", headers ? "headers" : "noheaders",
427	      query->lookup->comments ? "comments" : "nocomments",
428	      short_form ? "short_form" : "long_form");
429
430	flags = 0;
431	if (!headers) {
432		flags |= DNS_MESSAGETEXTFLAG_NOHEADERS;
433		flags |= DNS_MESSAGETEXTFLAG_NOCOMMENTS;
434	}
435	if (!query->lookup->comments)
436		flags |= DNS_MESSAGETEXTFLAG_NOCOMMENTS;
437
438	result = ISC_R_SUCCESS;
439
440	result = isc_buffer_allocate(mctx, &buf, len);
441	check_result(result, "isc_buffer_allocate");
442
443	if (query->lookup->comments && !short_form) {
444		if (query->lookup->cmdline[0] != 0)
445			printf("; %s\n", query->lookup->cmdline);
446		if (msg == query->lookup->sendmsg)
447			printf(";; Sending:\n");
448		else
449			printf(";; Got answer:\n");
450
451		if (headers) {
452			printf(";; ->>HEADER<<- opcode: %s, status: %s, "
453			       "id: %u\n",
454			       opcodetext[msg->opcode], rcodetext[msg->rcode],
455			       msg->id);
456			printf(";; flags:");
457			if ((msg->flags & DNS_MESSAGEFLAG_QR) != 0)
458				printf(" qr");
459			if ((msg->flags & DNS_MESSAGEFLAG_AA) != 0)
460				printf(" aa");
461			if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0)
462				printf(" tc");
463			if ((msg->flags & DNS_MESSAGEFLAG_RD) != 0)
464				printf(" rd");
465			if ((msg->flags & DNS_MESSAGEFLAG_RA) != 0)
466				printf(" ra");
467			if ((msg->flags & DNS_MESSAGEFLAG_AD) != 0)
468				printf(" ad");
469			if ((msg->flags & DNS_MESSAGEFLAG_CD) != 0)
470				printf(" cd");
471
472			printf("; QUERY: %u, ANSWER: %u, "
473			       "AUTHORITY: %u, ADDITIONAL: %u\n",
474			       msg->counts[DNS_SECTION_QUESTION],
475			       msg->counts[DNS_SECTION_ANSWER],
476			       msg->counts[DNS_SECTION_AUTHORITY],
477			       msg->counts[DNS_SECTION_ADDITIONAL]);
478		}
479	}
480
481repopulate_buffer:
482
483	if (query->lookup->comments && headers && !short_form) {
484		result = dns_message_pseudosectiontotext(msg,
485			 DNS_PSEUDOSECTION_OPT,
486			 style, flags, buf);
487		if (result == ISC_R_NOSPACE) {
488buftoosmall:
489			len += OUTPUTBUF;
490			isc_buffer_free(&buf);
491			result = isc_buffer_allocate(mctx, &buf, len);
492			if (result == ISC_R_SUCCESS)
493				goto repopulate_buffer;
494			else
495				goto cleanup;
496		}
497		check_result(result,
498		     "dns_message_pseudosectiontotext");
499	}
500
501	if (query->lookup->section_question && headers) {
502		if (!short_form) {
503			result = dns_message_sectiontotext(msg,
504						       DNS_SECTION_QUESTION,
505						       style, flags, buf);
506			if (result == ISC_R_NOSPACE)
507				goto buftoosmall;
508			check_result(result, "dns_message_sectiontotext");
509		}
510	}
511	if (query->lookup->section_answer) {
512		if (!short_form) {
513			result = dns_message_sectiontotext(msg,
514						       DNS_SECTION_ANSWER,
515						       style, flags, buf);
516			if (result == ISC_R_NOSPACE)
517				goto buftoosmall;
518			check_result(result, "dns_message_sectiontotext");
519		} else {
520			result = short_answer(msg, flags, buf, query);
521			if (result == ISC_R_NOSPACE)
522				goto buftoosmall;
523			check_result(result, "short_answer");
524		}
525	}
526	if (query->lookup->section_authority) {
527		if (!short_form) {
528			result = dns_message_sectiontotext(msg,
529						       DNS_SECTION_AUTHORITY,
530						       style, flags, buf);
531			if (result == ISC_R_NOSPACE)
532				goto buftoosmall;
533			check_result(result, "dns_message_sectiontotext");
534		}
535	}
536	if (query->lookup->section_additional) {
537		if (!short_form) {
538			result = dns_message_sectiontotext(msg,
539						      DNS_SECTION_ADDITIONAL,
540						      style, flags, buf);
541			if (result == ISC_R_NOSPACE)
542				goto buftoosmall;
543			check_result(result, "dns_message_sectiontotext");
544			/*
545			 * Only print the signature on the first record.
546			 */
547			if (headers) {
548				result = dns_message_pseudosectiontotext(
549						   msg,
550						   DNS_PSEUDOSECTION_TSIG,
551						   style, flags, buf);
552				if (result == ISC_R_NOSPACE)
553					goto buftoosmall;
554				check_result(result,
555					  "dns_message_pseudosectiontotext");
556				result = dns_message_pseudosectiontotext(
557						   msg,
558						   DNS_PSEUDOSECTION_SIG0,
559						   style, flags, buf);
560				if (result == ISC_R_NOSPACE)
561					goto buftoosmall;
562				check_result(result,
563					   "dns_message_pseudosectiontotext");
564			}
565		}
566	}
567
568	if (headers && query->lookup->comments && !short_form)
569		printf("\n");
570
571	printf("%.*s", (int)isc_buffer_usedlength(buf),
572	       (char *)isc_buffer_base(buf));
573	isc_buffer_free(&buf);
574
575cleanup:
576	if (style != NULL)
577		dns_master_styledestroy(&style, mctx);
578	return (result);
579}
580
581/*
582 * print the greeting message when the program first starts up.
583 */
584static void
585printgreeting(int argc, char **argv, dig_lookup_t *lookup) {
586	int i;
587	int remaining;
588	static isc_boolean_t first = ISC_TRUE;
589	char append[MXNAME];
590
591	if (printcmd) {
592		lookup->cmdline[sizeof(lookup->cmdline) - 1] = 0;
593		snprintf(lookup->cmdline, sizeof(lookup->cmdline),
594			 "%s; <<>> DiG " VERSION " <<>>",
595			 first?"\n":"");
596		i = 1;
597		while (i < argc) {
598			snprintf(append, sizeof(append), " %s", argv[i++]);
599			remaining = sizeof(lookup->cmdline) -
600				    strlen(lookup->cmdline) - 1;
601			strncat(lookup->cmdline, append, remaining);
602		}
603		remaining = sizeof(lookup->cmdline) -
604			    strlen(lookup->cmdline) - 1;
605		strncat(lookup->cmdline, "\n", remaining);
606		if (first && addresscount != 0) {
607			snprintf(append, sizeof(append),
608				 "; (%d server%s found)\n",
609				 addresscount,
610				 addresscount > 1 ? "s" : "");
611			remaining = sizeof(lookup->cmdline) -
612				    strlen(lookup->cmdline) - 1;
613			strncat(lookup->cmdline, append, remaining);
614		}
615		if (first) {
616			snprintf(append, sizeof(append),
617				 ";; global options: %s %s\n",
618			       short_form ? "short_form" : "",
619			       printcmd ? "printcmd" : "");
620			first = ISC_FALSE;
621			remaining = sizeof(lookup->cmdline) -
622				    strlen(lookup->cmdline) - 1;
623			strncat(lookup->cmdline, append, remaining);
624		}
625	}
626}
627
628/*
629 * Reorder an argument list so that server names all come at the end.
630 * This is a bit of a hack, to allow batch-mode processing to properly
631 * handle the server options.
632 */
633static void
634reorder_args(int argc, char *argv[]) {
635	int i, j;
636	char *ptr;
637	int end;
638
639	debug("reorder_args()");
640	end = argc - 1;
641	while (argv[end][0] == '@') {
642		end--;
643		if (end == 0)
644			return;
645	}
646	debug("arg[end]=%s", argv[end]);
647	for (i = 1; i < end - 1; i++) {
648		if (argv[i][0] == '@') {
649			debug("arg[%d]=%s", i, argv[i]);
650			ptr = argv[i];
651			for (j = i + 1; j < end; j++) {
652				debug("Moving %s to %d", argv[j], j - 1);
653				argv[j - 1] = argv[j];
654			}
655			debug("moving %s to end, %d", ptr, end - 1);
656			argv[end - 1] = ptr;
657			end--;
658			if (end < 1)
659				return;
660		}
661	}
662}
663
664static isc_uint32_t
665parse_uint(char *arg, const char *desc, isc_uint32_t max) {
666	isc_result_t result;
667	isc_uint32_t tmp;
668
669	result = isc_parse_uint32(&tmp, arg, 10);
670	if (result == ISC_R_SUCCESS && tmp > max)
671		result = ISC_R_RANGE;
672	if (result != ISC_R_SUCCESS)
673		fatal("%s '%s': %s", desc, arg, isc_result_totext(result));
674	return (tmp);
675}
676
677/*
678 * We're not using isc_commandline_parse() here since the command line
679 * syntax of dig is quite a bit different from that which can be described
680 * by that routine.
681 * XXX doc options
682 */
683
684static void
685plus_option(char *option, isc_boolean_t is_batchfile,
686	    dig_lookup_t *lookup)
687{
688	char option_store[256];
689	char *cmd, *value, *ptr;
690	isc_boolean_t state = ISC_TRUE;
691#ifdef DIG_SIGCHASE
692	size_t n;
693#endif
694
695	strncpy(option_store, option, sizeof(option_store));
696	option_store[sizeof(option_store)-1]=0;
697	ptr = option_store;
698	cmd = next_token(&ptr,"=");
699	if (cmd == NULL) {
700		printf(";; Invalid option %s\n", option_store);
701		return;
702	}
703	value = ptr;
704	if (strncasecmp(cmd, "no", 2)==0) {
705		cmd += 2;
706		state = ISC_FALSE;
707	}
708
709#define FULLCHECK(A) \
710	do { \
711		size_t _l = strlen(cmd); \
712		if (_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) \
713			goto invalid_option; \
714	} while (0)
715#define FULLCHECK2(A, B) \
716	do { \
717		size_t _l = strlen(cmd); \
718		if ((_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) && \
719		    (_l >= sizeof(B) || strncasecmp(cmd, B, _l) != 0)) \
720			goto invalid_option; \
721	} while (0)
722
723	switch (cmd[0]) {
724	case 'a':
725		switch (cmd[1]) {
726		case 'a': /* aaonly / aaflag */
727			FULLCHECK2("aaonly", "aaflag");
728			lookup->aaonly = state;
729			break;
730		case 'd':
731			switch (cmd[2]) {
732			case 'd': /* additional */
733				FULLCHECK("additional");
734				lookup->section_additional = state;
735				break;
736			case 'f': /* adflag */
737				FULLCHECK("adflag");
738				lookup->adflag = state;
739				break;
740			default:
741				goto invalid_option;
742			}
743			break;
744		case 'l': /* all */
745			FULLCHECK("all");
746			lookup->section_question = state;
747			lookup->section_authority = state;
748			lookup->section_answer = state;
749			lookup->section_additional = state;
750			lookup->comments = state;
751			lookup->stats = state;
752			printcmd = state;
753			break;
754		case 'n': /* answer */
755			FULLCHECK("answer");
756			lookup->section_answer = state;
757			break;
758		case 'u': /* authority */
759			FULLCHECK("authority");
760			lookup->section_authority = state;
761			break;
762		default:
763			goto invalid_option;
764		}
765		break;
766	case 'b':
767		switch (cmd[1]) {
768		case 'e':/* besteffort */
769			FULLCHECK("besteffort");
770			lookup->besteffort = state;
771			break;
772		case 'u':/* bufsize */
773			FULLCHECK("bufsize");
774			if (value == NULL)
775				goto need_value;
776			if (!state)
777				goto invalid_option;
778			lookup->udpsize = (isc_uint16_t) parse_uint(value,
779						    "buffer size", COMMSIZE);
780			break;
781		default:
782			goto invalid_option;
783		}
784		break;
785	case 'c':
786		switch (cmd[1]) {
787		case 'd':/* cdflag */
788			FULLCHECK("cdflag");
789			lookup->cdflag = state;
790			break;
791		case 'l': /* cl */
792			FULLCHECK("cl");
793			noclass = ISC_TF(!state);
794			break;
795		case 'm': /* cmd */
796			FULLCHECK("cmd");
797			printcmd = state;
798			break;
799		case 'o': /* comments */
800			FULLCHECK("comments");
801			lookup->comments = state;
802			if (lookup == default_lookup)
803				pluscomm = state;
804			break;
805		default:
806			goto invalid_option;
807		}
808		break;
809	case 'd':
810		switch (cmd[1]) {
811		case 'e': /* defname */
812			FULLCHECK("defname");
813			usesearch = state;
814			break;
815		case 'n': /* dnssec */
816			FULLCHECK("dnssec");
817			lookup->dnssec = state;
818			break;
819		case 'o': /* domain */
820			FULLCHECK("domain");
821			if (value == NULL)
822				goto need_value;
823			if (!state)
824				goto invalid_option;
825			strncpy(domainopt, value, sizeof(domainopt));
826			domainopt[sizeof(domainopt)-1] = '\0';
827			break;
828		default:
829			goto invalid_option;
830		}
831		break;
832	case 'f': /* fail */
833		FULLCHECK("fail");
834		lookup->servfail_stops = state;
835		break;
836	case 'i':
837		switch (cmd[1]) {
838		case 'd': /* identify */
839			FULLCHECK("identify");
840			lookup->identify = state;
841			break;
842		case 'g': /* ignore */
843		default: /* Inherets default for compatibility */
844			FULLCHECK("ignore");
845			lookup->ignore = ISC_TRUE;
846		}
847		break;
848	case 'm': /* multiline */
849		FULLCHECK("multiline");
850		multiline = state;
851		break;
852	case 'n':
853		switch (cmd[1]) {
854		case 'd': /* ndots */
855			FULLCHECK("ndots");
856			if (value == NULL)
857				goto need_value;
858			if (!state)
859				goto invalid_option;
860			ndots = parse_uint(value, "ndots", MAXNDOTS);
861			break;
862		case 's': /* nssearch */
863			FULLCHECK("nssearch");
864			lookup->ns_search_only = state;
865			if (state) {
866				lookup->trace_root = ISC_TRUE;
867				lookup->recurse = ISC_TRUE;
868				lookup->identify = ISC_TRUE;
869				lookup->stats = ISC_FALSE;
870				lookup->comments = ISC_FALSE;
871				lookup->section_additional = ISC_FALSE;
872				lookup->section_authority = ISC_FALSE;
873				lookup->section_question = ISC_FALSE;
874				lookup->rdtype = dns_rdatatype_ns;
875				lookup->rdtypeset = ISC_TRUE;
876				short_form = ISC_TRUE;
877			}
878			break;
879		default:
880			goto invalid_option;
881		}
882		break;
883	case 'q':
884		switch (cmd[1]) {
885		case 'r': /* qr */
886			FULLCHECK("qr");
887			qr = state;
888			break;
889		case 'u': /* question */
890			FULLCHECK("question");
891			lookup->section_question = state;
892			if (lookup == default_lookup)
893				plusquest = state;
894			break;
895		default:
896			goto invalid_option;
897		}
898		break;
899	case 'r':
900		switch (cmd[1]) {
901		case 'e':
902			switch (cmd[2]) {
903			case 'c': /* recurse */
904				FULLCHECK("recurse");
905				lookup->recurse = state;
906				break;
907			case 't': /* retry / retries */
908				FULLCHECK2("retry", "retries");
909				if (value == NULL)
910					goto need_value;
911				if (!state)
912					goto invalid_option;
913				lookup->retries = parse_uint(value, "retries",
914						       MAXTRIES - 1);
915				lookup->retries++;
916				break;
917			default:
918				goto invalid_option;
919			}
920			break;
921		default:
922			goto invalid_option;
923		}
924		break;
925	case 's':
926		switch (cmd[1]) {
927		case 'e': /* search */
928			FULLCHECK("search");
929			usesearch = state;
930			break;
931		case 'h': /* short */
932			FULLCHECK("short");
933			short_form = state;
934			if (state) {
935				printcmd = ISC_FALSE;
936				lookup->section_additional = ISC_FALSE;
937				lookup->section_answer = ISC_TRUE;
938				lookup->section_authority = ISC_FALSE;
939				lookup->section_question = ISC_FALSE;
940				lookup->comments = ISC_FALSE;
941				lookup->stats = ISC_FALSE;
942			}
943			break;
944#ifdef DIG_SIGCHASE
945		case 'i': /* sigchase */
946		        FULLCHECK("sigchase");
947			lookup->sigchase = state;
948			if (lookup->sigchase)
949				lookup->dnssec = ISC_TRUE;
950			break;
951#endif
952		case 't': /* stats */
953			FULLCHECK("stats");
954			lookup->stats = state;
955			break;
956		default:
957			goto invalid_option;
958		}
959		break;
960	case 't':
961		switch (cmd[1]) {
962		case 'c': /* tcp */
963			FULLCHECK("tcp");
964			if (!is_batchfile)
965				lookup->tcp_mode = state;
966			break;
967		case 'i': /* timeout */
968			FULLCHECK("timeout");
969			if (value == NULL)
970				goto need_value;
971			if (!state)
972				goto invalid_option;
973			timeout = parse_uint(value, "timeout", MAXTIMEOUT);
974			if (timeout == 0)
975				timeout = 1;
976			break;
977#if DIG_SIGCHASE_TD
978		case 'o': /* topdown */
979			FULLCHECK("topdown");
980			lookup->do_topdown = state;
981			break;
982#endif
983		case 'r':
984			switch (cmd[2]) {
985			case 'a': /* trace */
986				FULLCHECK("trace");
987				lookup->trace = state;
988				lookup->trace_root = state;
989				if (state) {
990					lookup->recurse = ISC_FALSE;
991					lookup->identify = ISC_TRUE;
992					lookup->comments = ISC_FALSE;
993					lookup->stats = ISC_FALSE;
994					lookup->section_additional = ISC_FALSE;
995					lookup->section_authority = ISC_TRUE;
996					lookup->section_question = ISC_FALSE;
997				}
998				break;
999			case 'i': /* tries */
1000				FULLCHECK("tries");
1001				if (value == NULL)
1002					goto need_value;
1003				if (!state)
1004					goto invalid_option;
1005				lookup->retries = parse_uint(value, "tries",
1006							     MAXTRIES);
1007				if (lookup->retries == 0)
1008					lookup->retries = 1;
1009				break;
1010#ifdef DIG_SIGCHASE
1011			case 'u': /* trusted-key */
1012				FULLCHECK("trusted-key");
1013			  	if (value == NULL)
1014					goto need_value;
1015				if (!state)
1016					goto invalid_option;
1017				n = strlcpy(trustedkey, ptr,
1018					    sizeof(trustedkey));
1019				if (n >= sizeof(trustedkey))
1020					fatal("trusted key too large");
1021				break;
1022#endif
1023			default:
1024				goto invalid_option;
1025			}
1026			break;
1027		case 't': /* ttlid */
1028			FULLCHECK("ttlid");
1029			nottl = ISC_TF(!state);
1030			break;
1031		default:
1032			goto invalid_option;
1033		}
1034		break;
1035	case 'v':
1036		FULLCHECK("vc");
1037		if (!is_batchfile)
1038			lookup->tcp_mode = state;
1039		break;
1040	default:
1041	invalid_option:
1042	need_value:
1043		fprintf(stderr, "Invalid option: +%s\n",
1044			 option);
1045		usage();
1046	}
1047	return;
1048}
1049
1050/*
1051 * ISC_TRUE returned if value was used
1052 */
1053static const char *single_dash_opts = "46dhimnv";
1054static const char *dash_opts = "46bcdfhikmnptvyx";
1055static isc_boolean_t
1056dash_option(char *option, char *next, dig_lookup_t **lookup,
1057	    isc_boolean_t *open_type_class)
1058{
1059	char opt, *value, *ptr;
1060	isc_result_t result;
1061	isc_boolean_t value_from_next;
1062	isc_textregion_t tr;
1063	dns_rdatatype_t rdtype;
1064	dns_rdataclass_t rdclass;
1065	char textname[MXNAME];
1066	struct in_addr in4;
1067	struct in6_addr in6;
1068	in_port_t srcport;
1069	char *hash, *cmd;
1070
1071	while (strpbrk(option, single_dash_opts) == &option[0]) {
1072		/*
1073		 * Since the -[46dhimnv] options do not take an argument,
1074		 * account for them (in any number and/or combination)
1075		 * if they appear as the first character(s) of a q-opt.
1076		 */
1077		opt = option[0];
1078		switch (opt) {
1079		case '4':
1080			if (have_ipv4) {
1081				isc_net_disableipv6();
1082				have_ipv6 = ISC_FALSE;
1083			} else {
1084				fatal("can't find IPv4 networking");
1085				return (ISC_FALSE);
1086			}
1087			break;
1088		case '6':
1089			if (have_ipv6) {
1090				isc_net_disableipv4();
1091				have_ipv4 = ISC_FALSE;
1092			} else {
1093				fatal("can't find IPv6 networking");
1094				return (ISC_FALSE);
1095			}
1096			break;
1097		case 'd':
1098			ptr = strpbrk(&option[1], dash_opts);
1099			if (ptr != &option[1]) {
1100				cmd = option;
1101				FULLCHECK("debug");
1102				debugging = ISC_TRUE;
1103				return (ISC_FALSE);
1104			} else
1105				debugging = ISC_TRUE;
1106			break;
1107		case 'h':
1108			help();
1109			exit(0);
1110			break;
1111		case 'i':
1112			ip6_int = ISC_TRUE;
1113			break;
1114		case 'm': /* memdebug */
1115			/* memdebug is handled in preparse_args() */
1116			break;
1117		case 'n':
1118			/* deprecated */
1119			break;
1120		case 'v':
1121			version();
1122			exit(0);
1123			break;
1124		}
1125		if (strlen(option) > 1U)
1126			option = &option[1];
1127		else
1128			return (ISC_FALSE);
1129	}
1130	opt = option[0];
1131	if (strlen(option) > 1U) {
1132		value_from_next = ISC_FALSE;
1133		value = &option[1];
1134	} else {
1135		value_from_next = ISC_TRUE;
1136		value = next;
1137	}
1138	if (value == NULL)
1139		goto invalid_option;
1140	switch (opt) {
1141	case 'b':
1142		hash = strchr(value, '#');
1143		if (hash != NULL) {
1144			srcport = (in_port_t)
1145			  	parse_uint(hash + 1,
1146					   "port number", MAXPORT);
1147			*hash = '\0';
1148		} else
1149			srcport = 0;
1150		if (have_ipv6 && inet_pton(AF_INET6, value, &in6) == 1) {
1151			isc_sockaddr_fromin6(&bind_address, &in6, srcport);
1152			isc_net_disableipv4();
1153		} else if (have_ipv4 && inet_pton(AF_INET, value, &in4) == 1) {
1154			isc_sockaddr_fromin(&bind_address, &in4, srcport);
1155			isc_net_disableipv6();
1156		} else {
1157			if (hash != NULL)
1158				*hash = '#';
1159			fatal("invalid address %s", value);
1160		}
1161		if (hash != NULL)
1162			*hash = '#';
1163		specified_source = ISC_TRUE;
1164		return (value_from_next);
1165	case 'c':
1166		if ((*lookup)->rdclassset) {
1167			fprintf(stderr, ";; Warning, extra class option\n");
1168		}
1169		*open_type_class = ISC_FALSE;
1170		tr.base = value;
1171		tr.length = strlen(value);
1172		result = dns_rdataclass_fromtext(&rdclass,
1173						 (isc_textregion_t *)&tr);
1174		if (result == ISC_R_SUCCESS) {
1175			(*lookup)->rdclass = rdclass;
1176			(*lookup)->rdclassset = ISC_TRUE;
1177		} else
1178			fprintf(stderr, ";; Warning, ignoring "
1179				"invalid class %s\n",
1180				value);
1181		return (value_from_next);
1182	case 'f':
1183		batchname = value;
1184		return (value_from_next);
1185	case 'k':
1186		strncpy(keyfile, value, sizeof(keyfile));
1187		keyfile[sizeof(keyfile)-1]=0;
1188		return (value_from_next);
1189	case 'p':
1190		port = (in_port_t) parse_uint(value, "port number", MAXPORT);
1191		return (value_from_next);
1192	case 't':
1193		*open_type_class = ISC_FALSE;
1194		if (strncasecmp(value, "ixfr=", 5) == 0) {
1195			rdtype = dns_rdatatype_ixfr;
1196			result = ISC_R_SUCCESS;
1197		} else {
1198			tr.base = value;
1199			tr.length = strlen(value);
1200			result = dns_rdatatype_fromtext(&rdtype,
1201						(isc_textregion_t *)&tr);
1202			if (result == ISC_R_SUCCESS &&
1203			    rdtype == dns_rdatatype_ixfr) {
1204				result = DNS_R_UNKNOWN;
1205			}
1206		}
1207		if (result == ISC_R_SUCCESS) {
1208			if ((*lookup)->rdtypeset) {
1209				fprintf(stderr, ";; Warning, "
1210						"extra type option\n");
1211			}
1212			if (rdtype == dns_rdatatype_ixfr) {
1213				(*lookup)->rdtype = dns_rdatatype_ixfr;
1214				(*lookup)->rdtypeset = ISC_TRUE;
1215				(*lookup)->ixfr_serial =
1216					parse_uint(&value[5], "serial number",
1217					  	MAXSERIAL);
1218				(*lookup)->section_question = plusquest;
1219				(*lookup)->comments = pluscomm;
1220			} else {
1221				(*lookup)->rdtype = rdtype;
1222				(*lookup)->rdtypeset = ISC_TRUE;
1223				if (rdtype == dns_rdatatype_axfr) {
1224					(*lookup)->section_question = plusquest;
1225					(*lookup)->comments = pluscomm;
1226				}
1227				(*lookup)->ixfr_serial = ISC_FALSE;
1228			}
1229		} else
1230			fprintf(stderr, ";; Warning, ignoring "
1231				 "invalid type %s\n",
1232				 value);
1233		return (value_from_next);
1234	case 'y':
1235		ptr = next_token(&value,":");
1236		if (ptr == NULL) {
1237			usage();
1238		}
1239		strncpy(keynametext, ptr, sizeof(keynametext));
1240		keynametext[sizeof(keynametext)-1]=0;
1241		ptr = next_token(&value, "");
1242		if (ptr == NULL)
1243			usage();
1244		strncpy(keysecret, ptr, sizeof(keysecret));
1245		keysecret[sizeof(keysecret)-1]=0;
1246		return (value_from_next);
1247	case 'x':
1248		*lookup = clone_lookup(default_lookup, ISC_TRUE);
1249		if (get_reverse(textname, sizeof(textname), value,
1250				ip6_int, ISC_FALSE) == ISC_R_SUCCESS) {
1251			strncpy((*lookup)->textname, textname,
1252				sizeof((*lookup)->textname));
1253			debug("looking up %s", (*lookup)->textname);
1254			(*lookup)->trace_root = ISC_TF((*lookup)->trace  ||
1255						(*lookup)->ns_search_only);
1256			(*lookup)->ip6_int = ip6_int;
1257			if (!(*lookup)->rdtypeset)
1258				(*lookup)->rdtype = dns_rdatatype_ptr;
1259			if (!(*lookup)->rdclassset)
1260				(*lookup)->rdclass = dns_rdataclass_in;
1261			(*lookup)->new_search = ISC_TRUE;
1262			ISC_LIST_APPEND(lookup_list, *lookup, link);
1263		} else {
1264			fprintf(stderr, "Invalid IP address %s\n", value);
1265			exit(1);
1266		}
1267		return (value_from_next);
1268	invalid_option:
1269	default:
1270		fprintf(stderr, "Invalid option: -%s\n", option);
1271		usage();
1272	}
1273	return (ISC_FALSE);
1274}
1275
1276/*
1277 * Because we may be trying to do memory allocation recording, we're going
1278 * to need to parse the arguments for the -m *before* we start the main
1279 * argument parsing routine.
1280 * I'd prefer not to have to do this, but I am not quite sure how else to
1281 * fix the problem.  Argument parsing in dig involves memory allocation
1282 * by its nature, so it can't be done in the main argument parser.
1283 */
1284static void
1285preparse_args(int argc, char **argv) {
1286	int rc;
1287	char **rv;
1288	char *option;
1289
1290	rc = argc;
1291	rv = argv;
1292	for (rc--, rv++; rc > 0; rc--, rv++) {
1293		if (rv[0][0] != '-')
1294			continue;
1295		option = &rv[0][1];
1296		while (strpbrk(option, single_dash_opts) == &option[0]) {
1297			if (option[0] == 'm') {
1298				memdebugging = ISC_TRUE;
1299				isc_mem_debugging = ISC_MEM_DEBUGTRACE |
1300					ISC_MEM_DEBUGRECORD;
1301				return;
1302			}
1303			option = &option[1];
1304		}
1305	}
1306}
1307
1308static void
1309getaddresses(dig_lookup_t *lookup, const char *host) {
1310	isc_result_t result;
1311	isc_sockaddr_t sockaddrs[DIG_MAX_ADDRESSES];
1312	isc_netaddr_t netaddr;
1313	int count, i;
1314	dig_server_t *srv;
1315	char tmp[ISC_NETADDR_FORMATSIZE];
1316
1317	result = bind9_getaddresses(host, 0, sockaddrs,
1318				    DIG_MAX_ADDRESSES, &count);
1319	if (result != ISC_R_SUCCESS)
1320	fatal("couldn't get address for '%s': %s",
1321	      host, isc_result_totext(result));
1322
1323	for (i = 0; i < count; i++) {
1324		isc_netaddr_fromsockaddr(&netaddr, &sockaddrs[i]);
1325		isc_netaddr_format(&netaddr, tmp, sizeof(tmp));
1326		srv = make_server(tmp, host);
1327		ISC_LIST_APPEND(lookup->my_server_list, srv, link);
1328	}
1329	addresscount = count;
1330}
1331
1332static void
1333parse_args(isc_boolean_t is_batchfile, isc_boolean_t config_only,
1334	   int argc, char **argv) {
1335	isc_result_t result;
1336	isc_textregion_t tr;
1337	isc_boolean_t firstarg = ISC_TRUE;
1338	dig_lookup_t *lookup = NULL;
1339	dns_rdatatype_t rdtype;
1340	dns_rdataclass_t rdclass;
1341	isc_boolean_t open_type_class = ISC_TRUE;
1342	char batchline[MXNAME];
1343	int bargc;
1344	char *bargv[64];
1345	int rc;
1346	char **rv;
1347#ifndef NOPOSIX
1348	char *homedir;
1349	char rcfile[256];
1350#endif
1351	char *input;
1352
1353	/*
1354	 * The semantics for parsing the args is a bit complex; if
1355	 * we don't have a host yet, make the arg apply globally,
1356	 * otherwise make it apply to the latest host.  This is
1357	 * a bit different than the previous versions, but should
1358	 * form a consistent user interface.
1359	 *
1360	 * First, create a "default lookup" which won't actually be used
1361	 * anywhere, except for cloning into new lookups
1362	 */
1363
1364	debug("parse_args()");
1365	if (!is_batchfile) {
1366		debug("making new lookup");
1367		default_lookup = make_empty_lookup();
1368
1369#ifndef NOPOSIX
1370		/*
1371		 * Treat ${HOME}/.digrc as a special batchfile
1372		 */
1373		INSIST(batchfp == NULL);
1374		homedir = getenv("HOME");
1375		if (homedir != NULL) {
1376			unsigned int n;
1377			n = snprintf(rcfile, sizeof(rcfile), "%s/.digrc",
1378			             homedir);
1379			if (n < sizeof(rcfile))
1380				batchfp = fopen(rcfile, "r");
1381		}
1382		if (batchfp != NULL) {
1383			while (fgets(batchline, sizeof(batchline),
1384				     batchfp) != 0) {
1385				debug("config line %s", batchline);
1386				bargc = 1;
1387				input = batchline;
1388				bargv[bargc] = next_token(&input, " \t\r\n");
1389				while ((bargv[bargc] != NULL) &&
1390				       (bargc < 62)) {
1391					bargc++;
1392					bargv[bargc] =
1393						next_token(&input, " \t\r\n");
1394				}
1395
1396				bargv[0] = argv[0];
1397				argv0 = argv[0];
1398
1399				reorder_args(bargc, (char **)bargv);
1400				parse_args(ISC_TRUE, ISC_TRUE, bargc,
1401					   (char **)bargv);
1402			}
1403			fclose(batchfp);
1404		}
1405#endif
1406	}
1407
1408	lookup = default_lookup;
1409
1410	rc = argc;
1411	rv = argv;
1412	for (rc--, rv++; rc > 0; rc--, rv++) {
1413		debug("main parsing %s", rv[0]);
1414		if (strncmp(rv[0], "%", 1) == 0)
1415			break;
1416		if (strncmp(rv[0], "@", 1) == 0) {
1417			getaddresses(lookup, &rv[0][1]);
1418		} else if (rv[0][0] == '+') {
1419			plus_option(&rv[0][1], is_batchfile,
1420				    lookup);
1421		} else if (rv[0][0] == '-') {
1422			if (rc <= 1) {
1423				if (dash_option(&rv[0][1], NULL,
1424						&lookup, &open_type_class)) {
1425					rc--;
1426					rv++;
1427				}
1428			} else {
1429				if (dash_option(&rv[0][1], rv[1],
1430						&lookup, &open_type_class)) {
1431					rc--;
1432					rv++;
1433				}
1434			}
1435		} else {
1436			/*
1437			 * Anything which isn't an option
1438			 */
1439			if (open_type_class) {
1440				if (strncasecmp(rv[0], "ixfr=", 5) == 0) {
1441					rdtype = dns_rdatatype_ixfr;
1442					result = ISC_R_SUCCESS;
1443				} else {
1444					tr.base = rv[0];
1445					tr.length = strlen(rv[0]);
1446					result = dns_rdatatype_fromtext(&rdtype,
1447					     	(isc_textregion_t *)&tr);
1448					if (result == ISC_R_SUCCESS &&
1449					    rdtype == dns_rdatatype_ixfr) {
1450						result = DNS_R_UNKNOWN;
1451						fprintf(stderr, ";; Warning, "
1452							"ixfr requires a "
1453							"serial number\n");
1454						continue;
1455					}
1456				}
1457				if (result == ISC_R_SUCCESS) {
1458					if (lookup->rdtypeset) {
1459						fprintf(stderr, ";; Warning, "
1460							"extra type option\n");
1461					}
1462					if (rdtype == dns_rdatatype_ixfr) {
1463						lookup->rdtype =
1464							dns_rdatatype_ixfr;
1465						lookup->rdtypeset = ISC_TRUE;
1466						lookup->ixfr_serial =
1467							parse_uint(&rv[0][5],
1468							  	"serial number",
1469							  	MAXSERIAL);
1470						lookup->section_question =
1471							plusquest;
1472						lookup->comments = pluscomm;
1473					} else {
1474						lookup->rdtype = rdtype;
1475						lookup->rdtypeset = ISC_TRUE;
1476						if (rdtype ==
1477						    dns_rdatatype_axfr) {
1478						    lookup->section_question =
1479								plusquest;
1480						    lookup->comments = pluscomm;
1481						}
1482						lookup->ixfr_serial = ISC_FALSE;
1483					}
1484					continue;
1485				}
1486				result = dns_rdataclass_fromtext(&rdclass,
1487						     (isc_textregion_t *)&tr);
1488				if (result == ISC_R_SUCCESS) {
1489					if (lookup->rdclassset) {
1490						fprintf(stderr, ";; Warning, "
1491							"extra class option\n");
1492					}
1493					lookup->rdclass = rdclass;
1494					lookup->rdclassset = ISC_TRUE;
1495					continue;
1496				}
1497			}
1498			if (!config_only) {
1499				lookup = clone_lookup(default_lookup,
1500						      ISC_TRUE);
1501				strncpy(lookup->textname, rv[0],
1502					sizeof(lookup->textname));
1503				lookup->textname[sizeof(lookup->textname)-1]=0;
1504				lookup->trace_root = ISC_TF(lookup->trace  ||
1505						     lookup->ns_search_only);
1506				lookup->new_search = ISC_TRUE;
1507				ISC_LIST_APPEND(lookup_list, lookup, link);
1508				debug("looking up %s", lookup->textname);
1509			}
1510			/* XXX Error message */
1511		}
1512	}
1513	/*
1514	 * If we have a batchfile, seed the lookup list with the
1515	 * first entry, then trust the callback in dighost_shutdown
1516	 * to get the rest
1517	 */
1518	if ((batchname != NULL) && !(is_batchfile)) {
1519		if (strcmp(batchname, "-") == 0)
1520			batchfp = stdin;
1521		else
1522			batchfp = fopen(batchname, "r");
1523		if (batchfp == NULL) {
1524			perror(batchname);
1525			if (exitcode < 8)
1526				exitcode = 8;
1527			fatal("couldn't open specified batch file");
1528		}
1529		/* XXX Remove code dup from shutdown code */
1530	next_line:
1531		if (fgets(batchline, sizeof(batchline), batchfp) != 0) {
1532			bargc = 1;
1533			debug("batch line %s", batchline);
1534			if (batchline[0] == '\r' || batchline[0] == '\n'
1535			    || batchline[0] == '#' || batchline[0] == ';')
1536				goto next_line;
1537			input = batchline;
1538			bargv[bargc] = next_token(&input, " \t\r\n");
1539			while ((bargv[bargc] != NULL) && (bargc < 14)) {
1540				bargc++;
1541				bargv[bargc] = next_token(&input, " \t\r\n");
1542			}
1543
1544			bargv[0] = argv[0];
1545			argv0 = argv[0];
1546
1547			reorder_args(bargc, (char **)bargv);
1548			parse_args(ISC_TRUE, ISC_FALSE, bargc, (char **)bargv);
1549		}
1550	}
1551	/*
1552	 * If no lookup specified, search for root
1553	 */
1554	if ((lookup_list.head == NULL) && !config_only) {
1555		lookup = clone_lookup(default_lookup, ISC_TRUE);
1556		lookup->trace_root = ISC_TF(lookup->trace ||
1557					    lookup->ns_search_only);
1558		lookup->new_search = ISC_TRUE;
1559		strcpy(lookup->textname, ".");
1560		lookup->rdtype = dns_rdatatype_ns;
1561		lookup->rdtypeset = ISC_TRUE;
1562		if (firstarg) {
1563			printgreeting(argc, argv, lookup);
1564			firstarg = ISC_FALSE;
1565		}
1566		ISC_LIST_APPEND(lookup_list, lookup, link);
1567	} else if (!config_only && firstarg) {
1568			printgreeting(argc, argv, lookup);
1569			firstarg = ISC_FALSE;
1570	}
1571}
1572
1573/*
1574 * Callback from dighost.c to allow program-specific shutdown code.
1575 * Here, we're possibly reading from a batch file, then shutting down
1576 * for real if there's nothing in the batch file to read.
1577 */
1578void
1579dighost_shutdown(void) {
1580	char batchline[MXNAME];
1581	int bargc;
1582	char *bargv[16];
1583	char *input;
1584
1585
1586	if (batchname == NULL) {
1587		isc_app_shutdown();
1588		return;
1589	}
1590
1591	fflush(stdout);
1592	if (feof(batchfp)) {
1593		batchname = NULL;
1594		isc_app_shutdown();
1595		if (batchfp != stdin)
1596			fclose(batchfp);
1597		return;
1598	}
1599
1600	if (fgets(batchline, sizeof(batchline), batchfp) != 0) {
1601		debug("batch line %s", batchline);
1602		bargc = 1;
1603		input = batchline;
1604		bargv[bargc] = next_token(&input, " \t\r\n");
1605		while ((bargv[bargc] != NULL) && (bargc < 14)) {
1606			bargc++;
1607			bargv[bargc] = next_token(&input, " \t\r\n");
1608		}
1609
1610		bargv[0] = argv0;
1611
1612		reorder_args(bargc, (char **)bargv);
1613		parse_args(ISC_TRUE, ISC_FALSE, bargc, (char **)bargv);
1614		start_lookup();
1615	} else {
1616		batchname = NULL;
1617		if (batchfp != stdin)
1618			fclose(batchfp);
1619		isc_app_shutdown();
1620		return;
1621	}
1622}
1623
1624int
1625main(int argc, char **argv) {
1626	isc_result_t result;
1627	dig_server_t *s, *s2;
1628
1629	ISC_LIST_INIT(lookup_list);
1630	ISC_LIST_INIT(server_list);
1631	ISC_LIST_INIT(search_list);
1632
1633	debug("main()");
1634	preparse_args(argc, argv);
1635	progname = argv[0];
1636	result = isc_app_start();
1637	check_result(result, "isc_app_start");
1638	setup_libs();
1639	parse_args(ISC_FALSE, ISC_FALSE, argc, argv);
1640	setup_system();
1641	if (domainopt[0] != '\0') {
1642		set_search_domain(domainopt);
1643		usesearch = ISC_TRUE;
1644	}
1645	result = isc_app_onrun(mctx, global_task, onrun_callback, NULL);
1646	check_result(result, "isc_app_onrun");
1647	isc_app_run();
1648	s = ISC_LIST_HEAD(default_lookup->my_server_list);
1649	while (s != NULL) {
1650		debug("freeing server %p belonging to %p",
1651		      s, default_lookup);
1652		s2 = s;
1653		s = ISC_LIST_NEXT(s, link);
1654		ISC_LIST_DEQUEUE(default_lookup->my_server_list, s2, link);
1655		isc_mem_free(mctx, s2);
1656	}
1657	isc_mem_free(mctx, default_lookup);
1658	if (batchname != NULL) {
1659		if (batchfp != stdin)
1660			fclose(batchfp);
1661		batchname = NULL;
1662	}
1663#ifdef DIG_SIGCHASE
1664	clean_trustedkey();
1665#endif
1666	cancel_all();
1667	destroy_libs();
1668	isc_app_finish();
1669	return (exitcode);
1670}
1671