xref: /netbsd-src/external/mpl/bind/dist/bin/dig/dig.c (revision 8ecbf5f02b752fcb7debe1a8fab1dc82602bc760)
1 /*	$NetBSD: dig.c,v 1.5 2020/05/24 19:46:11 christos 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 http://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 			isc_time_formatISO8601ms(&query->time_sent, tbuf,
672 						 sizeof(tbuf));
673 			printf("    query_time: !!timestamp %s\n", tbuf);
674 		}
675 
676 		if (!isquery && !isc_time_isepoch(&query->time_recv)) {
677 			char tbuf[100];
678 			isc_time_formatISO8601ms(&query->time_recv, tbuf,
679 						 sizeof(tbuf));
680 			printf("    response_time: !!timestamp %s\n", tbuf);
681 		}
682 
683 		printf("    message_size: %ub\n",
684 		       isc_buffer_usedlength(msgbuf));
685 
686 		pf = isc_sockaddr_pf(&query->sockaddr);
687 		if (pf == PF_INET || pf == PF_INET6) {
688 			printf("    socket_family: %s\n",
689 			       pf == PF_INET ? "INET" : "INET6");
690 
691 			printf("    socket_protocol: %s\n",
692 			       query->lookup->tcp_mode ? "TCP" : "UDP");
693 
694 			sport = isc_sockaddr_getport(&query->sockaddr);
695 			isc_sockaddr_format(&query->sockaddr, sockstr,
696 					    sizeof(sockstr));
697 			hash = strchr(sockstr, '#');
698 			if (hash != NULL) {
699 				*hash = '\0';
700 			}
701 			if (strcmp(sockstr, "::") == 0) {
702 				strlcat(sockstr, "0", sizeof(sockstr));
703 			}
704 
705 			printf("    response_address: %s\n", sockstr);
706 			printf("    response_port: %u\n", sport);
707 		}
708 
709 		if (query->sock != NULL &&
710 		    isc_socket_getsockname(query->sock, &saddr) ==
711 			    ISC_R_SUCCESS)
712 		{
713 			sport = isc_sockaddr_getport(&saddr);
714 			isc_sockaddr_format(&saddr, sockstr, sizeof(sockstr));
715 			hash = strchr(sockstr, '#');
716 			if (hash != NULL) {
717 				*hash = '\0';
718 			}
719 			if (strcmp(sockstr, "::") == 0) {
720 				strlcat(sockstr, "0", sizeof(sockstr));
721 			}
722 
723 			printf("    query_address: %s\n", sockstr);
724 			printf("    query_port: %u\n", sport);
725 		}
726 
727 		printf("    %s:\n", isquery ? "query_message_data"
728 					    : "response_message_data");
729 		result = dns_message_headertotext(msg, style, flags, buf);
730 	} else if (query->lookup->comments && !short_form) {
731 		if (query->lookup->cmdline[0] != '\0' && printcmd) {
732 			printf("; %s\n", query->lookup->cmdline);
733 		}
734 		if (msg == query->lookup->sendmsg) {
735 			printf(";; Sending:\n");
736 		} else {
737 			printf(";; Got answer:\n");
738 		}
739 
740 		if (headers) {
741 			if (isdotlocal(msg)) {
742 				printf(";; WARNING: .local is reserved for "
743 				       "Multicast DNS\n;; You are currently "
744 				       "testing what happens when an mDNS "
745 				       "query is leaked to DNS\n");
746 			}
747 			printf(";; ->>HEADER<<- opcode: %s, status: %s, "
748 			       "id: %u\n",
749 			       opcodetext[msg->opcode],
750 			       rcode_totext(msg->rcode), msg->id);
751 			printf(";; flags:");
752 			if ((msg->flags & DNS_MESSAGEFLAG_QR) != 0) {
753 				printf(" qr");
754 			}
755 			if ((msg->flags & DNS_MESSAGEFLAG_AA) != 0) {
756 				printf(" aa");
757 			}
758 			if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0) {
759 				printf(" tc");
760 			}
761 			if ((msg->flags & DNS_MESSAGEFLAG_RD) != 0) {
762 				printf(" rd");
763 			}
764 			if ((msg->flags & DNS_MESSAGEFLAG_RA) != 0) {
765 				printf(" ra");
766 			}
767 			if ((msg->flags & DNS_MESSAGEFLAG_AD) != 0) {
768 				printf(" ad");
769 			}
770 			if ((msg->flags & DNS_MESSAGEFLAG_CD) != 0) {
771 				printf(" cd");
772 			}
773 			if ((msg->flags & 0x0040U) != 0) {
774 				printf("; MBZ: 0x4");
775 			}
776 
777 			printf("; QUERY: %u, ANSWER: %u, "
778 			       "AUTHORITY: %u, ADDITIONAL: %u\n",
779 			       msg->counts[DNS_SECTION_QUESTION],
780 			       msg->counts[DNS_SECTION_ANSWER],
781 			       msg->counts[DNS_SECTION_AUTHORITY],
782 			       msg->counts[DNS_SECTION_ADDITIONAL]);
783 
784 			if (msg != query->lookup->sendmsg &&
785 			    (msg->flags & DNS_MESSAGEFLAG_RD) != 0 &&
786 			    (msg->flags & DNS_MESSAGEFLAG_RA) == 0)
787 			{
788 				printf(";; WARNING: recursion requested "
789 				       "but not available\n");
790 			}
791 		}
792 		if (msg != query->lookup->sendmsg &&
793 		    query->lookup->edns != -1 && msg->opt == NULL &&
794 		    (msg->rcode == dns_rcode_formerr ||
795 		     msg->rcode == dns_rcode_notimp))
796 		{
797 			printf("\n;; WARNING: EDNS query returned status "
798 			       "%s - retry with '%s+noedns'\n",
799 			       rcode_totext(msg->rcode),
800 			       query->lookup->dnssec ? "+nodnssec " : "");
801 		}
802 		if (msg != query->lookup->sendmsg && extrabytes != 0U) {
803 			printf(";; WARNING: Message has %u extra byte%s at "
804 			       "end\n",
805 			       extrabytes, extrabytes != 0 ? "s" : "");
806 		}
807 	}
808 
809 repopulate_buffer:
810 
811 	if (query->lookup->comments && headers && !short_form) {
812 		result = dns_message_pseudosectiontotext(
813 			msg, DNS_PSEUDOSECTION_OPT, style, flags, buf);
814 		if (result == ISC_R_NOSPACE) {
815 		buftoosmall:
816 			len += OUTPUTBUF;
817 			isc_buffer_free(&buf);
818 			isc_buffer_allocate(mctx, &buf, len);
819 			goto repopulate_buffer;
820 		}
821 		check_result(result, "dns_message_pseudosectiontotext");
822 	}
823 
824 	if (query->lookup->section_question && headers) {
825 		if (!short_form) {
826 			result = dns_message_sectiontotext(
827 				msg, DNS_SECTION_QUESTION, style, flags, buf);
828 			if (result == ISC_R_NOSPACE) {
829 				goto buftoosmall;
830 			}
831 			check_result(result, "dns_message_sectiontotext");
832 		}
833 	}
834 	if (query->lookup->section_answer) {
835 		if (!short_form) {
836 			result = dns_message_sectiontotext(
837 				msg, DNS_SECTION_ANSWER, style, flags, buf);
838 			if (result == ISC_R_NOSPACE) {
839 				goto buftoosmall;
840 			}
841 			check_result(result, "dns_message_sectiontotext");
842 		} else {
843 			result = short_answer(msg, flags, buf, query);
844 			if (result == ISC_R_NOSPACE) {
845 				goto buftoosmall;
846 			}
847 			check_result(result, "short_answer");
848 		}
849 	}
850 	if (query->lookup->section_authority) {
851 		if (!short_form) {
852 			result = dns_message_sectiontotext(
853 				msg, DNS_SECTION_AUTHORITY, style, flags, buf);
854 			if (result == ISC_R_NOSPACE) {
855 				goto buftoosmall;
856 			}
857 			check_result(result, "dns_message_sectiontotext");
858 		}
859 	}
860 	if (query->lookup->section_additional) {
861 		if (!short_form) {
862 			result = dns_message_sectiontotext(
863 				msg, DNS_SECTION_ADDITIONAL, style, flags, buf);
864 			if (result == ISC_R_NOSPACE) {
865 				goto buftoosmall;
866 			}
867 			check_result(result, "dns_message_sectiontotext");
868 			/*
869 			 * Only print the signature on the first record.
870 			 */
871 			if (headers) {
872 				result = dns_message_pseudosectiontotext(
873 					msg, DNS_PSEUDOSECTION_TSIG, style,
874 					flags, buf);
875 				if (result == ISC_R_NOSPACE) {
876 					goto buftoosmall;
877 				}
878 				check_result(result, "dns_message_"
879 						     "pseudosectiontotext");
880 				result = dns_message_pseudosectiontotext(
881 					msg, DNS_PSEUDOSECTION_SIG0, style,
882 					flags, buf);
883 				if (result == ISC_R_NOSPACE) {
884 					goto buftoosmall;
885 				}
886 				check_result(result, "dns_message_"
887 						     "pseudosectiontotext");
888 			}
889 		}
890 	}
891 
892 	if (headers && query->lookup->comments && !short_form && !yaml) {
893 		printf("\n");
894 	}
895 
896 	printf("%.*s", (int)isc_buffer_usedlength(buf),
897 	       (char *)isc_buffer_base(buf));
898 	isc_buffer_free(&buf);
899 
900 	if (style != NULL) {
901 		dns_master_styledestroy(&style, mctx);
902 	}
903 	return (result);
904 }
905 
906 /*%
907  * print the greeting message when the program first starts up.
908  */
909 static void
910 printgreeting(int argc, char **argv, dig_lookup_t *lookup) {
911 	int i;
912 	static bool first = true;
913 	char append[MXNAME];
914 
915 	if (printcmd) {
916 		snprintf(lookup->cmdline, sizeof(lookup->cmdline),
917 			 "%s; <<>> DiG " VERSION " <<>>", first ? "\n" : "");
918 		i = 1;
919 		while (i < argc) {
920 			snprintf(append, sizeof(append), " %s", argv[i++]);
921 			strlcat(lookup->cmdline, append,
922 				sizeof(lookup->cmdline));
923 		}
924 		strlcat(lookup->cmdline, "\n", sizeof(lookup->cmdline));
925 		if (first && addresscount != 0) {
926 			snprintf(append, sizeof(append),
927 				 "; (%d server%s found)\n", addresscount,
928 				 addresscount > 1 ? "s" : "");
929 			strlcat(lookup->cmdline, append,
930 				sizeof(lookup->cmdline));
931 		}
932 		if (first) {
933 			snprintf(append, sizeof(append),
934 				 ";; global options:%s%s\n",
935 				 short_form ? " +short" : "",
936 				 printcmd ? " +cmd" : "");
937 			first = false;
938 			strlcat(lookup->cmdline, append,
939 				sizeof(lookup->cmdline));
940 		}
941 	}
942 }
943 
944 /*%
945  * We're not using isc_commandline_parse() here since the command line
946  * syntax of dig is quite a bit different from that which can be described
947  * by that routine.
948  * XXX doc options
949  */
950 
951 static void
952 plus_option(char *option, bool is_batchfile, dig_lookup_t *lookup) {
953 	isc_result_t result;
954 	char *cmd, *value, *last = NULL, *code, *extra;
955 	uint32_t num;
956 	bool state = true;
957 	size_t n;
958 
959 	INSIST(option != NULL);
960 
961 	if ((cmd = strtok_r(option, "=", &last)) == NULL) {
962 		printf(";; Invalid option %s\n", option);
963 		return;
964 	}
965 	if (strncasecmp(cmd, "no", 2) == 0) {
966 		cmd += 2;
967 		state = false;
968 	}
969 	/* parse the rest of the string */
970 	value = strtok_r(NULL, "", &last);
971 
972 #define FULLCHECK(A)                                                 \
973 	do {                                                         \
974 		size_t _l = strlen(cmd);                             \
975 		if (_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) \
976 			goto invalid_option;                         \
977 	} while (/*CONSTCOND*/0)
978 #define FULLCHECK2(A, B)                                                 \
979 	do {                                                             \
980 		size_t _l = strlen(cmd);                                 \
981 		if ((_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) && \
982 		    (_l >= sizeof(B) || strncasecmp(cmd, B, _l) != 0))   \
983 			goto invalid_option;                             \
984 	} while (/*CONSTCOND*/0)
985 
986 	switch (cmd[0]) {
987 	case 'a':
988 		switch (cmd[1]) {
989 		case 'a': /* aaonly / aaflag */
990 			FULLCHECK2("aaonly", "aaflag");
991 			lookup->aaonly = state;
992 			break;
993 		case 'd':
994 			switch (cmd[2]) {
995 			case 'd': /* additional */
996 				FULLCHECK("additional");
997 				lookup->section_additional = state;
998 				break;
999 			case 'f':  /* adflag */
1000 			case '\0': /* +ad is a synonym for +adflag */
1001 				FULLCHECK("adflag");
1002 				lookup->adflag = state;
1003 				break;
1004 			default:
1005 				goto invalid_option;
1006 			}
1007 			break;
1008 		case 'l': /* all */
1009 			FULLCHECK("all");
1010 			lookup->section_question = state;
1011 			lookup->section_authority = state;
1012 			lookup->section_answer = state;
1013 			lookup->section_additional = state;
1014 			lookup->comments = state;
1015 			lookup->stats = state;
1016 			printcmd = state;
1017 			break;
1018 		case 'n': /* answer */
1019 			FULLCHECK("answer");
1020 			lookup->section_answer = state;
1021 			break;
1022 		case 'u': /* authority */
1023 			FULLCHECK("authority");
1024 			lookup->section_authority = state;
1025 			break;
1026 		default:
1027 			goto invalid_option;
1028 		}
1029 		break;
1030 	case 'b':
1031 		switch (cmd[1]) {
1032 		case 'a': /* badcookie */
1033 			FULLCHECK("badcookie");
1034 			lookup->badcookie = state;
1035 			break;
1036 		case 'e': /* besteffort */
1037 			FULLCHECK("besteffort");
1038 			lookup->besteffort = state;
1039 			break;
1040 		case 'u': /* bufsize */
1041 			FULLCHECK("bufsize");
1042 			if (value == NULL) {
1043 				goto need_value;
1044 			}
1045 			if (!state) {
1046 				goto invalid_option;
1047 			}
1048 			result = parse_uint(&num, value, COMMSIZE,
1049 					    "buffer size");
1050 			if (result != ISC_R_SUCCESS) {
1051 				warn("Couldn't parse buffer size");
1052 				goto exit_or_usage;
1053 			}
1054 			lookup->udpsize = num;
1055 			break;
1056 		default:
1057 			goto invalid_option;
1058 		}
1059 		break;
1060 	case 'c':
1061 		switch (cmd[1]) {
1062 		case 'd': /* cdflag */
1063 			switch (cmd[2]) {
1064 			case 'f':  /* cdflag */
1065 			case '\0': /* +cd is a synonym for +cdflag */
1066 				FULLCHECK("cdflag");
1067 				lookup->cdflag = state;
1068 				break;
1069 			default:
1070 				goto invalid_option;
1071 			}
1072 			break;
1073 		case 'l': /* class */
1074 			/* keep +cl for backwards compatibility */
1075 			FULLCHECK2("cl", "class");
1076 			lookup->noclass = !state;
1077 			break;
1078 		case 'm': /* cmd */
1079 			FULLCHECK("cmd");
1080 			printcmd = state;
1081 			break;
1082 		case 'o': /* comments */
1083 			switch (cmd[2]) {
1084 			case 'm':
1085 				FULLCHECK("comments");
1086 				lookup->comments = state;
1087 				if (lookup == default_lookup) {
1088 					pluscomm = state;
1089 				}
1090 				break;
1091 			case 'o': /* cookie */
1092 				FULLCHECK("cookie");
1093 				if (state && lookup->edns == -1) {
1094 					lookup->edns = 0;
1095 				}
1096 				lookup->sendcookie = state;
1097 				if (value != NULL) {
1098 					n = strlcpy(hexcookie, value,
1099 						    sizeof(hexcookie));
1100 					if (n >= sizeof(hexcookie)) {
1101 						warn("COOKIE data too large");
1102 						goto exit_or_usage;
1103 					}
1104 					lookup->cookie = hexcookie;
1105 				} else {
1106 					lookup->cookie = NULL;
1107 				}
1108 				break;
1109 			default:
1110 				goto invalid_option;
1111 			}
1112 			break;
1113 		case 'r':
1114 			FULLCHECK("crypto");
1115 			lookup->nocrypto = !state;
1116 			break;
1117 		default:
1118 			goto invalid_option;
1119 		}
1120 		break;
1121 	case 'd':
1122 		switch (cmd[1]) {
1123 		case 'e': /* defname */
1124 			FULLCHECK("defname");
1125 			if (!lookup->trace) {
1126 				usesearch = state;
1127 			}
1128 			break;
1129 		case 'n': /* dnssec */
1130 			FULLCHECK("dnssec");
1131 		dnssec:
1132 			if (state && lookup->edns == -1) {
1133 				lookup->edns = 0;
1134 			}
1135 			lookup->dnssec = state;
1136 			break;
1137 		case 'o': /* domain ... but treat "do" as synonym for dnssec */
1138 			if (cmd[2] == '\0') {
1139 				goto dnssec;
1140 			}
1141 			FULLCHECK("domain");
1142 			if (value == NULL) {
1143 				goto need_value;
1144 			}
1145 			if (!state) {
1146 				goto invalid_option;
1147 			}
1148 			strlcpy(domainopt, value, sizeof(domainopt));
1149 			break;
1150 		case 's': /* dscp */
1151 			FULLCHECK("dscp");
1152 			if (!state) {
1153 				lookup->dscp = -1;
1154 				break;
1155 			}
1156 			if (value == NULL) {
1157 				goto need_value;
1158 			}
1159 			result = parse_uint(&num, value, 0x3f, "DSCP");
1160 			if (result != ISC_R_SUCCESS) {
1161 				warn("Couldn't parse DSCP value");
1162 				goto exit_or_usage;
1163 			}
1164 			lookup->dscp = num;
1165 			break;
1166 		default:
1167 			goto invalid_option;
1168 		}
1169 		break;
1170 	case 'e':
1171 		switch (cmd[1]) {
1172 		case 'd':
1173 			switch (cmd[2]) {
1174 			case 'n':
1175 				switch (cmd[3]) {
1176 				case 's':
1177 					switch (cmd[4]) {
1178 					case 0:
1179 						FULLCHECK("edns");
1180 						if (!state) {
1181 							lookup->edns = -1;
1182 							break;
1183 						}
1184 						if (value == NULL) {
1185 							lookup->edns = 0;
1186 							break;
1187 						}
1188 						result = parse_uint(&num, value,
1189 								    255,
1190 								    "edns");
1191 						if (result != ISC_R_SUCCESS) {
1192 							warn("Couldn't parse "
1193 							     "edns");
1194 							goto exit_or_usage;
1195 						}
1196 						lookup->edns = num;
1197 						break;
1198 					case 'f':
1199 						FULLCHECK("ednsflags");
1200 						if (!state) {
1201 							lookup->ednsflags = 0;
1202 							break;
1203 						}
1204 						if (value == NULL) {
1205 							lookup->ednsflags = 0;
1206 							break;
1207 						}
1208 						result = parse_xint(
1209 							&num, value, 0xffff,
1210 							"ednsflags");
1211 						if (result != ISC_R_SUCCESS) {
1212 							warn("Couldn't parse "
1213 							     "ednsflags");
1214 							goto exit_or_usage;
1215 						}
1216 						lookup->ednsflags = num;
1217 						break;
1218 					case 'n':
1219 						FULLCHECK("ednsnegotiation");
1220 						lookup->ednsneg = state;
1221 						break;
1222 					case 'o':
1223 						FULLCHECK("ednsopt");
1224 						if (!state) {
1225 							lookup->ednsoptscnt = 0;
1226 							break;
1227 						}
1228 						code = NULL;
1229 						if (value != NULL) {
1230 							code = strtok_r(value,
1231 									":",
1232 									&last);
1233 						}
1234 						if (code == NULL) {
1235 							warn("ednsopt no "
1236 							     "code point "
1237 							     "specified");
1238 							goto exit_or_usage;
1239 						}
1240 						extra = strtok_r(NULL, "\0",
1241 								 &last);
1242 						save_opt(lookup, code, extra);
1243 						break;
1244 					default:
1245 						goto invalid_option;
1246 					}
1247 					break;
1248 				default:
1249 					goto invalid_option;
1250 				}
1251 				break;
1252 			default:
1253 				goto invalid_option;
1254 			}
1255 			break;
1256 		case 'x':
1257 			switch (cmd[2]) {
1258 			case 'p':
1259 				switch (cmd[3]) {
1260 				case 'a':
1261 					FULLCHECK("expandaaaa");
1262 					lookup->expandaaaa = state;
1263 					break;
1264 				case 'i':
1265 					FULLCHECK("expire");
1266 					lookup->expire = state;
1267 					break;
1268 				default:
1269 					goto invalid_option;
1270 				}
1271 				break;
1272 			default:
1273 				goto invalid_option;
1274 			}
1275 			break;
1276 		default:
1277 			goto invalid_option;
1278 		}
1279 		break;
1280 	case 'f': /* fail */
1281 		FULLCHECK("fail");
1282 		lookup->servfail_stops = state;
1283 		break;
1284 	case 'h':
1285 		FULLCHECK("header-only");
1286 		lookup->header_only = state;
1287 		break;
1288 	case 'i':
1289 		switch (cmd[1]) {
1290 		case 'd': /* identify */
1291 			switch (cmd[2]) {
1292 			case 'e':
1293 				FULLCHECK("identify");
1294 				lookup->identify = state;
1295 				break;
1296 			case 'n':
1297 				switch (cmd[3]) {
1298 				case 'i':
1299 					FULLCHECK("idnin");
1300 #ifndef HAVE_LIBIDN2
1301 					fprintf(stderr, ";; IDN input support"
1302 							" not enabled\n");
1303 #else  /* ifndef HAVE_LIBIDN2 */
1304 					lookup->idnin = state;
1305 #endif /* ifndef HAVE_LIBIDN2 */
1306 					break;
1307 				case 'o':
1308 					FULLCHECK("idnout");
1309 #ifndef HAVE_LIBIDN2
1310 					fprintf(stderr, ";; IDN output support"
1311 							" not enabled\n");
1312 #else  /* ifndef HAVE_LIBIDN2 */
1313 					lookup->idnout = state;
1314 #endif /* ifndef HAVE_LIBIDN2 */
1315 					break;
1316 				default:
1317 					goto invalid_option;
1318 				}
1319 				break;
1320 			default:
1321 				goto invalid_option;
1322 			}
1323 			break;
1324 		case 'g': /* ignore */
1325 		default:  /*
1326 			   * Inherits default for compatibility (+[no]i*).
1327 			   */
1328 			FULLCHECK("ignore");
1329 			lookup->ignore = state;
1330 		}
1331 		break;
1332 	case 'k':
1333 		switch (cmd[1]) {
1334 		case 'e':
1335 			switch (cmd[2]) {
1336 			case 'e':
1337 				switch (cmd[3]) {
1338 				case 'p':
1339 					switch (cmd[4]) {
1340 					case 'a':
1341 						FULLCHECK("keepalive");
1342 						lookup->tcp_keepalive = state;
1343 						break;
1344 					case 'o':
1345 						FULLCHECK("keepopen");
1346 						keep_open = state;
1347 						break;
1348 					default:
1349 						goto invalid_option;
1350 					}
1351 					break;
1352 				default:
1353 					goto invalid_option;
1354 				}
1355 				break;
1356 			default:
1357 				goto invalid_option;
1358 			}
1359 			break;
1360 		default:
1361 			goto invalid_option;
1362 		}
1363 		break;
1364 	case 'm': /* multiline */
1365 		switch (cmd[1]) {
1366 		case 'a':
1367 			FULLCHECK("mapped");
1368 			lookup->mapped = state;
1369 			break;
1370 		case 'u':
1371 			FULLCHECK("multiline");
1372 			lookup->multiline = state;
1373 			break;
1374 		default:
1375 			goto invalid_option;
1376 		}
1377 		break;
1378 	case 'n':
1379 		switch (cmd[1]) {
1380 		case 'd': /* ndots */
1381 			FULLCHECK("ndots");
1382 			if (value == NULL) {
1383 				goto need_value;
1384 			}
1385 			if (!state) {
1386 				goto invalid_option;
1387 			}
1388 			result = parse_uint(&num, value, MAXNDOTS, "ndots");
1389 			if (result != ISC_R_SUCCESS) {
1390 				warn("Couldn't parse ndots");
1391 				goto exit_or_usage;
1392 			}
1393 			ndots = num;
1394 			break;
1395 		case 's':
1396 			switch (cmd[2]) {
1397 			case 'i': /* nsid */
1398 				FULLCHECK("nsid");
1399 				if (state && lookup->edns == -1) {
1400 					lookup->edns = 0;
1401 				}
1402 				lookup->nsid = state;
1403 				break;
1404 			case 's': /* nssearch */
1405 				FULLCHECK("nssearch");
1406 				lookup->ns_search_only = state;
1407 				if (state) {
1408 					lookup->trace_root = true;
1409 					lookup->recurse = true;
1410 					lookup->identify = true;
1411 					lookup->stats = false;
1412 					lookup->comments = false;
1413 					lookup->section_additional = false;
1414 					lookup->section_authority = false;
1415 					lookup->section_question = false;
1416 					lookup->rdtype = dns_rdatatype_ns;
1417 					lookup->rdtypeset = true;
1418 					short_form = true;
1419 					lookup->rrcomments = 0;
1420 				}
1421 				break;
1422 			default:
1423 				goto invalid_option;
1424 			}
1425 			break;
1426 		default:
1427 			goto invalid_option;
1428 		}
1429 		break;
1430 	case 'o':
1431 		switch (cmd[1]) {
1432 		case 'n':
1433 			FULLCHECK("onesoa");
1434 			lookup->onesoa = state;
1435 			break;
1436 		case 'p':
1437 			FULLCHECK("opcode");
1438 			if (!state) {
1439 				lookup->opcode = 0; /* default - query */
1440 				break;
1441 			}
1442 			if (value == NULL) {
1443 				goto need_value;
1444 			}
1445 			for (num = 0;
1446 			     num < sizeof(opcodetext) / sizeof(opcodetext[0]);
1447 			     num++) {
1448 				if (strcasecmp(opcodetext[num], value) == 0) {
1449 					break;
1450 				}
1451 			}
1452 			if (num < 16) {
1453 				lookup->opcode = (dns_opcode_t)num;
1454 				break;
1455 			}
1456 			result = parse_uint(&num, value, 15, "opcode");
1457 			if (result != ISC_R_SUCCESS) {
1458 				warn("Couldn't parse opcode");
1459 				goto exit_or_usage;
1460 			}
1461 			lookup->opcode = (dns_opcode_t)num;
1462 			break;
1463 		default:
1464 			goto invalid_option;
1465 		}
1466 		break;
1467 	case 'p':
1468 		FULLCHECK("padding");
1469 		if (state && lookup->edns == -1) {
1470 			lookup->edns = 0;
1471 		}
1472 		if (value == NULL) {
1473 			goto need_value;
1474 		}
1475 		result = parse_uint(&num, value, 512, "padding");
1476 		if (result != ISC_R_SUCCESS) {
1477 			warn("Couldn't parse padding");
1478 			goto exit_or_usage;
1479 		}
1480 		lookup->padding = (uint16_t)num;
1481 		break;
1482 	case 'q':
1483 		switch (cmd[1]) {
1484 		case 'r': /* qr */
1485 			FULLCHECK("qr");
1486 			lookup->qr = state;
1487 			break;
1488 		case 'u': /* question */
1489 			FULLCHECK("question");
1490 			lookup->section_question = state;
1491 			if (lookup == default_lookup) {
1492 				plusquest = state;
1493 			}
1494 			break;
1495 		default:
1496 			goto invalid_option;
1497 		}
1498 		break;
1499 	case 'r':
1500 		switch (cmd[1]) {
1501 		case 'a': /* raflag */
1502 			FULLCHECK("raflag");
1503 			lookup->raflag = state;
1504 			break;
1505 		case 'd': /* rdflag */
1506 			FULLCHECK("rdflag");
1507 			lookup->recurse = state;
1508 			break;
1509 		case 'e':
1510 			switch (cmd[2]) {
1511 			case 'c': /* recurse */
1512 				FULLCHECK("recurse");
1513 				lookup->recurse = state;
1514 				break;
1515 			case 't': /* retry / retries */
1516 				FULLCHECK2("retry", "retries");
1517 				if (value == NULL) {
1518 					goto need_value;
1519 				}
1520 				if (!state) {
1521 					goto invalid_option;
1522 				}
1523 				result = parse_uint(&lookup->retries, value,
1524 						    MAXTRIES - 1, "retries");
1525 				if (result != ISC_R_SUCCESS) {
1526 					warn("Couldn't parse retries");
1527 					goto exit_or_usage;
1528 				}
1529 				lookup->retries++;
1530 				break;
1531 			default:
1532 				goto invalid_option;
1533 			}
1534 			break;
1535 		case 'r': /* rrcomments */
1536 			FULLCHECK("rrcomments");
1537 			lookup->rrcomments = state ? 1 : -1;
1538 			break;
1539 		default:
1540 			goto invalid_option;
1541 		}
1542 		break;
1543 	case 's':
1544 		switch (cmd[1]) {
1545 		case 'e': /* search */
1546 			FULLCHECK("search");
1547 			if (!lookup->trace) {
1548 				usesearch = state;
1549 			}
1550 			break;
1551 		case 'h':
1552 			if (cmd[2] != 'o') {
1553 				goto invalid_option;
1554 			}
1555 			switch (cmd[3]) {
1556 			case 'r': /* short */
1557 				FULLCHECK("short");
1558 				short_form = state;
1559 				if (state) {
1560 					printcmd = false;
1561 					lookup->section_additional = false;
1562 					lookup->section_answer = true;
1563 					lookup->section_authority = false;
1564 					lookup->section_question = false;
1565 					lookup->comments = false;
1566 					lookup->stats = false;
1567 					lookup->rrcomments = -1;
1568 				}
1569 				break;
1570 			case 'w': /* showsearch */
1571 				FULLCHECK("showsearch");
1572 				if (!lookup->trace) {
1573 					showsearch = state;
1574 					usesearch = state;
1575 				}
1576 				break;
1577 			default:
1578 				goto invalid_option;
1579 			}
1580 			break;
1581 		case 'i': /* sigchase */
1582 			FULLCHECK("sigchase");
1583 			fprintf(stderr, ";; +sigchase option is deprecated");
1584 			break;
1585 		case 'p': /* split */
1586 			FULLCHECK("split");
1587 			if (value != NULL && !state) {
1588 				goto invalid_option;
1589 			}
1590 			if (!state) {
1591 				splitwidth = 0;
1592 				break;
1593 			} else if (value == NULL) {
1594 				break;
1595 			}
1596 
1597 			result = parse_uint(&splitwidth, value, 1023, "split");
1598 			if ((splitwidth % 4) != 0U) {
1599 				splitwidth = ((splitwidth + 3) / 4) * 4;
1600 				fprintf(stderr,
1601 					";; Warning, split must be "
1602 					"a multiple of 4; adjusting "
1603 					"to %u\n",
1604 					splitwidth);
1605 			}
1606 			/*
1607 			 * There is an adjustment done in the
1608 			 * totext_<rrtype>() functions which causes
1609 			 * splitwidth to shrink.  This is okay when we're
1610 			 * using the default width but incorrect in this
1611 			 * case, so we correct for it
1612 			 */
1613 			if (splitwidth) {
1614 				splitwidth += 3;
1615 			}
1616 			if (result != ISC_R_SUCCESS) {
1617 				warn("Couldn't parse split");
1618 				goto exit_or_usage;
1619 			}
1620 			break;
1621 		case 't': /* stats */
1622 			FULLCHECK("stats");
1623 			lookup->stats = state;
1624 			break;
1625 		case 'u': /* subnet */
1626 			FULLCHECK("subnet");
1627 			if (state && value == NULL) {
1628 				goto need_value;
1629 			}
1630 			if (!state) {
1631 				if (lookup->ecs_addr != NULL) {
1632 					isc_mem_free(mctx, lookup->ecs_addr);
1633 					lookup->ecs_addr = NULL;
1634 				}
1635 				break;
1636 			}
1637 			if (lookup->edns == -1) {
1638 				lookup->edns = 0;
1639 			}
1640 			if (lookup->ecs_addr != NULL) {
1641 				isc_mem_free(mctx, lookup->ecs_addr);
1642 				lookup->ecs_addr = NULL;
1643 			}
1644 			result = parse_netprefix(&lookup->ecs_addr, value);
1645 			if (result != ISC_R_SUCCESS) {
1646 				warn("Couldn't parse client");
1647 				goto exit_or_usage;
1648 			}
1649 			break;
1650 		default:
1651 			goto invalid_option;
1652 		}
1653 		break;
1654 	case 't':
1655 		switch (cmd[1]) {
1656 		case 'c': /* tcp */
1657 			switch (cmd[2]) {
1658 			case 'f':
1659 				FULLCHECK("tcflag");
1660 				lookup->tcflag = state;
1661 				break;
1662 			case 'p':
1663 				FULLCHECK("tcp");
1664 				if (!is_batchfile) {
1665 					lookup->tcp_mode = state;
1666 					lookup->tcp_mode_set = true;
1667 				}
1668 				break;
1669 			default:
1670 				goto invalid_option;
1671 			}
1672 			break;
1673 		case 'i': /* timeout */
1674 			FULLCHECK("timeout");
1675 			if (value == NULL) {
1676 				goto need_value;
1677 			}
1678 			if (!state) {
1679 				goto invalid_option;
1680 			}
1681 			result = parse_uint(&timeout, value, MAXTIMEOUT,
1682 					    "timeout");
1683 			if (result != ISC_R_SUCCESS) {
1684 				warn("Couldn't parse timeout");
1685 				goto exit_or_usage;
1686 			}
1687 			if (timeout == 0) {
1688 				timeout = 1;
1689 			}
1690 			break;
1691 		case 'o':
1692 			FULLCHECK("topdown");
1693 			fprintf(stderr, ";; +topdown option is deprecated");
1694 			break;
1695 		case 'r':
1696 			switch (cmd[2]) {
1697 			case 'a': /* trace */
1698 				FULLCHECK("trace");
1699 				lookup->trace = state;
1700 				lookup->trace_root = state;
1701 				if (state) {
1702 					lookup->recurse = true;
1703 					lookup->identify = true;
1704 					lookup->comments = false;
1705 					lookup->rrcomments = 0;
1706 					lookup->stats = false;
1707 					lookup->section_additional = false;
1708 					lookup->section_authority = true;
1709 					lookup->section_question = false;
1710 					lookup->dnssec = true;
1711 					lookup->sendcookie = true;
1712 					usesearch = false;
1713 				}
1714 				break;
1715 			case 'i': /* tries */
1716 				FULLCHECK("tries");
1717 				if (value == NULL) {
1718 					goto need_value;
1719 				}
1720 				if (!state) {
1721 					goto invalid_option;
1722 				}
1723 				result = parse_uint(&lookup->retries, value,
1724 						    MAXTRIES, "tries");
1725 				if (result != ISC_R_SUCCESS) {
1726 					warn("Couldn't parse tries");
1727 					goto exit_or_usage;
1728 				}
1729 				if (lookup->retries == 0) {
1730 					lookup->retries = 1;
1731 				}
1732 				break;
1733 			case 'u': /* trusted-key */
1734 				FULLCHECK("trusted-key");
1735 				fprintf(stderr, ";; +trusted-key option is "
1736 						"deprecated");
1737 				break;
1738 			default:
1739 				goto invalid_option;
1740 			}
1741 			break;
1742 		case 't':
1743 			switch (cmd[2]) {
1744 			case 'l':
1745 				switch (cmd[3]) {
1746 				case 0:
1747 				case 'i': /* ttlid */
1748 					FULLCHECK2("ttl", "ttlid");
1749 					lookup->nottl = !state;
1750 					break;
1751 				case 'u': /* ttlunits */
1752 					FULLCHECK("ttlunits");
1753 					lookup->nottl = false;
1754 					lookup->ttlunits = state;
1755 					break;
1756 				default:
1757 					goto invalid_option;
1758 				}
1759 				break;
1760 			default:
1761 				goto invalid_option;
1762 			}
1763 			break;
1764 		default:
1765 			goto invalid_option;
1766 		}
1767 		break;
1768 	case 'u':
1769 		switch (cmd[1]) {
1770 		case 'n':
1771 			switch (cmd[2]) {
1772 			case 'e':
1773 				FULLCHECK("unexpected");
1774 				lookup->accept_reply_unexpected_src = state;
1775 				break;
1776 			case 'k':
1777 				FULLCHECK("unknownformat");
1778 				lookup->print_unknown_format = state;
1779 				break;
1780 			default:
1781 				goto invalid_option;
1782 			}
1783 			break;
1784 		default:
1785 			goto invalid_option;
1786 		}
1787 
1788 		break;
1789 	case 'v':
1790 		FULLCHECK("vc");
1791 		if (!is_batchfile) {
1792 			lookup->tcp_mode = state;
1793 			lookup->tcp_mode_set = true;
1794 		}
1795 		break;
1796 	case 'y': /* yaml */
1797 		FULLCHECK("yaml");
1798 		yaml = state;
1799 		if (state) {
1800 			printcmd = false;
1801 			lookup->stats = false;
1802 			lookup->rrcomments = -1;
1803 		}
1804 		break;
1805 	case 'z': /* zflag */
1806 		FULLCHECK("zflag");
1807 		lookup->zflag = state;
1808 		break;
1809 	default:
1810 	invalid_option:
1811 	need_value:
1812 #if TARGET_OS_IPHONE
1813 	exit_or_usage:
1814 #endif /* if TARGET_OS_IPHONE */
1815 		fprintf(stderr, "Invalid option: +%s\n", option);
1816 		usage();
1817 	}
1818 	return;
1819 
1820 #if !TARGET_OS_IPHONE
1821 exit_or_usage:
1822 	digexit();
1823 #endif /* if !TARGET_OS_IPHONE */
1824 }
1825 
1826 /*%
1827  * #true returned if value was used
1828  */
1829 static const char *single_dash_opts = "46dhimnruv";
1830 static const char *dash_opts = "46bcdfhikmnpqrtvyx";
1831 static bool
1832 dash_option(char *option, char *next, dig_lookup_t **lookup,
1833 	    bool *open_type_class, bool *need_clone, bool config_only, int argc,
1834 	    char **argv, bool *firstarg) {
1835 	char opt, *value, *ptr, *ptr2, *ptr3, *last;
1836 	isc_result_t result;
1837 	bool value_from_next;
1838 	isc_textregion_t tr;
1839 	dns_rdatatype_t rdtype;
1840 	dns_rdataclass_t rdclass;
1841 	char textname[MXNAME];
1842 	struct in_addr in4;
1843 	struct in6_addr in6;
1844 	in_port_t srcport;
1845 	char *hash, *cmd;
1846 	uint32_t num;
1847 
1848 	while (strpbrk(option, single_dash_opts) == &option[0]) {
1849 		/*
1850 		 * Since the -[46dhimnuv] options do not take an argument,
1851 		 * account for them (in any number and/or combination)
1852 		 * if they appear as the first character(s) of a q-opt.
1853 		 */
1854 		opt = option[0];
1855 		switch (opt) {
1856 		case '4':
1857 			if (have_ipv4) {
1858 				isc_net_disableipv6();
1859 				have_ipv6 = false;
1860 			} else {
1861 				fatal("can't find IPv4 networking");
1862 				/* NOTREACHED */
1863 				return (false);
1864 			}
1865 			break;
1866 		case '6':
1867 			if (have_ipv6) {
1868 				isc_net_disableipv4();
1869 				have_ipv4 = false;
1870 			} else {
1871 				fatal("can't find IPv6 networking");
1872 				/* NOTREACHED */
1873 				return (false);
1874 			}
1875 			break;
1876 		case 'd':
1877 			ptr = strpbrk(&option[1], dash_opts);
1878 			if (ptr != &option[1]) {
1879 				cmd = option;
1880 				FULLCHECK("debug");
1881 				debugging = true;
1882 				return (false);
1883 			} else {
1884 				debugging = true;
1885 			}
1886 			break;
1887 		case 'h':
1888 			help();
1889 			exit(0);
1890 			break;
1891 		case 'i':
1892 			/* deprecated */
1893 			break;
1894 		case 'm': /* memdebug */
1895 			/* memdebug is handled in preparse_args() */
1896 			break;
1897 		case 'n':
1898 			/* deprecated */
1899 			break;
1900 		case 'r':
1901 			debug("digrc (late)");
1902 			digrc = false;
1903 			break;
1904 		case 'u':
1905 			(*lookup)->use_usec = true;
1906 			break;
1907 		case 'v':
1908 			version();
1909 			exit(0);
1910 			break;
1911 		}
1912 		if (strlen(option) > 1U) {
1913 			option = &option[1];
1914 		} else {
1915 			return (false);
1916 		}
1917 	}
1918 	opt = option[0];
1919 	if (strlen(option) > 1U) {
1920 		value_from_next = false;
1921 		value = &option[1];
1922 	} else {
1923 		value_from_next = true;
1924 		value = next;
1925 	}
1926 	if (value == NULL) {
1927 		goto invalid_option;
1928 	}
1929 	switch (opt) {
1930 	case 'b':
1931 		hash = strchr(value, '#');
1932 		if (hash != NULL) {
1933 			result = parse_uint(&num, hash + 1, MAXPORT,
1934 					    "port number");
1935 			if (result != ISC_R_SUCCESS) {
1936 				fatal("Couldn't parse port number");
1937 			}
1938 			srcport = num;
1939 			*hash = '\0';
1940 		} else {
1941 			srcport = 0;
1942 		}
1943 		if (have_ipv6 && inet_pton(AF_INET6, value, &in6) == 1) {
1944 			isc_sockaddr_fromin6(&bind_address, &in6, srcport);
1945 			isc_net_disableipv4();
1946 		} else if (have_ipv4 && inet_pton(AF_INET, value, &in4) == 1) {
1947 			isc_sockaddr_fromin(&bind_address, &in4, srcport);
1948 			isc_net_disableipv6();
1949 		} else {
1950 			if (hash != NULL) {
1951 				*hash = '#';
1952 			}
1953 			fatal("invalid address %s", value);
1954 		}
1955 		if (hash != NULL) {
1956 			*hash = '#';
1957 		}
1958 		specified_source = true;
1959 		return (value_from_next);
1960 	case 'c':
1961 		if ((*lookup)->rdclassset) {
1962 			fprintf(stderr, ";; Warning, extra class option\n");
1963 		}
1964 		*open_type_class = false;
1965 		tr.base = value;
1966 		tr.length = (unsigned int)strlen(value);
1967 		result = dns_rdataclass_fromtext(&rdclass,
1968 						 (isc_textregion_t *)&tr);
1969 		if (result == ISC_R_SUCCESS) {
1970 			(*lookup)->rdclass = rdclass;
1971 			(*lookup)->rdclassset = true;
1972 		} else {
1973 			fprintf(stderr,
1974 				";; Warning, ignoring "
1975 				"invalid class %s\n",
1976 				value);
1977 		}
1978 		return (value_from_next);
1979 	case 'f':
1980 		atomic_store(&batchname, (uintptr_t)value);
1981 		return (value_from_next);
1982 	case 'k':
1983 		strlcpy(keyfile, value, sizeof(keyfile));
1984 		return (value_from_next);
1985 	case 'p':
1986 		result = parse_uint(&num, value, MAXPORT, "port number");
1987 		if (result != ISC_R_SUCCESS) {
1988 			fatal("Couldn't parse port number");
1989 		}
1990 		port = num;
1991 		return (value_from_next);
1992 	case 'q':
1993 		if (!config_only) {
1994 			if (*need_clone) {
1995 				(*lookup) = clone_lookup(default_lookup, true);
1996 			}
1997 			*need_clone = true;
1998 			strlcpy((*lookup)->textname, value,
1999 				sizeof((*lookup)->textname));
2000 			(*lookup)->trace_root = ((*lookup)->trace ||
2001 						 (*lookup)->ns_search_only);
2002 			(*lookup)->new_search = true;
2003 			if (*firstarg) {
2004 				printgreeting(argc, argv, *lookup);
2005 				*firstarg = false;
2006 			}
2007 			ISC_LIST_APPEND(lookup_list, (*lookup), link);
2008 			debug("looking up %s", (*lookup)->textname);
2009 		}
2010 		return (value_from_next);
2011 	case 't':
2012 		*open_type_class = false;
2013 		if (strncasecmp(value, "ixfr=", 5) == 0) {
2014 			rdtype = dns_rdatatype_ixfr;
2015 			result = ISC_R_SUCCESS;
2016 		} else {
2017 			tr.base = value;
2018 			tr.length = (unsigned int)strlen(value);
2019 			result = dns_rdatatype_fromtext(
2020 				&rdtype, (isc_textregion_t *)&tr);
2021 			if (result == ISC_R_SUCCESS &&
2022 			    rdtype == dns_rdatatype_ixfr) {
2023 				result = DNS_R_UNKNOWN;
2024 			}
2025 		}
2026 		if (result == ISC_R_SUCCESS) {
2027 			if ((*lookup)->rdtypeset) {
2028 				fprintf(stderr, ";; Warning, "
2029 						"extra type option\n");
2030 			}
2031 			if (rdtype == dns_rdatatype_ixfr) {
2032 				uint32_t serial;
2033 				(*lookup)->rdtype = dns_rdatatype_ixfr;
2034 				(*lookup)->rdtypeset = true;
2035 				result = parse_uint(&serial, &value[5],
2036 						    MAXSERIAL, "serial number");
2037 				if (result != ISC_R_SUCCESS) {
2038 					fatal("Couldn't parse serial number");
2039 				}
2040 				(*lookup)->ixfr_serial = serial;
2041 				(*lookup)->section_question = plusquest;
2042 				(*lookup)->comments = pluscomm;
2043 				if (!(*lookup)->tcp_mode_set) {
2044 					(*lookup)->tcp_mode = true;
2045 				}
2046 			} else {
2047 				(*lookup)->rdtype = rdtype;
2048 				if (!config_only) {
2049 					(*lookup)->rdtypeset = true;
2050 				}
2051 				if (rdtype == dns_rdatatype_axfr) {
2052 					(*lookup)->section_question = plusquest;
2053 					(*lookup)->comments = pluscomm;
2054 				} else if (rdtype == dns_rdatatype_any) {
2055 					if (!(*lookup)->tcp_mode_set) {
2056 						(*lookup)->tcp_mode = true;
2057 					}
2058 				}
2059 				(*lookup)->ixfr_serial = false;
2060 			}
2061 		} else {
2062 			fprintf(stderr,
2063 				";; Warning, ignoring "
2064 				"invalid type %s\n",
2065 				value);
2066 		}
2067 		return (value_from_next);
2068 	case 'y':
2069 		if ((ptr = strtok_r(value, ":", &last)) == NULL) {
2070 			usage();
2071 		}
2072 		if ((ptr2 = strtok_r(NULL, ":", &last)) == NULL) { /* name or
2073 								    * secret */
2074 			usage();
2075 		}
2076 		if ((ptr3 = strtok_r(NULL, ":", &last)) != NULL) { /* secret or
2077 								    * NULL */
2078 			parse_hmac(ptr);
2079 			ptr = ptr2;
2080 			ptr2 = ptr3;
2081 		} else {
2082 			hmacname = DNS_TSIG_HMACMD5_NAME;
2083 			digestbits = 0;
2084 		}
2085 		/* XXXONDREJ: FIXME */
2086 		strlcpy(keynametext, ptr, sizeof(keynametext));
2087 		strlcpy(keysecret, ptr2, sizeof(keysecret));
2088 		return (value_from_next);
2089 	case 'x':
2090 		if (*need_clone) {
2091 			*lookup = clone_lookup(default_lookup, true);
2092 		}
2093 		*need_clone = true;
2094 		if (get_reverse(textname, sizeof(textname), value, false) ==
2095 		    ISC_R_SUCCESS) {
2096 			strlcpy((*lookup)->textname, textname,
2097 				sizeof((*lookup)->textname));
2098 			debug("looking up %s", (*lookup)->textname);
2099 			(*lookup)->trace_root = ((*lookup)->trace ||
2100 						 (*lookup)->ns_search_only);
2101 			if (!(*lookup)->rdtypeset) {
2102 				(*lookup)->rdtype = dns_rdatatype_ptr;
2103 			}
2104 			if (!(*lookup)->rdclassset) {
2105 				(*lookup)->rdclass = dns_rdataclass_in;
2106 			}
2107 			(*lookup)->new_search = true;
2108 			if (*firstarg) {
2109 				printgreeting(argc, argv, *lookup);
2110 				*firstarg = false;
2111 			}
2112 			ISC_LIST_APPEND(lookup_list, *lookup, link);
2113 		} else {
2114 			fprintf(stderr, "Invalid IP address %s\n", value);
2115 			exit(1);
2116 		}
2117 		return (value_from_next);
2118 	invalid_option:
2119 	default:
2120 		fprintf(stderr, "Invalid option: -%s\n", option);
2121 		usage();
2122 	}
2123 	/* NOTREACHED */
2124 	return (false);
2125 }
2126 
2127 /*%
2128  * Because we may be trying to do memory allocation recording, we're going
2129  * to need to parse the arguments for the -m *before* we start the main
2130  * argument parsing routine.
2131  *
2132  * I'd prefer not to have to do this, but I am not quite sure how else to
2133  * fix the problem.  Argument parsing in dig involves memory allocation
2134  * by its nature, so it can't be done in the main argument parser.
2135  */
2136 static void
2137 preparse_args(int argc, char **argv) {
2138 	int rc;
2139 	char **rv;
2140 	char *option;
2141 
2142 	rc = argc;
2143 	rv = argv;
2144 	for (rc--, rv++; rc > 0; rc--, rv++) {
2145 		if (rv[0][0] != '-') {
2146 			continue;
2147 		}
2148 		option = &rv[0][1];
2149 		while (strpbrk(option, single_dash_opts) == &option[0]) {
2150 			switch (option[0]) {
2151 			case 'd':
2152 				/* For debugging early startup */
2153 				debugging = true;
2154 				break;
2155 			case 'm':
2156 				memdebugging = true;
2157 				isc_mem_debugging = ISC_MEM_DEBUGTRACE |
2158 						    ISC_MEM_DEBUGRECORD;
2159 				break;
2160 			case 'r':
2161 				/*
2162 				 * Must be done early, because ~/.digrc
2163 				 * is read before command line parsing
2164 				 */
2165 				debug("digrc (early)");
2166 				digrc = false;
2167 				break;
2168 			case '4':
2169 				if (ipv6only) {
2170 					fatal("only one of -4 and -6 allowed");
2171 				}
2172 				ipv4only = true;
2173 				break;
2174 			case '6':
2175 				if (ipv4only) {
2176 					fatal("only one of -4 and -6 allowed");
2177 				}
2178 				ipv6only = true;
2179 				break;
2180 			}
2181 			option = &option[1];
2182 		}
2183 		if (strlen(option) == 0U) {
2184 			continue;
2185 		}
2186 		/* Look for dash value option. */
2187 		if (strpbrk(option, dash_opts) != &option[0] ||
2188 		    strlen(option) > 1U) {
2189 			/* Error or value in option. */
2190 			continue;
2191 		}
2192 		/* Dash value is next argument so we need to skip it. */
2193 		rc--, rv++;
2194 		/* Handle missing argument */
2195 		if (rc == 0) {
2196 			break;
2197 		}
2198 	}
2199 }
2200 
2201 static int
2202 split_batchline(char *batchline, char **bargv, int len, const char *msg) {
2203 	int bargc;
2204 	char *last = NULL;
2205 
2206 	REQUIRE(batchline != NULL);
2207 
2208 	for (bargc = 1, bargv[bargc] = strtok_r(batchline, " \t\r\n", &last);
2209 	     bargc < len && bargv[bargc];
2210 	     bargv[++bargc] = strtok_r(NULL, " \t\r\n", &last))
2211 	{
2212 		debug("%s %d: %s", msg, bargc, bargv[bargc]);
2213 	}
2214 	return (bargc);
2215 }
2216 
2217 static void
2218 parse_args(bool is_batchfile, bool config_only, int argc, char **argv) {
2219 	isc_result_t result;
2220 	isc_textregion_t tr;
2221 	bool firstarg = true;
2222 	dig_lookup_t *lookup = NULL;
2223 	dns_rdatatype_t rdtype;
2224 	dns_rdataclass_t rdclass;
2225 	bool open_type_class = true;
2226 	char batchline[MXNAME];
2227 	int bargc;
2228 	char *bargv[64];
2229 	int rc;
2230 	char **rv;
2231 #ifndef NOPOSIX
2232 	char *homedir;
2233 	char rcfile[PATH_MAX];
2234 #endif /* ifndef NOPOSIX */
2235 	bool need_clone = true;
2236 
2237 	/*
2238 	 * The semantics for parsing the args is a bit complex; if
2239 	 * we don't have a host yet, make the arg apply globally,
2240 	 * otherwise make it apply to the latest host.  This is
2241 	 * a bit different than the previous versions, but should
2242 	 * form a consistent user interface.
2243 	 *
2244 	 * First, create a "default lookup" which won't actually be used
2245 	 * anywhere, except for cloning into new lookups
2246 	 */
2247 
2248 	debug("parse_args()");
2249 	if (!is_batchfile) {
2250 		debug("making new lookup");
2251 		default_lookup = make_empty_lookup();
2252 		default_lookup->adflag = true;
2253 		default_lookup->edns = 0;
2254 		default_lookup->sendcookie = true;
2255 
2256 #ifndef NOPOSIX
2257 		/*
2258 		 * Treat ${HOME}/.digrc as a special batchfile
2259 		 */
2260 		INSIST(batchfp == NULL);
2261 		homedir = getenv("HOME");
2262 		if (homedir != NULL && digrc) {
2263 			unsigned int n;
2264 			debug("digrc (open)");
2265 			n = snprintf(rcfile, sizeof(rcfile), "%s/.digrc",
2266 				     homedir);
2267 			if (n < sizeof(rcfile)) {
2268 				batchfp = fopen(rcfile, "r");
2269 			}
2270 		}
2271 		if (batchfp != NULL) {
2272 			while (fgets(batchline, sizeof(batchline), batchfp) !=
2273 			       0) {
2274 				debug("config line %s", batchline);
2275 				bargc = split_batchline(batchline, bargv, 62,
2276 							".digrc argv");
2277 				bargv[0] = argv[0];
2278 				argv0 = argv[0];
2279 				parse_args(true, true, bargc, (char **)bargv);
2280 			}
2281 			fclose(batchfp);
2282 		}
2283 #endif /* ifndef NOPOSIX */
2284 	}
2285 
2286 	if (is_batchfile && !config_only) {
2287 		/* Processing '-f batchfile'. */
2288 		lookup = clone_lookup(default_lookup, true);
2289 		need_clone = false;
2290 	} else {
2291 		lookup = default_lookup;
2292 	}
2293 
2294 	rc = argc;
2295 	rv = argv;
2296 	for (rc--, rv++; rc > 0; rc--, rv++) {
2297 		debug("main parsing %s", rv[0]);
2298 		if (strncmp(rv[0], "%", 1) == 0) {
2299 			break;
2300 		}
2301 		if (rv[0][0] == '@') {
2302 			if (is_batchfile && !config_only) {
2303 				addresscount = getaddresses(lookup, &rv[0][1],
2304 							    &result);
2305 				if (addresscount == 0) {
2306 					fprintf(stderr,
2307 						"couldn't get address "
2308 						"for '%s': %s: skipping "
2309 						"lookup\n",
2310 						&rv[0][1],
2311 						isc_result_totext(result));
2312 					if (ISC_LINK_LINKED(lookup, link)) {
2313 						ISC_LIST_DEQUEUE(lookup_list,
2314 								 lookup, link);
2315 					}
2316 					destroy_lookup(lookup);
2317 					return;
2318 				}
2319 			} else {
2320 				addresscount = getaddresses(lookup, &rv[0][1],
2321 							    NULL);
2322 				if (addresscount == 0) {
2323 					fatal("no valid addresses for '%s'\n",
2324 					      &rv[0][1]);
2325 				}
2326 			}
2327 		} else if (rv[0][0] == '+') {
2328 			plus_option(&rv[0][1], is_batchfile, lookup);
2329 		} else if (rv[0][0] == '-') {
2330 			if (rc <= 1) {
2331 				if (dash_option(&rv[0][1], NULL, &lookup,
2332 						&open_type_class, &need_clone,
2333 						config_only, argc, argv,
2334 						&firstarg))
2335 				{
2336 					rc--;
2337 					rv++;
2338 				}
2339 			} else {
2340 				if (dash_option(&rv[0][1], rv[1], &lookup,
2341 						&open_type_class, &need_clone,
2342 						config_only, argc, argv,
2343 						&firstarg))
2344 				{
2345 					rc--;
2346 					rv++;
2347 				}
2348 			}
2349 		} else {
2350 			/*
2351 			 * Anything which isn't an option
2352 			 */
2353 			if (open_type_class) {
2354 				if (strncasecmp(rv[0], "ixfr=", 5) == 0) {
2355 					rdtype = dns_rdatatype_ixfr;
2356 					result = ISC_R_SUCCESS;
2357 				} else {
2358 					tr.base = rv[0];
2359 					tr.length = (unsigned int)strlen(rv[0]);
2360 					result = dns_rdatatype_fromtext(
2361 						&rdtype,
2362 						(isc_textregion_t *)&tr);
2363 					if (result == ISC_R_SUCCESS &&
2364 					    rdtype == dns_rdatatype_ixfr) {
2365 						fprintf(stderr, ";; Warning, "
2366 								"ixfr requires "
2367 								"a "
2368 								"serial "
2369 								"number\n");
2370 						continue;
2371 					}
2372 				}
2373 				if (result == ISC_R_SUCCESS) {
2374 					if (lookup->rdtypeset) {
2375 						fprintf(stderr, ";; Warning, "
2376 								"extra type "
2377 								"option\n");
2378 					}
2379 					if (rdtype == dns_rdatatype_ixfr) {
2380 						uint32_t serial;
2381 						lookup->rdtype =
2382 							dns_rdatatype_ixfr;
2383 						lookup->rdtypeset = true;
2384 						result = parse_uint(&serial,
2385 								    &rv[0][5],
2386 								    MAXSERIAL,
2387 								    "serial "
2388 								    "number");
2389 						if (result != ISC_R_SUCCESS) {
2390 							fatal("Couldn't parse "
2391 							      "serial number");
2392 						}
2393 						lookup->ixfr_serial = serial;
2394 						lookup->section_question =
2395 							plusquest;
2396 						lookup->comments = pluscomm;
2397 						if (!lookup->tcp_mode_set) {
2398 							lookup->tcp_mode = true;
2399 						}
2400 					} else {
2401 						lookup->rdtype = rdtype;
2402 						lookup->rdtypeset = true;
2403 						if (rdtype ==
2404 						    dns_rdatatype_axfr) {
2405 							lookup->section_question =
2406 								plusquest;
2407 							lookup->comments =
2408 								pluscomm;
2409 						}
2410 						if (rdtype ==
2411 							    dns_rdatatype_any &&
2412 						    !lookup->tcp_mode_set) {
2413 							lookup->tcp_mode = true;
2414 						}
2415 						lookup->ixfr_serial = false;
2416 					}
2417 					continue;
2418 				}
2419 				result = dns_rdataclass_fromtext(
2420 					&rdclass, (isc_textregion_t *)&tr);
2421 				if (result == ISC_R_SUCCESS) {
2422 					if (lookup->rdclassset) {
2423 						fprintf(stderr, ";; Warning, "
2424 								"extra class "
2425 								"option\n");
2426 					}
2427 					lookup->rdclass = rdclass;
2428 					lookup->rdclassset = true;
2429 					continue;
2430 				}
2431 			}
2432 
2433 			if (!config_only) {
2434 				if (need_clone) {
2435 					lookup = clone_lookup(default_lookup,
2436 							      true);
2437 				}
2438 				need_clone = true;
2439 				strlcpy(lookup->textname, rv[0],
2440 					sizeof(lookup->textname));
2441 				lookup->trace_root = (lookup->trace ||
2442 						      lookup->ns_search_only);
2443 				lookup->new_search = true;
2444 				if (firstarg) {
2445 					printgreeting(argc, argv, lookup);
2446 					firstarg = false;
2447 				}
2448 				ISC_LIST_APPEND(lookup_list, lookup, link);
2449 				debug("looking up %s", lookup->textname);
2450 			}
2451 			/* XXX Error message */
2452 		}
2453 	}
2454 
2455 	/*
2456 	 * If we have a batchfile, seed the lookup list with the
2457 	 * first entry, then trust the callback in dighost_shutdown
2458 	 * to get the rest
2459 	 */
2460 	char *filename = (char *)atomic_load(&batchname);
2461 	if ((filename != NULL) && !(is_batchfile)) {
2462 		if (strcmp(filename, "-") == 0) {
2463 			batchfp = stdin;
2464 		} else {
2465 			batchfp = fopen(filename, "r");
2466 		}
2467 		if (batchfp == NULL) {
2468 			perror(filename);
2469 			if (exitcode < 8) {
2470 				exitcode = 8;
2471 			}
2472 			fatal("couldn't open specified batch file");
2473 		}
2474 		/* XXX Remove code dup from shutdown code */
2475 	next_line:
2476 		if (fgets(batchline, sizeof(batchline), batchfp) != 0) {
2477 			debug("batch line %s", batchline);
2478 			if (batchline[0] == '\r' || batchline[0] == '\n' ||
2479 			    batchline[0] == '#' || batchline[0] == ';')
2480 			{
2481 				goto next_line;
2482 			}
2483 			bargc = split_batchline(batchline, bargv, 14,
2484 						"batch argv");
2485 			bargv[0] = argv[0];
2486 			argv0 = argv[0];
2487 			parse_args(true, false, bargc, (char **)bargv);
2488 			return;
2489 		}
2490 		return;
2491 	}
2492 	/*
2493 	 * If no lookup specified, search for root
2494 	 */
2495 	if ((lookup_list.head == NULL) && !config_only) {
2496 		if (need_clone) {
2497 			lookup = clone_lookup(default_lookup, true);
2498 		}
2499 		need_clone = true;
2500 		lookup->trace_root = (lookup->trace || lookup->ns_search_only);
2501 		lookup->new_search = true;
2502 		strlcpy(lookup->textname, ".", sizeof(lookup->textname));
2503 		lookup->rdtype = dns_rdatatype_ns;
2504 		lookup->rdtypeset = true;
2505 		if (firstarg) {
2506 			printgreeting(argc, argv, lookup);
2507 			firstarg = false;
2508 		}
2509 		ISC_LIST_APPEND(lookup_list, lookup, link);
2510 	}
2511 	if (!need_clone) {
2512 		destroy_lookup(lookup);
2513 	}
2514 }
2515 
2516 /*
2517  * Callback from dighost.c to allow program-specific shutdown code.
2518  * Here, we're possibly reading from a batch file, then shutting down
2519  * for real if there's nothing in the batch file to read.
2520  */
2521 static void
2522 query_finished(void) {
2523 	char batchline[MXNAME];
2524 	int bargc;
2525 	char *bargv[16];
2526 
2527 	if (atomic_load(&batchname) == 0) {
2528 		isc_app_shutdown();
2529 		return;
2530 	}
2531 
2532 	fflush(stdout);
2533 	if (feof(batchfp)) {
2534 		atomic_store(&batchname, 0);
2535 		isc_app_shutdown();
2536 		if (batchfp != stdin) {
2537 			fclose(batchfp);
2538 		}
2539 		return;
2540 	}
2541 
2542 	if (fgets(batchline, sizeof(batchline), batchfp) != 0) {
2543 		debug("batch line %s", batchline);
2544 		bargc = split_batchline(batchline, bargv, 14, "batch argv");
2545 		bargv[0] = argv0;
2546 		parse_args(true, false, bargc, (char **)bargv);
2547 		start_lookup();
2548 	} else {
2549 		atomic_store(&batchname, 0);
2550 		if (batchfp != stdin) {
2551 			fclose(batchfp);
2552 		}
2553 		isc_app_shutdown();
2554 		return;
2555 	}
2556 }
2557 
2558 static void
2559 dig_error(const char *format, ...) {
2560 	va_list args;
2561 
2562 	if (yaml) {
2563 		printf("-\n");
2564 		printf("  type: DIG_ERROR\n");
2565 
2566 		/*
2567 		 * Print an indent before a literal block quote.
2568 		 * Note: this will break if used to print more than
2569 		 * one line of text as only the first line would be
2570 		 * indented.
2571 		 */
2572 		printf("  message: |\n");
2573 		printf("    ");
2574 	} else {
2575 		printf(";; ");
2576 	}
2577 
2578 	va_start(args, format);
2579 	vprintf(format, args);
2580 	va_end(args);
2581 
2582 	if (!yaml) {
2583 		printf("\n");
2584 	}
2585 }
2586 
2587 static void
2588 dig_warning(const char *format, ...) {
2589 	va_list args;
2590 
2591 	if (!yaml) {
2592 		printf(";; ");
2593 
2594 		va_start(args, format);
2595 		vprintf(format, args);
2596 		va_end(args);
2597 
2598 		printf("\n");
2599 	}
2600 }
2601 
2602 static void
2603 dig_comments(dig_lookup_t *lookup, const char *format, ...) {
2604 	va_list args;
2605 
2606 	if (lookup->comments && !yaml) {
2607 		printf(";; ");
2608 
2609 		va_start(args, format);
2610 		vprintf(format, args);
2611 		va_end(args);
2612 
2613 		printf("\n");
2614 	}
2615 }
2616 
2617 void
2618 dig_setup(int argc, char **argv) {
2619 	isc_result_t result;
2620 
2621 	ISC_LIST_INIT(lookup_list);
2622 	ISC_LIST_INIT(server_list);
2623 	ISC_LIST_INIT(search_list);
2624 
2625 	debug("dig_setup()");
2626 
2627 	/* setup dighost callbacks */
2628 	dighost_printmessage = printmessage;
2629 	dighost_received = received;
2630 	dighost_trying = trying;
2631 	dighost_shutdown = query_finished;
2632 	dighost_error = dig_error;
2633 	dighost_warning = dig_warning;
2634 	dighost_comments = dig_comments;
2635 
2636 	progname = argv[0];
2637 	preparse_args(argc, argv);
2638 
2639 	result = isc_app_start();
2640 	check_result(result, "isc_app_start");
2641 
2642 	setup_libs();
2643 	setup_system(ipv4only, ipv6only);
2644 }
2645 
2646 void
2647 dig_query_setup(bool is_batchfile, bool config_only, int argc, char **argv) {
2648 	debug("dig_query_setup");
2649 
2650 	parse_args(is_batchfile, config_only, argc, argv);
2651 	if (keyfile[0] != 0) {
2652 		setup_file_key();
2653 	} else if (keysecret[0] != 0) {
2654 		setup_text_key();
2655 	}
2656 	if (domainopt[0] != '\0') {
2657 		set_search_domain(domainopt);
2658 		usesearch = true;
2659 	}
2660 }
2661 
2662 void
2663 dig_startup() {
2664 	isc_result_t result;
2665 
2666 	debug("dig_startup()");
2667 
2668 	result = isc_app_onrun(mctx, global_task, onrun_callback, NULL);
2669 	check_result(result, "isc_app_onrun");
2670 	isc_app_run();
2671 }
2672 
2673 void
2674 dig_query_start() {
2675 	start_lookup();
2676 }
2677 
2678 void
2679 dig_shutdown() {
2680 	destroy_lookup(default_lookup);
2681 	if (atomic_load(&batchname) != 0) {
2682 		if (batchfp != stdin) {
2683 			fclose(batchfp);
2684 		}
2685 		atomic_store(&batchname, 0);
2686 	}
2687 	cancel_all();
2688 	destroy_libs();
2689 	isc_app_finish();
2690 }
2691 
2692 /*% Main processing routine for dig */
2693 int
2694 main(int argc, char **argv) {
2695 	dig_setup(argc, argv);
2696 	dig_query_setup(false, false, argc, argv);
2697 	dig_startup();
2698 	dig_shutdown();
2699 
2700 	return (exitcode);
2701 }
2702