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