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