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