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