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