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