xref: /openbsd-src/usr.bin/dig/dig.c (revision 46035553bfdd96e63c94e32da0210227ec2e3cf1)
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.19 2020/12/20 11:27:47 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, 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, buf);
567 				if (result == ISC_R_NOSPACE)
568 					goto buftoosmall;
569 				check_result(result,
570 					  "dns_message_pseudosectiontotext");
571 				result = dns_message_pseudosectiontotext(
572 						   msg,
573 						   DNS_PSEUDOSECTION_SIG0,
574 						   style, flags, buf);
575 				if (result == ISC_R_NOSPACE)
576 					goto buftoosmall;
577 				check_result(result,
578 					   "dns_message_pseudosectiontotext");
579 			}
580 		}
581 	}
582 
583 	if (headers && query->lookup->comments && !short_form)
584 		printf("\n");
585 
586 	printf("%.*s", (int)isc_buffer_usedlength(buf),
587 	       (char *)isc_buffer_base(buf));
588 	isc_buffer_free(&buf);
589 
590 cleanup:
591 	if (style != NULL)
592 		dns_master_styledestroy(&style);
593 	return (result);
594 }
595 
596 /*%
597  * print the greeting message when the program first starts up.
598  */
599 static void
600 printgreeting(int argc, char **argv, dig_lookup_t *lookup) {
601 	int i;
602 	static int first = 1;
603 	char append[MXNAME];
604 
605 	if (printcmd) {
606 		snprintf(lookup->cmdline, sizeof(lookup->cmdline),
607 			 "%s; <<>> dig " VERSION " <<>>",
608 			 first?"\n":"");
609 		i = 1;
610 		while (i < argc) {
611 			snprintf(append, sizeof(append), " %s", argv[i++]);
612 			strlcat(lookup->cmdline, append,
613 				sizeof(lookup->cmdline));
614 		}
615 		strlcat(lookup->cmdline, "\n", sizeof(lookup->cmdline));
616 		if (first && addresscount != 0) {
617 			snprintf(append, sizeof(append),
618 				 "; (%d server%s found)\n",
619 				 addresscount,
620 				 addresscount > 1 ? "s" : "");
621 			strlcat(lookup->cmdline, append,
622 				sizeof(lookup->cmdline));
623 		}
624 		if (first) {
625 			snprintf(append, sizeof(append),
626 				 ";; global options:%s%s\n",
627 				 short_form ? " +short" : "",
628 				 printcmd ? " +cmd" : "");
629 			first = 0;
630 			strlcat(lookup->cmdline, append,
631 				sizeof(lookup->cmdline));
632 		}
633 	}
634 }
635 
636 static void
637 plus_option(const char *option, int is_batchfile,
638 	    dig_lookup_t *lookup)
639 {
640 	isc_result_t result;
641 	char option_store[256];
642 	char *cmd, *value, *ptr, *code, *ep;
643 	const char *errstr;
644 	long lval;
645 	uint32_t num;
646 	int state = 1;
647 	size_t n;
648 
649 	strlcpy(option_store, option, sizeof(option_store));
650 	ptr = option_store;
651 	cmd = next_token(&ptr, "=");
652 	if (cmd == NULL) {
653 		printf(";; Invalid option %s\n", option_store);
654 		return;
655 	}
656 	value = ptr;
657 	if (strncasecmp(cmd, "no", 2)==0) {
658 		cmd += 2;
659 		state = 0;
660 	}
661 
662 #define FULLCHECK(A) \
663 	do { \
664 		size_t _l = strlen(cmd); \
665 		if (_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) \
666 			goto invalid_option; \
667 	} while (0)
668 #define FULLCHECK2(A, B) \
669 	do { \
670 		size_t _l = strlen(cmd); \
671 		if ((_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) && \
672 		    (_l >= sizeof(B) || strncasecmp(cmd, B, _l) != 0)) \
673 			goto invalid_option; \
674 	} while (0)
675 
676 	switch (cmd[0]) {
677 	case 'a':
678 		switch (cmd[1]) {
679 		case 'a': /* aaonly / aaflag */
680 			FULLCHECK2("aaonly", "aaflag");
681 			lookup->aaonly = state;
682 			break;
683 		case 'd':
684 			switch (cmd[2]) {
685 			case 'd': /* additional */
686 				FULLCHECK("additional");
687 				lookup->section_additional = state;
688 				break;
689 			case 'f': /* adflag */
690 			case '\0': /* +ad is a synonym for +adflag */
691 				FULLCHECK("adflag");
692 				lookup->adflag = state;
693 				break;
694 			default:
695 				goto invalid_option;
696 			}
697 			break;
698 		case 'l': /* all */
699 			FULLCHECK("all");
700 			lookup->section_question = state;
701 			lookup->section_authority = state;
702 			lookup->section_answer = state;
703 			lookup->section_additional = state;
704 			lookup->comments = state;
705 			lookup->stats = state;
706 			printcmd = state;
707 			break;
708 		case 'n': /* answer */
709 			FULLCHECK("answer");
710 			lookup->section_answer = state;
711 			break;
712 		case 'u': /* authority */
713 			FULLCHECK("authority");
714 			lookup->section_authority = state;
715 			break;
716 		default:
717 			goto invalid_option;
718 		}
719 		break;
720 	case 'b':
721 		switch (cmd[1]) {
722 		case 'e':/* besteffort */
723 			FULLCHECK("besteffort");
724 			lookup->besteffort = state;
725 			break;
726 		case 'u':/* bufsize */
727 			FULLCHECK("bufsize");
728 			if (value == NULL)
729 				goto need_value;
730 			if (!state)
731 				goto invalid_option;
732 			num = strtonum(value, 0, COMMSIZE, &errstr);
733 			if (errstr != NULL)
734 				fatal("buffer size is %s: '%s'", errstr, value);
735 			lookup->udpsize = num;
736 			break;
737 		default:
738 			goto invalid_option;
739 		}
740 		break;
741 	case 'c':
742 		switch (cmd[1]) {
743 		case 'd':/* cdflag */
744 			switch (cmd[2]) {
745 			case 'f': /* cdflag */
746 			case '\0': /* +cd is a synonym for +cdflag */
747 				FULLCHECK("cdflag");
748 				lookup->cdflag = state;
749 				break;
750 			default:
751 				goto invalid_option;
752 			}
753 			break;
754 		case 'l': /* class */
755 			/* keep +cl for backwards compatibility */
756 			FULLCHECK2("cl", "class");
757 			noclass = !state;
758 			break;
759 		case 'm': /* cmd */
760 			FULLCHECK("cmd");
761 			printcmd = state;
762 			break;
763 		case 'o': /* comments */
764 			switch (cmd[2]) {
765 			case 'o':
766 				FULLCHECK("cookie");
767 				goto sit;
768 			case 'm':
769 				FULLCHECK("comments");
770 				lookup->comments = state;
771 				if (lookup == default_lookup)
772 					pluscomm = state;
773 				break;
774 			default:
775 				goto invalid_option;
776 			}
777 			break;
778 		case 'r':
779 			FULLCHECK("crypto");
780 			nocrypto = !state;
781 			break;
782 		default:
783 			goto invalid_option;
784 		}
785 		break;
786 	case 'd':
787 		switch (cmd[1]) {
788 		case 'e': /* defname */
789 			FULLCHECK("defname");
790 			if (!lookup->trace) {
791 				usesearch = state;
792 			}
793 			break;
794 		case 'n': /* dnssec */
795 			FULLCHECK("dnssec");
796 			if (state && lookup->edns == -1)
797 				lookup->edns = 0;
798 			lookup->dnssec = state;
799 			break;
800 		case 'o': /* domain */
801 			FULLCHECK("domain");
802 			if (value == NULL)
803 				goto need_value;
804 			if (!state)
805 				goto invalid_option;
806 			strlcpy(domainopt, value, sizeof(domainopt));
807 			break;
808 		default:
809 			goto invalid_option;
810 		}
811 		break;
812 	case 'e':
813 		switch (cmd[1]) {
814 		case 'd':
815 			switch(cmd[2]) {
816 			case 'n':
817 				switch (cmd[3]) {
818 				case 's':
819 					switch (cmd[4]) {
820 					case 0:
821 						FULLCHECK("edns");
822 						if (!state) {
823 							lookup->edns = -1;
824 							break;
825 						}
826 						if (value == NULL) {
827 							lookup->edns = 0;
828 							break;
829 						}
830 						num = strtonum(value, 0, 255,
831 						    &errstr);
832 						if (errstr != NULL)
833 							fatal("edns is %s: "
834 							    "'%s'", errstr,
835 							    value);
836 						lookup->edns = num;
837 						break;
838 					case 'f':
839 						FULLCHECK("ednsflags");
840 						if (!state) {
841 							lookup->ednsflags = 0;
842 							break;
843 						}
844 						if (value == NULL) {
845 							lookup->ednsflags = 0;
846 							break;
847 						}
848 						errno = 0;
849 						lval = strtol(value, &ep, 0);
850 						if (value[0] == '\0' || *ep !=
851 						    '\0' || lval < 0 || lval >
852 						    0xffff || errno != 0)
853 							fatal("Couldn't parse "
854 							      "ednsflags");
855 						lookup->ednsflags = lval;
856 						break;
857 					case 'n':
858 						FULLCHECK("ednsnegotiation");
859 						lookup->ednsneg = state;
860 						break;
861 					case 'o':
862 						FULLCHECK("ednsopt");
863 						if (!state) {
864 							lookup->ednsoptscnt = 0;
865 							break;
866 						}
867 						if (value == NULL)
868 							fatal("ednsopt no "
869 							      "code point "
870 							      "specified");
871 						code = next_token(&value, ":");
872 						save_opt(lookup, code, value);
873 						break;
874 					default:
875 						goto invalid_option;
876 					}
877 					break;
878 				default:
879 					goto invalid_option;
880 				}
881 				break;
882 			default:
883 				goto invalid_option;
884 			}
885 			break;
886 		case 'x':
887 			FULLCHECK("expire");
888 			lookup->expire = state;
889 			break;
890 		default:
891 			goto invalid_option;
892 		}
893 		break;
894 	case 'f': /* fail */
895 		FULLCHECK("fail");
896 		lookup->servfail_stops = state;
897 		break;
898 	case 'i':
899 		switch (cmd[1]) {
900 		case 'd': /* identify */
901 			switch (cmd[2]) {
902 			case 'e':
903 				FULLCHECK("identify");
904 				lookup->identify = state;
905 				break;
906 			case 'n':
907 				FULLCHECK("idnout");
908 				fprintf(stderr, ";; IDN support not enabled\n");
909 				break;
910 			default:
911 				goto invalid_option;
912 			}
913 			break;
914 		case 'g': /* ignore */
915 		default: /*
916 			  * Inherits default for compatibility (+[no]i*).
917 			  */
918 			FULLCHECK("ignore");
919 			lookup->ignore = state;
920 		}
921 		break;
922 	case 'k':
923 		FULLCHECK("keepopen");
924 		keep_open = state;
925 		break;
926 	case 'm': /* multiline */
927 		FULLCHECK("multiline");
928 		multiline = state;
929 		break;
930 	case 'n':
931 		switch (cmd[1]) {
932 		case 'd': /* ndots */
933 			FULLCHECK("ndots");
934 			if (value == NULL)
935 				goto need_value;
936 			if (!state)
937 				goto invalid_option;
938 			num = strtonum(value, 0, MAXNDOTS, &errstr);
939 			if (errstr != NULL)
940 				fatal("ndots is %s: '%s'", errstr, value);
941 			ndots = num;
942 			break;
943 		case 's':
944 			switch (cmd[2]) {
945 			case 'i': /* nsid */
946 				FULLCHECK("nsid");
947 				if (state && lookup->edns == -1)
948 					lookup->edns = 0;
949 				lookup->nsid = state;
950 				break;
951 			case 's': /* nssearch */
952 				FULLCHECK("nssearch");
953 				lookup->ns_search_only = state;
954 				if (state) {
955 					lookup->trace_root = 1;
956 					lookup->recurse = 1;
957 					lookup->identify = 1;
958 					lookup->stats = 0;
959 					lookup->comments = 0;
960 					lookup->section_additional = 0;
961 					lookup->section_authority = 0;
962 					lookup->section_question = 0;
963 					lookup->rdtype = dns_rdatatype_ns;
964 					lookup->rdtypeset = 1;
965 					short_form = 1;
966 					rrcomments = 0;
967 				}
968 				break;
969 			default:
970 				goto invalid_option;
971 			}
972 			break;
973 		default:
974 			goto invalid_option;
975 		}
976 		break;
977 	case 'o':
978 		switch (cmd[1]) {
979 		case 'n':
980 			FULLCHECK("onesoa");
981 			onesoa = state;
982 			break;
983 		case 'p':
984 			FULLCHECK("opcode");
985 			if (!state) {
986 				lookup->opcode = 0;	/* default - query */
987 				break;
988 			}
989 			if (value == NULL)
990 				goto need_value;
991 			for (num = 0;
992 			     num < sizeof(opcodetext)/sizeof(opcodetext[0]);
993 			     num++) {
994 				if (strcasecmp(opcodetext[num], value) == 0)
995 					break;
996 			}
997 			if (num < 16) {
998 				lookup->opcode = (dns_opcode_t)num;
999 				break;
1000 			}
1001 			num = strtonum(value, 0, 15, &errstr);
1002 			if (errstr != NULL)
1003 				fatal("opcode is %s: '%s'", errstr, value);
1004 			lookup->opcode = (dns_opcode_t)num;
1005 			break;
1006 		default:
1007 			goto invalid_option;
1008 		}
1009 		break;
1010 	case 'q':
1011 		switch (cmd[1]) {
1012 		case 'r': /* qr */
1013 			FULLCHECK("qr");
1014 			qr = state;
1015 			break;
1016 		case 'u': /* question */
1017 			FULLCHECK("question");
1018 			lookup->section_question = state;
1019 			if (lookup == default_lookup)
1020 				plusquest = state;
1021 			break;
1022 		default:
1023 			goto invalid_option;
1024 		}
1025 		break;
1026 	case 'r':
1027 		switch (cmd[1]) {
1028 		case 'd': /* rdflag */
1029 			FULLCHECK("rdflag");
1030 			lookup->recurse = state;
1031 			break;
1032 		case 'e':
1033 			switch (cmd[2]) {
1034 			case 'c': /* recurse */
1035 				FULLCHECK("recurse");
1036 				lookup->recurse = state;
1037 				break;
1038 			case 't': /* retry / retries */
1039 				FULLCHECK2("retry", "retries");
1040 				if (value == NULL)
1041 					goto need_value;
1042 				if (!state)
1043 					goto invalid_option;
1044 				lookup->retries = strtonum(value, 0,
1045 				    MAXTRIES - 1, &errstr);
1046 				if (errstr != NULL)
1047 					fatal("retries is %s: '%s'", errstr,
1048 					    value);
1049 				lookup->retries++;
1050 				break;
1051 			default:
1052 				goto invalid_option;
1053 			}
1054 			break;
1055 		case 'r': /* rrcomments */
1056 			FULLCHECK("rrcomments");
1057 			rrcomments = state ? 1 : -1;
1058 			break;
1059 		default:
1060 			goto invalid_option;
1061 		}
1062 		break;
1063 	case 's':
1064 		switch (cmd[1]) {
1065 		case 'e': /* search */
1066 			FULLCHECK("search");
1067 			if (!lookup->trace) {
1068 				usesearch = state;
1069 			}
1070 			break;
1071 		case 'h':
1072 			if (cmd[2] != 'o')
1073 				goto invalid_option;
1074 			switch (cmd[3]) {
1075 			case 'r': /* short */
1076 				FULLCHECK("short");
1077 				short_form = state;
1078 				if (state) {
1079 					printcmd = 0;
1080 					lookup->section_additional = 0;
1081 					lookup->section_answer = 1;
1082 					lookup->section_authority = 0;
1083 					lookup->section_question = 0;
1084 					lookup->comments = 0;
1085 					lookup->stats = 0;
1086 					rrcomments = -1;
1087 				}
1088 				break;
1089 			case 'w': /* showsearch */
1090 				FULLCHECK("showsearch");
1091 				if (!lookup->trace) {
1092 					showsearch = state;
1093 					usesearch = state;
1094 				}
1095 				break;
1096 			default:
1097 				goto invalid_option;
1098 			}
1099 			break;
1100 		case 'i':
1101 			switch (cmd[2]) {
1102 			case 't': /* sit */
1103 				FULLCHECK("sit");
1104  sit:
1105 				if (state && lookup->edns == -1)
1106 					lookup->edns = 0;
1107 				lookup->sit = state;
1108 				if (value != NULL) {
1109 					n = strlcpy(sitvalue, value,
1110 						    sizeof(sitvalue));
1111 					if (n >= sizeof(sitvalue))
1112 						fatal("SIT data too large");
1113 					lookup->sitvalue = sitvalue;
1114 				} else
1115 					lookup->sitvalue = NULL;
1116 				break;
1117 			default:
1118 				goto invalid_option;
1119 			}
1120 			break;
1121 		case 'p': /* split */
1122 			FULLCHECK("split");
1123 			if (value != NULL && !state)
1124 				goto invalid_option;
1125 			if (!state) {
1126 				splitwidth = 0;
1127 				break;
1128 			} else if (value == NULL)
1129 				break;
1130 
1131 			splitwidth = strtonum(value, 0, 1023, &errstr);
1132 			if (errstr != NULL)
1133 				fatal("split is %s: '%s'", errstr, value);
1134 			if ((splitwidth % 4) != 0U) {
1135 				splitwidth = ((splitwidth + 3) / 4) * 4;
1136 				fprintf(stderr, ";; Warning, split must be "
1137 						"a multiple of 4; adjusting "
1138 						"to %u\n", splitwidth);
1139 			}
1140 			/*
1141 			 * There is an adjustment done in the
1142 			 * totext_<rrtype>() functions which causes
1143 			 * splitwidth to shrink.  This is okay when we're
1144 			 * using the default width but incorrect in this
1145 			 * case, so we correct for it
1146 			 */
1147 			if (splitwidth)
1148 				splitwidth += 3;
1149 			break;
1150 		case 't': /* stats */
1151 			FULLCHECK("stats");
1152 			lookup->stats = state;
1153 			break;
1154 		case 'u': /* subnet */
1155 			FULLCHECK("subnet");
1156 			if (state && value == NULL)
1157 				goto need_value;
1158 			if (!state) {
1159 				if (lookup->ecs_addr != NULL) {
1160 					free(lookup->ecs_addr);
1161 					lookup->ecs_addr = NULL;
1162 				}
1163 				break;
1164 			}
1165 			if (lookup->edns == -1)
1166 				lookup->edns = 0;
1167 			if (lookup->ecs_addr != NULL) {
1168 				free(lookup->ecs_addr);
1169 				lookup->ecs_addr = NULL;
1170 			}
1171 			result = parse_netprefix(&lookup->ecs_addr,
1172 			    &lookup->ecs_plen, value);
1173 			if (result != ISC_R_SUCCESS)
1174 				fatal("Couldn't parse client");
1175 			break;
1176 		default:
1177 			goto invalid_option;
1178 		}
1179 		break;
1180 	case 't':
1181 		switch (cmd[1]) {
1182 		case 'c': /* tcp */
1183 			FULLCHECK("tcp");
1184 			if (!is_batchfile) {
1185 				lookup->tcp_mode = state;
1186 				lookup->tcp_mode_set = 1;
1187 			}
1188 			break;
1189 		case 'i': /* timeout */
1190 			FULLCHECK("timeout");
1191 			if (value == NULL)
1192 				goto need_value;
1193 			if (!state)
1194 				goto invalid_option;
1195 			timeout = strtonum(value, 0, MAXTIMEOUT, &errstr);
1196 			if (errstr != NULL)
1197 				fatal("timeout is %s: '%s'", errstr, value);
1198 			if (timeout == 0)
1199 				timeout = 1;
1200 			break;
1201 		case 'r':
1202 			switch (cmd[2]) {
1203 			case 'a': /* trace */
1204 				FULLCHECK("trace");
1205 				lookup->trace = state;
1206 				lookup->trace_root = state;
1207 				if (state) {
1208 					lookup->recurse = 0;
1209 					lookup->identify = 1;
1210 					lookup->comments = 0;
1211 					rrcomments = 0;
1212 					lookup->stats = 0;
1213 					lookup->section_additional = 0;
1214 					lookup->section_authority = 1;
1215 					lookup->section_question = 0;
1216 					lookup->dnssec = 1;
1217 					usesearch = 0;
1218 				}
1219 				break;
1220 			case 'i': /* tries */
1221 				FULLCHECK("tries");
1222 				if (value == NULL)
1223 					goto need_value;
1224 				if (!state)
1225 					goto invalid_option;
1226 				lookup->retries = strtonum(value, 0, MAXTRIES,
1227 				    &errstr);
1228 				if (errstr != NULL)
1229 					fatal("tries is %s: '%s'", errstr,
1230 					    value);
1231 				if (lookup->retries == 0)
1232 					lookup->retries = 1;
1233 				break;
1234 			default:
1235 				goto invalid_option;
1236 			}
1237 			break;
1238 		case 't': /* ttlid */
1239 			FULLCHECK("ttlid");
1240 			nottl = !state;
1241 			break;
1242 		default:
1243 			goto invalid_option;
1244 		}
1245 		break;
1246 	case 'v':
1247 		FULLCHECK("vc");
1248 		if (!is_batchfile) {
1249 			lookup->tcp_mode = state;
1250 			lookup->tcp_mode_set = 1;
1251 		}
1252 		break;
1253 	default:
1254 	invalid_option:
1255 	need_value:
1256 		fprintf(stderr, "Invalid option: +%s\n",
1257 			option);
1258 		usage();
1259 	}
1260 	return;
1261 }
1262 
1263 /*%
1264  * #1 returned if value was used
1265  */
1266 static const char *single_dash_opts = "46dhinuv";
1267 static const char *dash_opts = "46bcdfhikmnptvyx";
1268 static int
1269 dash_option(char *option, char *next, dig_lookup_t **lookup,
1270 	    int *open_type_class, int *need_clone,
1271 	    int config_only, int argc, char **argv,
1272 	    int *firstarg)
1273 {
1274 	char opt, *value, *ptr, *ptr2, *ptr3;
1275 	isc_result_t result;
1276 	int value_from_next;
1277 	isc_textregion_t tr;
1278 	dns_rdatatype_t rdtype;
1279 	dns_rdataclass_t rdclass;
1280 	char textname[MXNAME];
1281 	char *cmd;
1282 	uint32_t num;
1283 	const char *errstr;
1284 
1285 	while (strpbrk(option, single_dash_opts) == &option[0]) {
1286 		/*
1287 		 * Since the -[46dhinuv] options do not take an argument,
1288 		 * account for them (in any number and/or combination)
1289 		 * if they appear as the first character(s) of a q-opt.
1290 		 */
1291 		opt = option[0];
1292 		switch (opt) {
1293 		case '4':
1294 			if (have_ipv4)
1295 				have_ipv6 = 0;
1296 			else
1297 				fatal("can't find IPv4 networking");
1298 			break;
1299 		case '6':
1300 			if (have_ipv6)
1301 				have_ipv4 = 0;
1302 			else
1303 				fatal("can't find IPv6 networking");
1304 			break;
1305 		case 'd':
1306 			ptr = strpbrk(&option[1], dash_opts);
1307 			if (ptr != &option[1]) {
1308 				cmd = option;
1309 				FULLCHECK("debug");
1310 				debugging = 1;
1311 				return (0);
1312 			} else
1313 				debugging = 1;
1314 			break;
1315 		case 'h':
1316 			help();
1317 			exit(0);
1318 			break;
1319 		case 'i':
1320 			ip6_int = 1;
1321 			break;
1322 		case 'n':
1323 			/* deprecated */
1324 			break;
1325 		case 'u':
1326 			use_usec = 1;
1327 			break;
1328 		case 'v':
1329 			version();
1330 			exit(0);
1331 			break;
1332 		}
1333 		if (strlen(option) > 1U)
1334 			option = &option[1];
1335 		else
1336 			return (0);
1337 	}
1338 	opt = option[0];
1339 	if (strlen(option) > 1U) {
1340 		value_from_next = 0;
1341 		value = &option[1];
1342 	} else {
1343 		value_from_next = 1;
1344 		value = next;
1345 	}
1346 	if (value == NULL)
1347 		goto invalid_option;
1348 	switch (opt) {
1349 	case 'b': {
1350 		struct addrinfo *ai = NULL, hints;
1351 		int error;
1352 		char *hash;
1353 
1354 		memset(&hints, 0, sizeof(hints));
1355 		hints.ai_flags = AI_NUMERICHOST;
1356 		hints.ai_socktype = SOCK_DGRAM;
1357 
1358 		hash = strchr(value, '#');
1359 		if (hash != NULL) {
1360 			*hash = '\0';
1361 			error = getaddrinfo(value, hash + 1, &hints, &ai);
1362 			*hash = '#';
1363 		} else
1364 			error = getaddrinfo(value, NULL, &hints, &ai);
1365 
1366 		if (error)
1367 			fatal("invalid address %s: %s", value,
1368 			    gai_strerror(error));
1369 		if (ai == NULL || ai->ai_addrlen > sizeof(bind_address))
1370 			fatal("invalid address %s", value);
1371 		if (!have_ipv4 && ai->ai_family == AF_INET)
1372 			fatal("%s: wrong address family", value);
1373 		if (!have_ipv6 && ai->ai_family == AF_INET6)
1374 			fatal("%s: wrong address family", value);
1375 
1376 		memset(&bind_address, 0, sizeof(bind_address));
1377 		memcpy(&bind_address, ai->ai_addr, ai->ai_addrlen);
1378 
1379 		specified_source = 1;
1380 		return (value_from_next);
1381 	}
1382 	case 'c':
1383 		if ((*lookup)->rdclassset) {
1384 			fprintf(stderr, ";; Warning, extra class option\n");
1385 		}
1386 		*open_type_class = 0;
1387 		tr.base = value;
1388 		tr.length = (unsigned int) strlen(value);
1389 		result = dns_rdataclass_fromtext(&rdclass,
1390 						 (isc_textregion_t *)&tr);
1391 		if (result == ISC_R_SUCCESS) {
1392 			(*lookup)->rdclass = rdclass;
1393 			(*lookup)->rdclassset = 1;
1394 		} else
1395 			fprintf(stderr, ";; Warning, ignoring "
1396 				"invalid class %s\n",
1397 				value);
1398 		return (value_from_next);
1399 	case 'f':
1400 		batchname = value;
1401 		return (value_from_next);
1402 	case 'k':
1403 		strlcpy(keyfile, value, sizeof(keyfile));
1404 		return (value_from_next);
1405 	case 'p':
1406 		num = strtonum(value, 0, MAXPORT, &errstr);
1407 		if (errstr != NULL)
1408 			fatal("port number is %s: '%s'", errstr, value);
1409 		port = num;
1410 		return (value_from_next);
1411 	case 'q':
1412 		if (!config_only) {
1413 			if (*need_clone)
1414 				(*lookup) = clone_lookup(default_lookup,
1415 							 1);
1416 			*need_clone = 1;
1417 			strlcpy((*lookup)->textname, value,
1418 				sizeof((*lookup)->textname));
1419 			(*lookup)->trace_root = (*lookup)->trace  ||
1420 						     (*lookup)->ns_search_only;
1421 			(*lookup)->new_search = 1;
1422 			if (*firstarg) {
1423 				printgreeting(argc, argv, *lookup);
1424 				*firstarg = 0;
1425 			}
1426 			ISC_LIST_APPEND(lookup_list, (*lookup), link);
1427 			debug("looking up %s", (*lookup)->textname);
1428 		}
1429 		return (value_from_next);
1430 	case 't':
1431 		*open_type_class = 0;
1432 		if (strncasecmp(value, "ixfr=", 5) == 0) {
1433 			rdtype = dns_rdatatype_ixfr;
1434 			result = ISC_R_SUCCESS;
1435 		} else {
1436 			tr.base = value;
1437 			tr.length = (unsigned int) strlen(value);
1438 			result = dns_rdatatype_fromtext(&rdtype,
1439 						(isc_textregion_t *)&tr);
1440 			if (result == ISC_R_SUCCESS &&
1441 			    rdtype == dns_rdatatype_ixfr) {
1442 				result = DNS_R_UNKNOWN;
1443 			}
1444 		}
1445 		if (result == ISC_R_SUCCESS) {
1446 			if ((*lookup)->rdtypeset) {
1447 				fprintf(stderr, ";; Warning, "
1448 						"extra type option\n");
1449 			}
1450 			if (rdtype == dns_rdatatype_ixfr) {
1451 				uint32_t serial;
1452 				(*lookup)->rdtype = dns_rdatatype_ixfr;
1453 				(*lookup)->rdtypeset = 1;
1454 				serial = strtonum(&value[5], 0, MAXSERIAL,
1455 				    &errstr);
1456 				if (errstr != NULL)
1457 					fatal("serial number is %s: '%s'",
1458 					    errstr, &value[5]);
1459 				(*lookup)->ixfr_serial = serial;
1460 				(*lookup)->section_question = plusquest;
1461 				(*lookup)->comments = pluscomm;
1462 				if (!(*lookup)->tcp_mode_set)
1463 					(*lookup)->tcp_mode = 1;
1464 			} else {
1465 				(*lookup)->rdtype = rdtype;
1466 				if (!config_only)
1467 					(*lookup)->rdtypeset = 1;
1468 				if (rdtype == dns_rdatatype_axfr) {
1469 					(*lookup)->section_question = plusquest;
1470 					(*lookup)->comments = pluscomm;
1471 				}
1472 				(*lookup)->ixfr_serial = 0;
1473 			}
1474 		} else
1475 			fprintf(stderr, ";; Warning, ignoring "
1476 				 "invalid type %s\n",
1477 				 value);
1478 		return (value_from_next);
1479 	case 'y':
1480 		ptr = next_token(&value, ":");	/* hmac type or name */
1481 		if (ptr == NULL) {
1482 			usage();
1483 		}
1484 		ptr2 = next_token(&value, ":");	/* name or secret */
1485 		if (ptr2 == NULL)
1486 			usage();
1487 		ptr3 = next_token(&value, ":"); /* secret or NULL */
1488 		if (ptr3 != NULL) {
1489 			parse_hmac(ptr);
1490 			ptr = ptr2;
1491 			ptr2 = ptr3;
1492 		} else  {
1493 			hmacname = DNS_TSIG_HMACSHA256_NAME;
1494 			digestbits = 0;
1495 		}
1496 		strlcpy(keynametext, ptr, sizeof(keynametext));
1497 		strlcpy(keysecret, ptr2, sizeof(keysecret));
1498 		return (value_from_next);
1499 	case 'x':
1500 		if (*need_clone)
1501 			*lookup = clone_lookup(default_lookup, 1);
1502 		*need_clone = 1;
1503 		if (get_reverse(textname, sizeof(textname), value,
1504 				ip6_int, 0) == ISC_R_SUCCESS) {
1505 			strlcpy((*lookup)->textname, textname,
1506 				sizeof((*lookup)->textname));
1507 			debug("looking up %s", (*lookup)->textname);
1508 			(*lookup)->trace_root = (*lookup)->trace  ||
1509 						(*lookup)->ns_search_only;
1510 			(*lookup)->ip6_int = ip6_int;
1511 			if (!(*lookup)->rdtypeset)
1512 				(*lookup)->rdtype = dns_rdatatype_ptr;
1513 			if (!(*lookup)->rdclassset)
1514 				(*lookup)->rdclass = dns_rdataclass_in;
1515 			(*lookup)->new_search = 1;
1516 			if (*firstarg) {
1517 				printgreeting(argc, argv, *lookup);
1518 				*firstarg = 0;
1519 			}
1520 			ISC_LIST_APPEND(lookup_list, *lookup, link);
1521 		} else {
1522 			fprintf(stderr, "Invalid IP address %s\n", value);
1523 			exit(1);
1524 		}
1525 		return (value_from_next);
1526 	invalid_option:
1527 	default:
1528 		fprintf(stderr, "Invalid option: -%s\n", option);
1529 		usage();
1530 	}
1531 	/* NOTREACHED */
1532 	return (0);
1533 }
1534 
1535 /*%
1536  * Because we may be trying to do memory allocation recording, we're going
1537  * to need to parse the arguments for the -m *before* we start the main
1538  * argument parsing routine.
1539  *
1540  * I'd prefer not to have to do this, but I am not quite sure how else to
1541  * fix the problem.  Argument parsing in dig involves memory allocation
1542  * by its nature, so it can't be done in the main argument parser.
1543  */
1544 static void
1545 preparse_args(int argc, char **argv) {
1546 	int rc;
1547 	char **rv;
1548 	char *option;
1549 
1550 	rc = argc;
1551 	rv = argv;
1552 	for (rc--, rv++; rc > 0; rc--, rv++) {
1553 		if (rv[0][0] != '-')
1554 			continue;
1555 		option = &rv[0][1];
1556 		while (strpbrk(option, single_dash_opts) == &option[0]) {
1557 			switch (option[0]) {
1558 			case '4':
1559 				if (ipv6only)
1560 					fatal("only one of -4 and -6 allowed");
1561 				ipv4only = 1;
1562 				break;
1563 			case '6':
1564 				if (ipv4only)
1565 					fatal("only one of -4 and -6 allowed");
1566 				ipv6only = 1;
1567 				break;
1568 			}
1569 			option = &option[1];
1570 		}
1571 	}
1572 }
1573 
1574 static void
1575 parse_args(int is_batchfile, int config_only,
1576 	   int argc, char **argv)
1577 {
1578 	isc_result_t result;
1579 	isc_textregion_t tr;
1580 	int firstarg = 1;
1581 	dig_lookup_t *lookup = NULL;
1582 	dns_rdatatype_t rdtype;
1583 	dns_rdataclass_t rdclass;
1584 	int open_type_class = 1;
1585 	char batchline[MXNAME];
1586 	int bargc;
1587 	char *bargv[64];
1588 	int rc;
1589 	char **rv;
1590 	char *input;
1591 	int i;
1592 	int need_clone = 1;
1593 	const char *errstr;
1594 
1595 	/*
1596 	 * The semantics for parsing the args is a bit complex; if
1597 	 * we don't have a host yet, make the arg apply globally,
1598 	 * otherwise make it apply to the latest host.  This is
1599 	 * a bit different than the previous versions, but should
1600 	 * form a consistent user interface.
1601 	 *
1602 	 * First, create a "default lookup" which won't actually be used
1603 	 * anywhere, except for cloning into new lookups
1604 	 */
1605 
1606 	debug("parse_args()");
1607 	if (!is_batchfile) {
1608 		debug("making new lookup");
1609 		default_lookup = make_empty_lookup();
1610 		default_lookup->adflag = 1;
1611 		default_lookup->edns = 0;
1612 	}
1613 
1614 	if (is_batchfile && !config_only) {
1615 		/* Processing '-f batchfile'. */
1616 		lookup = clone_lookup(default_lookup, 1);
1617 		need_clone = 0;
1618 	} else
1619 		lookup = default_lookup;
1620 
1621 	rc = argc;
1622 	rv = argv;
1623 	for (rc--, rv++; rc > 0; rc--, rv++) {
1624 		debug("main parsing %s", rv[0]);
1625 		if (strncmp(rv[0], "%", 1) == 0)
1626 			break;
1627 		if (rv[0][0] == '@') {
1628 
1629 			if (is_batchfile && !config_only) {
1630 				addresscount = getaddresses(lookup, &rv[0][1],
1631 							     &result);
1632 				if (result != ISC_R_SUCCESS) {
1633 					fprintf(stderr, "couldn't get address "
1634 						"for '%s': %s: skipping "
1635 						"lookup\n", &rv[0][1],
1636 						isc_result_totext(result));
1637 					if (ISC_LINK_LINKED(lookup, link))
1638 						ISC_LIST_DEQUEUE(lookup_list,
1639 								 lookup, link);
1640 					destroy_lookup(lookup);
1641 					return;
1642 				}
1643 			} else
1644 				addresscount = getaddresses(lookup, &rv[0][1],
1645 							    NULL);
1646 		} else if (rv[0][0] == '+') {
1647 			plus_option(&rv[0][1], is_batchfile,
1648 				    lookup);
1649 		} else if (rv[0][0] == '-') {
1650 			if (rc <= 1) {
1651 				if (dash_option(&rv[0][1], NULL,
1652 						&lookup, &open_type_class,
1653 						&need_clone, config_only,
1654 						argc, argv, &firstarg)) {
1655 					rc--;
1656 					rv++;
1657 				}
1658 			} else {
1659 				if (dash_option(&rv[0][1], rv[1],
1660 						&lookup, &open_type_class,
1661 						&need_clone, config_only,
1662 						argc, argv, &firstarg)) {
1663 					rc--;
1664 					rv++;
1665 				}
1666 			}
1667 		} else {
1668 			/*
1669 			 * Anything which isn't an option
1670 			 */
1671 			if (open_type_class) {
1672 				if (strncasecmp(rv[0], "ixfr=", 5) == 0) {
1673 					rdtype = dns_rdatatype_ixfr;
1674 					result = ISC_R_SUCCESS;
1675 				} else {
1676 					tr.base = rv[0];
1677 					tr.length =
1678 						(unsigned int) strlen(rv[0]);
1679 					result = dns_rdatatype_fromtext(&rdtype,
1680 						(isc_textregion_t *)&tr);
1681 					if (result == ISC_R_SUCCESS &&
1682 					    rdtype == dns_rdatatype_ixfr) {
1683 						fprintf(stderr, ";; Warning, "
1684 							"ixfr requires a "
1685 							"serial number\n");
1686 						continue;
1687 					}
1688 				}
1689 				if (result == ISC_R_SUCCESS) {
1690 					if (lookup->rdtypeset) {
1691 						fprintf(stderr, ";; Warning, "
1692 							"extra type option\n");
1693 					}
1694 					if (rdtype == dns_rdatatype_ixfr) {
1695 						uint32_t serial;
1696 						lookup->rdtype =
1697 							dns_rdatatype_ixfr;
1698 						lookup->rdtypeset = 1;
1699 						serial = strtonum(&rv[0][5], 0,
1700 						    MAXSERIAL, &errstr);
1701 						if (errstr != NULL)
1702 							fatal("serial number "
1703 							    "is %s: '%s'",
1704 							    errstr, &rv[0][5]);
1705 						lookup->ixfr_serial = serial;
1706 						lookup->section_question =
1707 							plusquest;
1708 						lookup->comments = pluscomm;
1709 						if (!lookup->tcp_mode_set)
1710 							lookup->tcp_mode = 1;
1711 					} else {
1712 						lookup->rdtype = rdtype;
1713 						lookup->rdtypeset = 1;
1714 						if (rdtype ==
1715 						    dns_rdatatype_axfr) {
1716 						    lookup->section_question =
1717 								plusquest;
1718 						    lookup->comments = pluscomm;
1719 						}
1720 						lookup->ixfr_serial = 0;
1721 					}
1722 					continue;
1723 				}
1724 				result = dns_rdataclass_fromtext(&rdclass,
1725 						     (isc_textregion_t *)&tr);
1726 				if (result == ISC_R_SUCCESS) {
1727 					if (lookup->rdclassset) {
1728 						fprintf(stderr, ";; Warning, "
1729 							"extra class option\n");
1730 					}
1731 					lookup->rdclass = rdclass;
1732 					lookup->rdclassset = 1;
1733 					continue;
1734 				}
1735 			}
1736 
1737 			if (!config_only) {
1738 				if (need_clone)
1739 					lookup = clone_lookup(default_lookup,
1740 								      1);
1741 				need_clone = 1;
1742 				strlcpy(lookup->textname, rv[0],
1743 					sizeof(lookup->textname));
1744 				lookup->trace_root = lookup->trace  ||
1745 						     lookup->ns_search_only;
1746 				lookup->new_search = 1;
1747 				if (firstarg) {
1748 					printgreeting(argc, argv, lookup);
1749 					firstarg = 0;
1750 				}
1751 				ISC_LIST_APPEND(lookup_list, lookup, link);
1752 				debug("looking up %s", lookup->textname);
1753 			}
1754 			/* XXX Error message */
1755 		}
1756 	}
1757 
1758 	/*
1759 	 * If we have a batchfile, seed the lookup list with the
1760 	 * first entry, then trust the callback in dighost_shutdown
1761 	 * to get the rest
1762 	 */
1763 	if ((batchname != NULL) && !(is_batchfile)) {
1764 		if (strcmp(batchname, "-") == 0)
1765 			batchfp = stdin;
1766 		else
1767 			batchfp = fopen(batchname, "r");
1768 		if (batchfp == NULL) {
1769 			perror(batchname);
1770 			if (exitcode < 8)
1771 				exitcode = 8;
1772 			fatal("couldn't open specified batch file");
1773 		}
1774 		/* XXX Remove code dup from shutdown code */
1775 	next_line:
1776 		if (fgets(batchline, sizeof(batchline), batchfp) != NULL) {
1777 			bargc = 1;
1778 			debug("batch line %s", batchline);
1779 			if (batchline[0] == '\r' || batchline[0] == '\n'
1780 			    || batchline[0] == '#' || batchline[0] == ';')
1781 				goto next_line;
1782 			input = batchline;
1783 			bargv[bargc] = next_token(&input, " \t\r\n");
1784 			while ((bargc < 14) && (bargv[bargc] != NULL)) {
1785 				bargc++;
1786 				bargv[bargc] = next_token(&input, " \t\r\n");
1787 			}
1788 
1789 			bargv[0] = argv[0];
1790 			argv0 = argv[0];
1791 
1792 			for(i = 0; i < bargc; i++)
1793 				debug("batch argv %d: %s", i, bargv[i]);
1794 			parse_args(1, 0, bargc, (char **)bargv);
1795 			return;
1796 		}
1797 		return;
1798 	}
1799 	/*
1800 	 * If no lookup specified, search for root
1801 	 */
1802 	if ((lookup_list.head == NULL) && !config_only) {
1803 		if (need_clone)
1804 			lookup = clone_lookup(default_lookup, 1);
1805 		need_clone = 1;
1806 		lookup->trace_root = lookup->trace || lookup->ns_search_only;
1807 		lookup->new_search = 1;
1808 		strlcpy(lookup->textname, ".", sizeof(lookup->textname));
1809 		lookup->rdtype = dns_rdatatype_ns;
1810 		lookup->rdtypeset = 1;
1811 		if (firstarg) {
1812 			printgreeting(argc, argv, lookup);
1813 			firstarg = 0;
1814 		}
1815 		ISC_LIST_APPEND(lookup_list, lookup, link);
1816 	}
1817 	if (!need_clone)
1818 		destroy_lookup(lookup);
1819 }
1820 
1821 /*
1822  * Callback from dighost.c to allow program-specific shutdown code.
1823  * Here, we're possibly reading from a batch file, then shutting down
1824  * for real if there's nothing in the batch file to read.
1825  */
1826 static void
1827 query_finished(void) {
1828 	char batchline[MXNAME];
1829 	int bargc;
1830 	char *bargv[16];
1831 	char *input;
1832 	int i;
1833 
1834 	if (batchname == NULL) {
1835 		isc_app_shutdown();
1836 		return;
1837 	}
1838 
1839 	fflush(stdout);
1840 	if (feof(batchfp)) {
1841 		batchname = NULL;
1842 		isc_app_shutdown();
1843 		if (batchfp != stdin)
1844 			fclose(batchfp);
1845 		return;
1846 	}
1847 
1848 	if (fgets(batchline, sizeof(batchline), batchfp) != NULL) {
1849 		debug("batch line %s", batchline);
1850 		bargc = 1;
1851 		input = batchline;
1852 		bargv[bargc] = next_token(&input, " \t\r\n");
1853 		while ((bargc < 14) && (bargv[bargc] != NULL)) {
1854 			bargc++;
1855 			bargv[bargc] = next_token(&input, " \t\r\n");
1856 		}
1857 
1858 		bargv[0] = argv0;
1859 
1860 		for(i = 0; i < bargc; i++)
1861 			debug("batch argv %d: %s", i, bargv[i]);
1862 		parse_args(1, 0, bargc, (char **)bargv);
1863 		start_lookup();
1864 	} else {
1865 		batchname = NULL;
1866 		if (batchfp != stdin)
1867 			fclose(batchfp);
1868 		isc_app_shutdown();
1869 		return;
1870 	}
1871 }
1872 
1873 void dig_setup(int argc, char **argv)
1874 {
1875 	isc_result_t result;
1876 
1877 	ISC_LIST_INIT(lookup_list);
1878 	ISC_LIST_INIT(server_list);
1879 	ISC_LIST_INIT(root_hints_server_list);
1880 	ISC_LIST_INIT(search_list);
1881 
1882 	if (pledge("stdio rpath inet dns", NULL) == -1) {
1883 		perror("pledge");
1884 		exit(1);
1885 	}
1886 
1887 	debug("dig_setup()");
1888 
1889 	/* setup dighost callbacks */
1890 	dighost_printmessage = printmessage;
1891 	dighost_received = received;
1892 	dighost_trying = trying;
1893 	dighost_shutdown = query_finished;
1894 
1895 	progname = argv[0];
1896 	preparse_args(argc, argv);
1897 
1898 	result = isc_app_start();
1899 	check_result(result, "isc_app_start");
1900 
1901 	setup_libs();
1902 	setup_system(ipv4only, ipv6only);
1903 }
1904 
1905 void dig_query_setup(int is_batchfile, int config_only,
1906 		int argc, char **argv)
1907 {
1908 	debug("dig_query_setup");
1909 
1910 	parse_args(is_batchfile, config_only, argc, argv);
1911 	if (keyfile[0] != 0)
1912 		setup_file_key();
1913 	else if (keysecret[0] != 0)
1914 		setup_text_key();
1915 
1916 	if (pledge("stdio inet dns", NULL) == -1) {
1917 		perror("pledge");
1918 		exit(1);
1919 	}
1920 
1921 	if (domainopt[0] != '\0') {
1922 		set_search_domain(domainopt);
1923 		usesearch = 1;
1924 	}
1925 }
1926 
1927 void dig_startup() {
1928 	isc_result_t result;
1929 
1930 	debug("dig_startup()");
1931 
1932 	result = isc_app_onrun(global_task, onrun_callback, NULL);
1933 	check_result(result, "isc_app_onrun");
1934 	isc_app_run();
1935 }
1936 
1937 void
1938 dig_shutdown() {
1939 	destroy_lookup(default_lookup);
1940 	if (batchname != NULL) {
1941 		if (batchfp != stdin)
1942 			fclose(batchfp);
1943 		batchname = NULL;
1944 	}
1945 
1946 	cancel_all();
1947 	destroy_libs();
1948 }
1949 
1950 /*% Main processing routine for dig */
1951 int
1952 main(int argc, char **argv) {
1953 	extern char *__progname;
1954 
1955 	if (strcmp("host", __progname) == 0)
1956 		return host_main(argc, argv);
1957 	if (strcmp("nslookup", __progname) == 0)
1958 		return nslookup_main(argc, argv);
1959 
1960 	dig_setup(argc, argv);
1961 	dig_query_setup(0, 0, argc, argv);
1962 	dig_startup();
1963 	dig_shutdown();
1964 
1965 	return (exitcode);
1966 }
1967