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