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