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