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