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