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