Home | History | Annotate | Line # | Download | only in tools
mdig.c revision 1.6
      1 /*	$NetBSD: mdig.c,v 1.6 2020/05/24 19:46:19 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  *
      6  * This Source Code Form is subject to the terms of the Mozilla Public
      7  * License, v. 2.0. If a copy of the MPL was not distributed with this
      8  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
      9  *
     10  * See the COPYRIGHT file distributed with this work for additional
     11  * information regarding copyright ownership.
     12  */
     13 
     14 #include <inttypes.h>
     15 #include <stdbool.h>
     16 #include <stdlib.h>
     17 #include <string.h>
     18 #include <unistd.h>
     19 
     20 #include <isc/app.h>
     21 #include <isc/base64.h>
     22 #include <isc/hash.h>
     23 #include <isc/hex.h>
     24 #include <isc/log.h>
     25 #include <isc/mem.h>
     26 #include <isc/net.h>
     27 #include <isc/nonce.h>
     28 #include <isc/parseint.h>
     29 #include <isc/print.h>
     30 #include <isc/random.h>
     31 #include <isc/sockaddr.h>
     32 #include <isc/socket.h>
     33 #include <isc/string.h>
     34 #include <isc/task.h>
     35 #include <isc/timer.h>
     36 #include <isc/util.h>
     37 
     38 #include <dns/byaddr.h>
     39 #include <dns/dispatch.h>
     40 #include <dns/events.h>
     41 #include <dns/fixedname.h>
     42 #include <dns/message.h>
     43 #include <dns/name.h>
     44 #include <dns/rdata.h>
     45 #include <dns/rdataclass.h>
     46 #include <dns/rdataset.h>
     47 #include <dns/rdatatype.h>
     48 #include <dns/request.h>
     49 #include <dns/resolver.h>
     50 #include <dns/result.h>
     51 #include <dns/types.h>
     52 #include <dns/view.h>
     53 
     54 #include <dst/result.h>
     55 
     56 #include <bind9/getaddresses.h>
     57 
     58 #define CHECK(str, x)                                                       \
     59 	{                                                                   \
     60 		if ((x) != ISC_R_SUCCESS) {                                 \
     61 			fprintf(stderr, "mdig: %s failed with %s\n", (str), \
     62 				isc_result_totext(x));                      \
     63 			exit(-1);                                           \
     64 		}                                                           \
     65 	}
     66 
     67 #define RUNCHECK(x) RUNTIME_CHECK((x) == ISC_R_SUCCESS)
     68 
     69 #define ADD_STRING(b, s)                                        \
     70 	{                                                       \
     71 		if (strlen(s) >= isc_buffer_availablelength(b)) \
     72 			return ((ISC_R_NOSPACE));               \
     73 		else                                            \
     74 			isc_buffer_putstr(b, s);                \
     75 	}
     76 
     77 #define MXNAME	   (DNS_NAME_MAXTEXT + 1)
     78 #define COMMSIZE   0xffff
     79 #define OUTPUTBUF  32767
     80 #define MAXPORT	   0xffff
     81 #define PORT	   53
     82 #define MAXTIMEOUT 0xffff
     83 #define TCPTIMEOUT 10
     84 #define UDPTIMEOUT 5
     85 #define MAXTRIES   0xffffffff
     86 
     87 static isc_mem_t *mctx;
     88 static dns_requestmgr_t *requestmgr;
     89 static const char *batchname;
     90 static FILE *batchfp;
     91 static bool have_ipv4 = false;
     92 static bool have_ipv6 = false;
     93 static bool have_src = false;
     94 static bool tcp_mode = false;
     95 static bool besteffort = true;
     96 static bool display_short_form = false;
     97 static bool display_headers = true;
     98 static bool display_comments = true;
     99 static int display_rrcomments = 0;
    100 static bool display_ttlunits = true;
    101 static bool display_ttl = true;
    102 static bool display_class = true;
    103 static bool display_crypto = true;
    104 static bool display_multiline = false;
    105 static bool display_question = true;
    106 static bool display_answer = true;
    107 static bool display_authority = true;
    108 static bool display_additional = true;
    109 static bool display_unknown_format = false;
    110 static bool yaml = false;
    111 static bool continue_on_error = false;
    112 static uint32_t display_splitwidth = 0xffffffff;
    113 static isc_sockaddr_t srcaddr;
    114 static char *server;
    115 static isc_sockaddr_t dstaddr;
    116 static in_port_t port = 53;
    117 static isc_dscp_t dscp = -1;
    118 static unsigned char cookie_secret[33];
    119 static int onfly = 0;
    120 static char hexcookie[81];
    121 
    122 struct query {
    123 	char textname[MXNAME]; /*% Name we're going to be
    124 				* looking up */
    125 	bool recurse;
    126 	bool have_aaonly;
    127 	bool have_adflag;
    128 	bool have_cdflag;
    129 	bool have_zflag;
    130 	bool dnssec;
    131 	bool expire;
    132 	bool send_cookie;
    133 	char *cookie;
    134 	bool nsid;
    135 	dns_rdatatype_t rdtype;
    136 	dns_rdataclass_t rdclass;
    137 	uint16_t udpsize;
    138 	int16_t edns;
    139 	dns_ednsopt_t *ednsopts;
    140 	unsigned int ednsoptscnt;
    141 	unsigned int ednsflags;
    142 	isc_sockaddr_t *ecs_addr;
    143 	unsigned int timeout;
    144 	unsigned int udptimeout;
    145 	unsigned int udpretries;
    146 	ISC_LINK(struct query) link;
    147 };
    148 static struct query default_query;
    149 static ISC_LIST(struct query) queries;
    150 
    151 #define EDNSOPTS 100U
    152 /*% opcode text */
    153 static const char *const opcodetext[] = {
    154 	"QUERY",      "IQUERY",	    "STATUS",	  "RESERVED3",
    155 	"NOTIFY",     "UPDATE",	    "RESERVED6",  "RESERVED7",
    156 	"RESERVED8",  "RESERVED9",  "RESERVED10", "RESERVED11",
    157 	"RESERVED12", "RESERVED13", "RESERVED14", "RESERVED15"
    158 };
    159 
    160 /*% return code text */
    161 static const char *const rcodetext[] = {
    162 	"NOERROR",    "FORMERR",    "SERVFAIL",	  "NXDOMAIN",	"NOTIMP",
    163 	"REFUSED",    "YXDOMAIN",   "YXRRSET",	  "NXRRSET",	"NOTAUTH",
    164 	"NOTZONE",    "RESERVED11", "RESERVED12", "RESERVED13", "RESERVED14",
    165 	"RESERVED15", "BADVERS"
    166 };
    167 
    168 /*% safe rcodetext[] */
    169 static char *
    170 rcode_totext(dns_rcode_t rcode) {
    171 	static char buf[sizeof("?65535")];
    172 	union {
    173 		const char *consttext;
    174 		char *deconsttext;
    175 	} totext;
    176 
    177 	if (rcode >= (sizeof(rcodetext) / sizeof(rcodetext[0]))) {
    178 		snprintf(buf, sizeof(buf), "?%u", rcode);
    179 		totext.deconsttext = buf;
    180 	} else {
    181 		totext.consttext = rcodetext[rcode];
    182 	}
    183 	return (totext.deconsttext);
    184 }
    185 
    186 /* receive response event handler */
    187 static void
    188 recvresponse(isc_task_t *task, isc_event_t *event) {
    189 	dns_requestevent_t *reqev = (dns_requestevent_t *)event;
    190 	isc_result_t result;
    191 	dns_message_t *query = NULL, *response = NULL;
    192 	unsigned int parseflags = 0;
    193 	isc_buffer_t *msgbuf = NULL, *buf = NULL;
    194 	unsigned int len = OUTPUTBUF;
    195 	dns_master_style_t *style = NULL;
    196 	unsigned int styleflags = 0;
    197 	dns_messagetextflag_t flags;
    198 
    199 	UNUSED(task);
    200 
    201 	REQUIRE(reqev != NULL);
    202 	query = reqev->ev_arg;
    203 
    204 	if (reqev->result != ISC_R_SUCCESS) {
    205 		fprintf(stderr, "response failed with %s\n",
    206 			isc_result_totext(reqev->result));
    207 		if (continue_on_error) {
    208 			goto cleanup;
    209 		} else {
    210 			exit(-1);
    211 		}
    212 	}
    213 
    214 	result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE, &response);
    215 	CHECK("dns_message_create", result);
    216 
    217 	parseflags |= DNS_MESSAGEPARSE_PRESERVEORDER;
    218 	if (besteffort) {
    219 		parseflags |= DNS_MESSAGEPARSE_BESTEFFORT;
    220 		parseflags |= DNS_MESSAGEPARSE_IGNORETRUNCATION;
    221 	}
    222 
    223 	msgbuf = dns_request_getanswer(reqev->request);
    224 	result = dns_request_getresponse(reqev->request, response, parseflags);
    225 	CHECK("dns_request_getresponse", result);
    226 
    227 	styleflags |= DNS_STYLEFLAG_REL_OWNER;
    228 	if (yaml) {
    229 		styleflags |= DNS_STYLEFLAG_YAML;
    230 		response->indent.string = "  ";
    231 		response->indent.count = 3;
    232 	} else {
    233 		if (display_comments) {
    234 			styleflags |= DNS_STYLEFLAG_COMMENT;
    235 		}
    236 		if (display_unknown_format) {
    237 			styleflags |= DNS_STYLEFLAG_UNKNOWNFORMAT;
    238 		}
    239 		if (display_rrcomments > 0) {
    240 			styleflags |= DNS_STYLEFLAG_RRCOMMENT;
    241 		}
    242 		if (display_ttlunits) {
    243 			styleflags |= DNS_STYLEFLAG_TTL_UNITS;
    244 		}
    245 		if (!display_ttl) {
    246 			styleflags |= DNS_STYLEFLAG_NO_TTL;
    247 		}
    248 		if (!display_class) {
    249 			styleflags |= DNS_STYLEFLAG_NO_CLASS;
    250 		}
    251 		if (!display_crypto) {
    252 			styleflags |= DNS_STYLEFLAG_NOCRYPTO;
    253 		}
    254 		if (display_multiline) {
    255 			styleflags |= DNS_STYLEFLAG_OMIT_OWNER;
    256 			styleflags |= DNS_STYLEFLAG_OMIT_CLASS;
    257 			styleflags |= DNS_STYLEFLAG_REL_DATA;
    258 			styleflags |= DNS_STYLEFLAG_OMIT_TTL;
    259 			styleflags |= DNS_STYLEFLAG_TTL;
    260 			styleflags |= DNS_STYLEFLAG_MULTILINE;
    261 			styleflags |= DNS_STYLEFLAG_COMMENT;
    262 			/* Turn on rrcomments unless explicitly disabled */
    263 			if (display_rrcomments >= 0) {
    264 				styleflags |= DNS_STYLEFLAG_RRCOMMENT;
    265 			}
    266 		}
    267 	}
    268 	if (display_multiline || (!display_ttl && !display_class)) {
    269 		result = dns_master_stylecreate(&style, styleflags, 24, 24, 24,
    270 						32, 80, 8, display_splitwidth,
    271 						mctx);
    272 	} else if (!display_ttl || !display_class) {
    273 		result = dns_master_stylecreate(&style, styleflags, 24, 24, 32,
    274 						40, 80, 8, display_splitwidth,
    275 						mctx);
    276 	} else {
    277 		result = dns_master_stylecreate(&style, styleflags, 24, 32, 40,
    278 						48, 80, 8, display_splitwidth,
    279 						mctx);
    280 	}
    281 	CHECK("dns_master_stylecreate2", result);
    282 
    283 	flags = 0;
    284 	if (!display_headers) {
    285 		flags |= DNS_MESSAGETEXTFLAG_NOHEADERS;
    286 		flags |= DNS_MESSAGETEXTFLAG_NOCOMMENTS;
    287 	}
    288 	if (!display_comments) {
    289 		flags |= DNS_MESSAGETEXTFLAG_NOCOMMENTS;
    290 	}
    291 
    292 	isc_buffer_allocate(mctx, &buf, len);
    293 
    294 	if (yaml) {
    295 		char sockstr[ISC_SOCKADDR_FORMATSIZE];
    296 		uint16_t sport;
    297 		char *hash;
    298 		int pf;
    299 
    300 		printf("-\n");
    301 		printf("  type: MESSAGE\n");
    302 		printf("  message:\n");
    303 
    304 		if (((response->flags & DNS_MESSAGEFLAG_RD) != 0) &&
    305 		    ((response->flags & DNS_MESSAGEFLAG_RA) != 0))
    306 		{
    307 			printf("    type: RECURSIVE_RESPONSE\n");
    308 		} else {
    309 			printf("    type: AUTH_RESPONSE\n");
    310 		}
    311 
    312 		printf("    message_size: %ub\n",
    313 		       isc_buffer_usedlength(msgbuf));
    314 
    315 		pf = isc_sockaddr_pf(&dstaddr);
    316 		if (pf == PF_INET || pf == PF_INET6) {
    317 			printf("    socket_family: %s\n",
    318 			       pf == PF_INET ? "INET" : "INET6");
    319 
    320 			printf("    socket_protocol: %s\n",
    321 			       tcp_mode ? "TCP" : "UDP");
    322 
    323 			sport = isc_sockaddr_getport(&dstaddr);
    324 			isc_sockaddr_format(&dstaddr, sockstr, sizeof(sockstr));
    325 			hash = strchr(sockstr, '#');
    326 			if (hash != NULL) {
    327 				*hash = '\0';
    328 			}
    329 			printf("    response_address: %s\n", sockstr);
    330 			printf("    response_port: %u\n", sport);
    331 		}
    332 
    333 		if (have_src) {
    334 			sport = isc_sockaddr_getport(&srcaddr);
    335 			isc_sockaddr_format(&srcaddr, sockstr, sizeof(sockstr));
    336 			hash = strchr(sockstr, '#');
    337 			if (hash != NULL) {
    338 				*hash = '\0';
    339 			}
    340 			printf("    query_address: %s\n", sockstr);
    341 			printf("    query_port: %u\n", sport);
    342 		}
    343 
    344 		printf("    %s:\n", "response_message_data");
    345 		result = dns_message_headertotext(response, style, flags, buf);
    346 		CHECK("dns_message_headertotext", result);
    347 	} else if (display_comments && !display_short_form) {
    348 		printf(";; Got answer:\n");
    349 
    350 		if (display_headers) {
    351 			printf(";; ->>HEADER<<- opcode: %s, status: %s, "
    352 			       "id: %u\n",
    353 			       opcodetext[response->opcode],
    354 			       rcode_totext(response->rcode), response->id);
    355 			printf(";; flags:");
    356 			if ((response->flags & DNS_MESSAGEFLAG_QR) != 0) {
    357 				printf(" qr");
    358 			}
    359 			if ((response->flags & DNS_MESSAGEFLAG_AA) != 0) {
    360 				printf(" aa");
    361 			}
    362 			if ((response->flags & DNS_MESSAGEFLAG_TC) != 0) {
    363 				printf(" tc");
    364 			}
    365 			if ((response->flags & DNS_MESSAGEFLAG_RD) != 0) {
    366 				printf(" rd");
    367 			}
    368 			if ((response->flags & DNS_MESSAGEFLAG_RA) != 0) {
    369 				printf(" ra");
    370 			}
    371 			if ((response->flags & DNS_MESSAGEFLAG_AD) != 0) {
    372 				printf(" ad");
    373 			}
    374 			if ((response->flags & DNS_MESSAGEFLAG_CD) != 0) {
    375 				printf(" cd");
    376 			}
    377 			if ((response->flags & 0x0040U) != 0) {
    378 				printf("; MBZ: 0x4");
    379 			}
    380 
    381 			printf("; QUERY: %u, ANSWER: %u, "
    382 			       "AUTHORITY: %u, ADDITIONAL: %u\n",
    383 			       response->counts[DNS_SECTION_QUESTION],
    384 			       response->counts[DNS_SECTION_ANSWER],
    385 			       response->counts[DNS_SECTION_AUTHORITY],
    386 			       response->counts[DNS_SECTION_ADDITIONAL]);
    387 
    388 			if ((response->flags & DNS_MESSAGEFLAG_RD) != 0 &&
    389 			    (response->flags & DNS_MESSAGEFLAG_RA) == 0)
    390 			{
    391 				printf(";; WARNING: recursion requested "
    392 				       "but not available\n");
    393 			}
    394 		}
    395 	}
    396 
    397 repopulate_buffer:
    398 
    399 	if (display_comments && display_headers && !display_short_form) {
    400 		result = dns_message_pseudosectiontotext(
    401 			response, DNS_PSEUDOSECTION_OPT, style, flags, buf);
    402 		if (result == ISC_R_NOSPACE) {
    403 		buftoosmall:
    404 			len += OUTPUTBUF;
    405 			isc_buffer_free(&buf);
    406 			isc_buffer_allocate(mctx, &buf, len);
    407 			goto repopulate_buffer;
    408 		}
    409 		CHECK("dns_message_pseudosectiontotext", result);
    410 	}
    411 
    412 	if (display_question && display_headers && !display_short_form) {
    413 		result = dns_message_sectiontotext(
    414 			response, DNS_SECTION_QUESTION, style, flags, buf);
    415 		if (result == ISC_R_NOSPACE) {
    416 			goto buftoosmall;
    417 		}
    418 		CHECK("dns_message_sectiontotext", result);
    419 	}
    420 
    421 	if (display_answer && !display_short_form) {
    422 		result = dns_message_sectiontotext(response, DNS_SECTION_ANSWER,
    423 						   style, flags, buf);
    424 		if (result == ISC_R_NOSPACE) {
    425 			goto buftoosmall;
    426 		}
    427 		CHECK("dns_message_sectiontotext", result);
    428 	} else if (display_answer) {
    429 		dns_name_t *name;
    430 		dns_rdataset_t *rdataset;
    431 		isc_result_t loopresult;
    432 		dns_name_t empty_name;
    433 		dns_rdata_t rdata = DNS_RDATA_INIT;
    434 		unsigned int answerstyleflags = 0;
    435 
    436 		if (!display_crypto) {
    437 			answerstyleflags |= DNS_STYLEFLAG_NOCRYPTO;
    438 		}
    439 		if (display_unknown_format) {
    440 			answerstyleflags |= DNS_STYLEFLAG_UNKNOWNFORMAT;
    441 		}
    442 
    443 		dns_name_init(&empty_name, NULL);
    444 		result = dns_message_firstname(response, DNS_SECTION_ANSWER);
    445 		if (result != ISC_R_NOMORE) {
    446 			CHECK("dns_message_firstname", result);
    447 		}
    448 
    449 		for (;;) {
    450 			if (result == ISC_R_NOMORE) {
    451 				break;
    452 			}
    453 			CHECK("dns_message_nextname", result);
    454 			name = NULL;
    455 			dns_message_currentname(response, DNS_SECTION_ANSWER,
    456 						&name);
    457 
    458 			for (rdataset = ISC_LIST_HEAD(name->list);
    459 			     rdataset != NULL;
    460 			     rdataset = ISC_LIST_NEXT(rdataset, link))
    461 			{
    462 				loopresult = dns_rdataset_first(rdataset);
    463 				while (loopresult == ISC_R_SUCCESS) {
    464 					dns_rdataset_current(rdataset, &rdata);
    465 					result = dns_rdata_tofmttext(
    466 						&rdata, NULL, answerstyleflags,
    467 						0, 60, " ", buf);
    468 					if (result == ISC_R_NOSPACE) {
    469 						goto buftoosmall;
    470 					}
    471 					CHECK("dns_rdata_tofmttext", result);
    472 					loopresult =
    473 						dns_rdataset_next(rdataset);
    474 					dns_rdata_reset(&rdata);
    475 					if (strlen("\n") >=
    476 					    isc_buffer_availablelength(buf)) {
    477 						goto buftoosmall;
    478 					}
    479 					isc_buffer_putstr(buf, "\n");
    480 				}
    481 			}
    482 			result = dns_message_nextname(response,
    483 						      DNS_SECTION_ANSWER);
    484 		}
    485 	}
    486 
    487 	if (display_authority && !display_short_form) {
    488 		result = dns_message_sectiontotext(
    489 			response, DNS_SECTION_AUTHORITY, style, flags, buf);
    490 		if (result == ISC_R_NOSPACE) {
    491 			goto buftoosmall;
    492 		}
    493 		CHECK("dns_message_sectiontotext", result);
    494 	}
    495 
    496 	if (display_additional && !display_short_form) {
    497 		result = dns_message_sectiontotext(
    498 			response, DNS_SECTION_ADDITIONAL, style, flags, buf);
    499 		if (result == ISC_R_NOSPACE) {
    500 			goto buftoosmall;
    501 		}
    502 		CHECK("dns_message_sectiontotext", result);
    503 	}
    504 
    505 	if (display_additional && !display_short_form && display_headers) {
    506 		/*
    507 		 * Only print the signature on the first record.
    508 		 */
    509 		result = dns_message_pseudosectiontotext(
    510 			response, DNS_PSEUDOSECTION_TSIG, style, flags, buf);
    511 		if (result == ISC_R_NOSPACE) {
    512 			goto buftoosmall;
    513 		}
    514 		CHECK("dns_message_pseudosectiontotext", result);
    515 		result = dns_message_pseudosectiontotext(
    516 			response, DNS_PSEUDOSECTION_SIG0, style, flags, buf);
    517 		if (result == ISC_R_NOSPACE) {
    518 			goto buftoosmall;
    519 		}
    520 		CHECK("dns_message_pseudosectiontotext", result);
    521 	}
    522 
    523 	if (display_headers && display_comments && !display_short_form && !yaml)
    524 	{
    525 		printf("\n");
    526 	}
    527 
    528 	printf("%.*s", (int)isc_buffer_usedlength(buf),
    529 	       (char *)isc_buffer_base(buf));
    530 	isc_buffer_free(&buf);
    531 
    532 cleanup:
    533 	fflush(stdout);
    534 	if (style != NULL) {
    535 		dns_master_styledestroy(&style, mctx);
    536 	}
    537 	if (query != NULL) {
    538 		dns_message_destroy(&query);
    539 	}
    540 	if (response != NULL) {
    541 		dns_message_destroy(&response);
    542 	}
    543 	dns_request_destroy(&reqev->request);
    544 	isc_event_free(&event);
    545 
    546 	if (--onfly == 0) {
    547 		isc_app_shutdown();
    548 	}
    549 	return;
    550 }
    551 
    552 /*%
    553  * Add EDNS0 option record to a message.  Currently, the only supported
    554  * options are UDP buffer size, the DO bit, and EDNS options
    555  * (e.g., NSID, COOKIE, client-subnet)
    556  */
    557 static void
    558 add_opt(dns_message_t *msg, uint16_t udpsize, uint16_t edns, unsigned int flags,
    559 	dns_ednsopt_t *opts, size_t count) {
    560 	dns_rdataset_t *rdataset = NULL;
    561 	isc_result_t result;
    562 
    563 	result = dns_message_buildopt(msg, &rdataset, edns, udpsize, flags,
    564 				      opts, count);
    565 	CHECK("dns_message_buildopt", result);
    566 	result = dns_message_setopt(msg, rdataset);
    567 	CHECK("dns_message_setopt", result);
    568 }
    569 
    570 static void
    571 compute_cookie(unsigned char *cookie, size_t len) {
    572 	/* XXXMPA need to fix, should be per server. */
    573 	INSIST(len >= 8U);
    574 	memmove(cookie, cookie_secret, 8);
    575 }
    576 
    577 static isc_result_t
    578 sendquery(struct query *query, isc_task_t *task) {
    579 	dns_request_t *request;
    580 	dns_message_t *message;
    581 	dns_name_t *qname;
    582 	dns_rdataset_t *qrdataset;
    583 	isc_result_t result;
    584 	dns_fixedname_t queryname;
    585 	isc_buffer_t buf;
    586 	unsigned int options;
    587 
    588 	onfly++;
    589 
    590 	dns_fixedname_init(&queryname);
    591 	isc_buffer_init(&buf, query->textname, strlen(query->textname));
    592 	isc_buffer_add(&buf, strlen(query->textname));
    593 	result = dns_name_fromtext(dns_fixedname_name(&queryname), &buf,
    594 				   dns_rootname, 0, NULL);
    595 	CHECK("dns_name_fromtext", result);
    596 
    597 	message = NULL;
    598 	result = dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER, &message);
    599 	CHECK("dns_message_create", result);
    600 
    601 	message->opcode = dns_opcode_query;
    602 	if (query->recurse) {
    603 		message->flags |= DNS_MESSAGEFLAG_RD;
    604 	}
    605 	if (query->have_aaonly) {
    606 		message->flags |= DNS_MESSAGEFLAG_AA;
    607 	}
    608 	if (query->have_adflag) {
    609 		message->flags |= DNS_MESSAGEFLAG_AD;
    610 	}
    611 	if (query->have_cdflag) {
    612 		message->flags |= DNS_MESSAGEFLAG_CD;
    613 	}
    614 	if (query->have_zflag) {
    615 		message->flags |= 0x0040U;
    616 	}
    617 	message->rdclass = query->rdclass;
    618 	message->id = (unsigned short)(random() & 0xFFFF);
    619 
    620 	qname = NULL;
    621 	result = dns_message_gettempname(message, &qname);
    622 	CHECK("dns_message_gettempname", result);
    623 
    624 	qrdataset = NULL;
    625 	result = dns_message_gettemprdataset(message, &qrdataset);
    626 	CHECK("dns_message_gettemprdataset", result);
    627 
    628 	dns_name_init(qname, NULL);
    629 	dns_name_clone(dns_fixedname_name(&queryname), qname);
    630 	dns_rdataset_makequestion(qrdataset, query->rdclass, query->rdtype);
    631 	ISC_LIST_APPEND(qname->list, qrdataset, link);
    632 	dns_message_addname(message, qname, DNS_SECTION_QUESTION);
    633 
    634 	if (query->udpsize > 0 || query->dnssec || query->edns > -1 ||
    635 	    query->ecs_addr != NULL)
    636 	{
    637 		dns_ednsopt_t opts[EDNSOPTS + DNS_EDNSOPTIONS];
    638 		unsigned int flags;
    639 		int i = 0;
    640 		char ecsbuf[20];
    641 		unsigned char cookie[40];
    642 
    643 		if (query->udpsize == 0) {
    644 			query->udpsize = 4096;
    645 		}
    646 		if (query->edns < 0) {
    647 			query->edns = 0;
    648 		}
    649 
    650 		if (query->nsid) {
    651 			INSIST(i < DNS_EDNSOPTIONS);
    652 			opts[i].code = DNS_OPT_NSID;
    653 			opts[i].length = 0;
    654 			opts[i].value = NULL;
    655 			i++;
    656 		}
    657 
    658 		if (query->ecs_addr != NULL) {
    659 			uint8_t addr[16], family;
    660 			uint32_t plen;
    661 			struct sockaddr *sa;
    662 			struct sockaddr_in *sin;
    663 			struct sockaddr_in6 *sin6;
    664 			size_t addrl;
    665 			isc_buffer_t b;
    666 
    667 			sa = &query->ecs_addr->type.sa;
    668 			plen = query->ecs_addr->length;
    669 
    670 			/* Round up prefix len to a multiple of 8 */
    671 			addrl = (plen + 7) / 8;
    672 
    673 			INSIST(i < DNS_EDNSOPTIONS);
    674 			opts[i].code = DNS_OPT_CLIENT_SUBNET;
    675 			opts[i].length = (uint16_t)addrl + 4;
    676 			CHECK("isc_buffer_allocate", result);
    677 			isc_buffer_init(&b, ecsbuf, sizeof(ecsbuf));
    678 			if (sa->sa_family == AF_INET) {
    679 				family = 1;
    680 				sin = (struct sockaddr_in *)sa;
    681 				memmove(addr, &sin->sin_addr, 4);
    682 				if ((plen % 8) != 0) {
    683 					addr[addrl - 1] &= ~0U
    684 							   << (8 - (plen % 8));
    685 				}
    686 			} else {
    687 				family = 2;
    688 				sin6 = (struct sockaddr_in6 *)sa;
    689 				memmove(addr, &sin6->sin6_addr, 16);
    690 			}
    691 
    692 			/* Mask off last address byte */
    693 			if (addrl > 0 && (plen % 8) != 0) {
    694 				addr[addrl - 1] &= ~0U << (8 - (plen % 8));
    695 			}
    696 
    697 			/* family */
    698 			isc_buffer_putuint16(&b, family);
    699 			/* source prefix-length */
    700 			isc_buffer_putuint8(&b, plen);
    701 			/* scope prefix-length */
    702 			isc_buffer_putuint8(&b, 0);
    703 			/* address */
    704 			if (addrl > 0) {
    705 				isc_buffer_putmem(&b, addr, (unsigned)addrl);
    706 			}
    707 
    708 			opts[i].value = (uint8_t *)ecsbuf;
    709 			i++;
    710 		}
    711 
    712 		if (query->send_cookie) {
    713 			INSIST(i < DNS_EDNSOPTIONS);
    714 			opts[i].code = DNS_OPT_COOKIE;
    715 			if (query->cookie != NULL) {
    716 				isc_buffer_t b;
    717 
    718 				isc_buffer_init(&b, cookie, sizeof(cookie));
    719 				result = isc_hex_decodestring(query->cookie,
    720 							      &b);
    721 				CHECK("isc_hex_decodestring", result);
    722 				opts[i].value = isc_buffer_base(&b);
    723 				opts[i].length = isc_buffer_usedlength(&b);
    724 			} else {
    725 				compute_cookie(cookie, 8);
    726 				opts[i].length = 8;
    727 				opts[i].value = cookie;
    728 			}
    729 			i++;
    730 		}
    731 
    732 		if (query->expire) {
    733 			INSIST(i < DNS_EDNSOPTIONS);
    734 			opts[i].code = DNS_OPT_EXPIRE;
    735 			opts[i].length = 0;
    736 			opts[i].value = NULL;
    737 			i++;
    738 		}
    739 
    740 		if (query->ednsoptscnt != 0) {
    741 			memmove(&opts[i], query->ednsopts,
    742 				sizeof(dns_ednsopt_t) * query->ednsoptscnt);
    743 			i += query->ednsoptscnt;
    744 		}
    745 
    746 		flags = query->ednsflags;
    747 		flags &= ~DNS_MESSAGEEXTFLAG_DO;
    748 		if (query->dnssec) {
    749 			flags |= DNS_MESSAGEEXTFLAG_DO;
    750 		}
    751 		add_opt(message, query->udpsize, query->edns, flags, opts, i);
    752 	}
    753 
    754 	options = 0;
    755 	if (tcp_mode) {
    756 		options |= DNS_REQUESTOPT_TCP | DNS_REQUESTOPT_SHARE;
    757 	}
    758 	request = NULL;
    759 	result = dns_request_createvia(
    760 		requestmgr, message, have_src ? &srcaddr : NULL, &dstaddr, dscp,
    761 		options, NULL, query->timeout, query->udptimeout,
    762 		query->udpretries, task, recvresponse, message, &request);
    763 	CHECK("dns_request_createvia4", result);
    764 
    765 	return (ISC_R_SUCCESS);
    766 }
    767 
    768 static void
    769 sendqueries(isc_task_t *task, isc_event_t *event) {
    770 	struct query *query = (struct query *)event->ev_arg;
    771 
    772 	isc_event_free(&event);
    773 
    774 	while (query != NULL) {
    775 		struct query *next = ISC_LIST_NEXT(query, link);
    776 
    777 		sendquery(query, task);
    778 		query = next;
    779 	}
    780 
    781 	if (onfly == 0) {
    782 		isc_app_shutdown();
    783 	}
    784 	return;
    785 }
    786 
    787 ISC_PLATFORM_NORETURN_PRE static void
    788 usage(void) ISC_PLATFORM_NORETURN_POST;
    789 
    790 static void
    791 usage(void) {
    792 	fputs("Usage: mdig @server {global-opt} host\n"
    793 	      "           {local-opt} [ host {local-opt} [...]]\n",
    794 	      stderr);
    795 	fputs("\nUse \"mdig -h\" (or \"mdig -h | more\") "
    796 	      "for complete list of options\n",
    797 	      stderr);
    798 	exit(1);
    799 }
    800 
    801 /*% help */
    802 static void
    803 help(void) {
    804 	fputs("Usage: mdig @server {global-opt} host\n"
    805 	      "           {local-opt} [ host {local-opt} [...]]\n",
    806 	      stdout);
    807 	fputs("Where:\n"
    808 	      " anywhere opt    is one of:\n"
    809 	      "                 -f filename         (batch mode)\n"
    810 	      "                 -h                  (print help and exit)\n"
    811 	      "                 -v                  (print version and exit)\n"
    812 	      " global opt      is one of:\n"
    813 	      "                 -4                  (use IPv4 query transport "
    814 	      "only)\n"
    815 	      "                 -6                  (use IPv6 query transport "
    816 	      "only)\n"
    817 	      "                 -b address[#port]   (bind to source "
    818 	      "address/port)\n"
    819 	      "                 -p port             (specify port number)\n"
    820 	      "                 -m                  (enable memory usage "
    821 	      "debugging)\n"
    822 	      "                 +[no]dscp[=###]     (Set the DSCP value to ### "
    823 	      "[0..63])\n"
    824 	      "                 +[no]vc             (TCP mode)\n"
    825 	      "                 +[no]tcp            (TCP mode, alternate "
    826 	      "syntax)\n"
    827 	      "                 +[no]besteffort     (Try to parse even illegal "
    828 	      "messages)\n"
    829 	      "                 +[no]cl             (Control display of class "
    830 	      "in records)\n"
    831 	      "                 +[no]comments       (Control display of "
    832 	      "comment lines)\n"
    833 	      "                 +[no]rrcomments     (Control display of "
    834 	      "per-record "
    835 	      "comments)\n"
    836 	      "                 +[no]crypto         (Control display of "
    837 	      "cryptographic "
    838 	      "fields in records)\n"
    839 	      "                 +[no]question       (Control display of "
    840 	      "question)\n"
    841 	      "                 +[no]answer         (Control display of "
    842 	      "answer)\n"
    843 	      "                 +[no]authority      (Control display of "
    844 	      "authority)\n"
    845 	      "                 +[no]additional     (Control display of "
    846 	      "additional)\n"
    847 	      "                 +[no]short          (Disable everything except "
    848 	      "short\n"
    849 	      "                                      form of answer)\n"
    850 	      "                 +[no]ttlid          (Control display of ttls "
    851 	      "in records)\n"
    852 	      "                 +[no]ttlunits       (Display TTLs in "
    853 	      "human-readable units)\n"
    854 	      "                 +[no]unknownformat  (Print RDATA in RFC 3597 "
    855 	      "\"unknown\" format)\n"
    856 	      "                 +[no]all            (Set or clear all display "
    857 	      "flags)\n"
    858 	      "                 +[no]multiline      (Print records in an "
    859 	      "expanded format)\n"
    860 	      "                 +[no]split=##       (Split hex/base64 fields "
    861 	      "into chunks)\n"
    862 	      " local opt       is one of:\n"
    863 	      "                 -c class            (specify query class)\n"
    864 	      "                 -t type             (specify query type)\n"
    865 	      "                 -x dot-notation     (shortcut for reverse "
    866 	      "lookups)\n"
    867 	      "                 +timeout=###        (Set query timeout) "
    868 	      "[UDP=5,TCP=10]\n"
    869 	      "                 +udptimeout=###     (Set timeout before UDP "
    870 	      "retry)\n"
    871 	      "                 +tries=###          (Set number of UDP "
    872 	      "attempts) [3]\n"
    873 	      "                 +retry=###          (Set number of UDP "
    874 	      "retries) [2]\n"
    875 	      "                 +bufsize=###        (Set EDNS0 Max UDP packet "
    876 	      "size)\n"
    877 	      "                 +subnet=addr        (Set edns-client-subnet "
    878 	      "option)\n"
    879 	      "                 +[no]edns[=###]     (Set EDNS version) [0]\n"
    880 	      "                 +ednsflags=###      (Set EDNS flag bits)\n"
    881 	      "                 +ednsopt=###[:value] (Send specified EDNS "
    882 	      "option)\n"
    883 	      "                 +noednsopt          (Clear list of +ednsopt "
    884 	      "options)\n"
    885 	      "                 +[no]recurse        (Recursive mode)\n"
    886 	      "                 +[no]aaonly         (Set AA flag in query "
    887 	      "(+[no]aaflag))\n"
    888 	      "                 +[no]adflag         (Set AD flag in query)\n"
    889 	      "                 +[no]cdflag         (Set CD flag in query)\n"
    890 	      "                 +[no]zflag          (Set Z flag in query)\n"
    891 	      "                 +[no]dnssec         (Request DNSSEC records)\n"
    892 	      "                 +[no]expire         (Request time to expire)\n"
    893 	      "                 +[no]cookie[=###]   (Send a COOKIE option)\n"
    894 	      "                 +[no]nsid           (Request Name Server ID)\n",
    895 	      stdout);
    896 }
    897 
    898 ISC_PLATFORM_NORETURN_PRE static void
    899 fatal(const char *format, ...)
    900 	ISC_FORMAT_PRINTF(1, 2) ISC_PLATFORM_NORETURN_POST;
    901 
    902 static void
    903 fatal(const char *format, ...) {
    904 	va_list args;
    905 
    906 	fflush(stdout);
    907 	fprintf(stderr, "mdig: ");
    908 	va_start(args, format);
    909 	vfprintf(stderr, format, args);
    910 	va_end(args);
    911 	fprintf(stderr, "\n");
    912 	exit(-2);
    913 }
    914 
    915 static isc_result_t
    916 parse_uint_helper(uint32_t *uip, const char *value, uint32_t max,
    917 		  const char *desc, int base) {
    918 	uint32_t n;
    919 	isc_result_t result = isc_parse_uint32(&n, value, base);
    920 	if (result == ISC_R_SUCCESS && n > max) {
    921 		result = ISC_R_RANGE;
    922 	}
    923 	if (result != ISC_R_SUCCESS) {
    924 		printf("invalid %s '%s': %s\n", desc, value,
    925 		       isc_result_totext(result));
    926 		return (result);
    927 	}
    928 	*uip = n;
    929 	return (ISC_R_SUCCESS);
    930 }
    931 
    932 static isc_result_t
    933 parse_uint(uint32_t *uip, const char *value, uint32_t max, const char *desc) {
    934 	return (parse_uint_helper(uip, value, max, desc, 10));
    935 }
    936 
    937 static isc_result_t
    938 parse_xint(uint32_t *uip, const char *value, uint32_t max, const char *desc) {
    939 	return (parse_uint_helper(uip, value, max, desc, 0));
    940 }
    941 
    942 static void
    943 newopts(struct query *query) {
    944 	size_t len = sizeof(query->ednsopts[0]) * EDNSOPTS;
    945 	size_t i;
    946 
    947 	query->ednsopts = isc_mem_allocate(mctx, len);
    948 
    949 	for (i = 0; i < EDNSOPTS; i++) {
    950 		query->ednsopts[i].code = 0;
    951 		query->ednsopts[i].length = 0;
    952 		query->ednsopts[i].value = NULL;
    953 	}
    954 }
    955 
    956 static void
    957 save_opt(struct query *query, char *code, char *value) {
    958 	uint32_t num;
    959 	isc_buffer_t b;
    960 	isc_result_t result;
    961 
    962 	if (query->ednsopts == NULL) {
    963 		newopts(query);
    964 	}
    965 
    966 	if (query->ednsoptscnt == EDNSOPTS) {
    967 		fatal("too many ednsopts");
    968 	}
    969 
    970 	result = parse_uint(&num, code, 65535, "ednsopt");
    971 	if (result != ISC_R_SUCCESS) {
    972 		fatal("bad edns code point: %s", code);
    973 	}
    974 
    975 	query->ednsopts[query->ednsoptscnt].code = num;
    976 	query->ednsopts[query->ednsoptscnt].length = 0;
    977 	query->ednsopts[query->ednsoptscnt].value = NULL;
    978 
    979 	if (value != NULL) {
    980 		char *buf;
    981 		buf = isc_mem_allocate(mctx, strlen(value) / 2 + 1);
    982 		isc_buffer_init(&b, buf, strlen(value) / 2 + 1);
    983 		result = isc_hex_decodestring(value, &b);
    984 		CHECK("isc_hex_decodestring", result);
    985 		query->ednsopts[query->ednsoptscnt].value = isc_buffer_base(&b);
    986 		query->ednsopts[query->ednsoptscnt].length =
    987 			isc_buffer_usedlength(&b);
    988 	}
    989 
    990 	query->ednsoptscnt++;
    991 }
    992 
    993 static isc_result_t
    994 parse_netprefix(isc_sockaddr_t **sap, const char *value) {
    995 	isc_sockaddr_t *sa = NULL;
    996 	struct in_addr in4;
    997 	struct in6_addr in6;
    998 	uint32_t netmask = 0xffffffff;
    999 	char *slash = NULL;
   1000 	bool parsed = false;
   1001 	char buf[sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:XXX.XXX.XXX.XXX/128")];
   1002 
   1003 	if (strlcpy(buf, value, sizeof(buf)) >= sizeof(buf)) {
   1004 		fatal("invalid prefix '%s'\n", value);
   1005 	}
   1006 
   1007 	slash = strchr(buf, '/');
   1008 	if (slash != NULL) {
   1009 		isc_result_t result;
   1010 		*slash = '\0';
   1011 		result = isc_parse_uint32(&netmask, slash + 1, 10);
   1012 		if (result != ISC_R_SUCCESS) {
   1013 			fatal("invalid prefix length in '%s': %s\n", value,
   1014 			      isc_result_totext(result));
   1015 		}
   1016 	} else if (strcmp(value, "0") == 0) {
   1017 		netmask = 0;
   1018 	}
   1019 
   1020 	sa = isc_mem_allocate(mctx, sizeof(*sa));
   1021 	if (inet_pton(AF_INET6, buf, &in6) == 1) {
   1022 		parsed = true;
   1023 		isc_sockaddr_fromin6(sa, &in6, 0);
   1024 		if (netmask > 128) {
   1025 			netmask = 128;
   1026 		}
   1027 	} else if (inet_pton(AF_INET, buf, &in4) == 1) {
   1028 		parsed = true;
   1029 		isc_sockaddr_fromin(sa, &in4, 0);
   1030 		if (netmask > 32) {
   1031 			netmask = 32;
   1032 		}
   1033 	} else if (netmask != 0xffffffff) {
   1034 		int i;
   1035 
   1036 		for (i = 0; i < 3 && strlen(buf) < sizeof(buf) - 2; i++) {
   1037 			strlcat(buf, ".0", sizeof(buf));
   1038 			if (inet_pton(AF_INET, buf, &in4) == 1) {
   1039 				parsed = true;
   1040 				isc_sockaddr_fromin(sa, &in4, 0);
   1041 				break;
   1042 			}
   1043 		}
   1044 
   1045 		if (netmask > 32) {
   1046 			netmask = 32;
   1047 		}
   1048 	}
   1049 
   1050 	if (!parsed) {
   1051 		fatal("invalid address '%s'", value);
   1052 	}
   1053 
   1054 	sa->length = netmask;
   1055 	*sap = sa;
   1056 
   1057 	return (ISC_R_SUCCESS);
   1058 }
   1059 
   1060 /*%
   1061  * Append 'len' bytes of 'text' at '*p', failing with
   1062  * ISC_R_NOSPACE if that would advance p past 'end'.
   1063  */
   1064 static isc_result_t
   1065 append(const char *text, int len, char **p, char *end) {
   1066 	if (len > end - *p) {
   1067 		return (ISC_R_NOSPACE);
   1068 	}
   1069 	memmove(*p, text, len);
   1070 	*p += len;
   1071 	return (ISC_R_SUCCESS);
   1072 }
   1073 
   1074 static isc_result_t
   1075 reverse_octets(const char *in, char **p, char *end) {
   1076 	const char *dot = strchr(in, '.');
   1077 	int len;
   1078 	if (dot != NULL) {
   1079 		isc_result_t result;
   1080 		result = reverse_octets(dot + 1, p, end);
   1081 		CHECK("reverse_octets", result);
   1082 		result = append(".", 1, p, end);
   1083 		CHECK("append", result);
   1084 		len = (int)(dot - in);
   1085 	} else {
   1086 		len = strlen(in);
   1087 	}
   1088 	return (append(in, len, p, end));
   1089 }
   1090 
   1091 static void
   1092 get_reverse(char *reverse, size_t len, const char *value) {
   1093 	int r;
   1094 	isc_result_t result;
   1095 	isc_netaddr_t addr;
   1096 
   1097 	addr.family = AF_INET6;
   1098 	r = inet_pton(AF_INET6, value, &addr.type.in6);
   1099 	if (r > 0) {
   1100 		/* This is a valid IPv6 address. */
   1101 		dns_fixedname_t fname;
   1102 		dns_name_t *name;
   1103 		unsigned int options = 0;
   1104 
   1105 		name = dns_fixedname_initname(&fname);
   1106 		result = dns_byaddr_createptrname(&addr, options, name);
   1107 		CHECK("dns_byaddr_createptrname2", result);
   1108 		dns_name_format(name, reverse, (unsigned int)len);
   1109 		return;
   1110 	} else {
   1111 		/*
   1112 		 * Not a valid IPv6 address.  Assume IPv4.
   1113 		 * Construct the in-addr.arpa name by blindly
   1114 		 * reversing octets whether or not they look like
   1115 		 * integers, so that this can be used for RFC2317
   1116 		 * names and such.
   1117 		 */
   1118 		char *p = reverse;
   1119 		char *end = reverse + len;
   1120 		result = reverse_octets(value, &p, end);
   1121 		CHECK("reverse_octets", result);
   1122 		/* Append .in-addr.arpa. and a terminating NUL. */
   1123 		result = append(".in-addr.arpa.", 15, &p, end);
   1124 		CHECK("append", result);
   1125 		return;
   1126 	}
   1127 }
   1128 
   1129 /*%
   1130  * We're not using isc_commandline_parse() here since the command line
   1131  * syntax of mdig is quite a bit different from that which can be described
   1132  * by that routine.
   1133  * XXX doc options
   1134  */
   1135 
   1136 static void
   1137 plus_option(char *option, struct query *query, bool global) {
   1138 	isc_result_t result;
   1139 	char *cmd, *value, *last = NULL, *code;
   1140 	uint32_t num;
   1141 	bool state = true;
   1142 	size_t n;
   1143 
   1144 	INSIST(option != NULL);
   1145 
   1146 	if ((cmd = strtok_r(option, "=", &last)) == NULL) {
   1147 		printf(";; Invalid option %s\n", option);
   1148 		return;
   1149 	}
   1150 	if (strncasecmp(cmd, "no", 2) == 0) {
   1151 		cmd += 2;
   1152 		state = false;
   1153 	}
   1154 	/* parse the rest of the string */
   1155 	value = strtok_r(NULL, "", &last);
   1156 
   1157 #define FULLCHECK(A)                                                 \
   1158 	do {                                                         \
   1159 		size_t _l = strlen(cmd);                             \
   1160 		if (_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) \
   1161 			goto invalid_option;                         \
   1162 	} while (0)
   1163 #define FULLCHECK2(A, B)                                                 \
   1164 	do {                                                             \
   1165 		size_t _l = strlen(cmd);                                 \
   1166 		if ((_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) && \
   1167 		    (_l >= sizeof(B) || strncasecmp(cmd, B, _l) != 0))   \
   1168 			goto invalid_option;                             \
   1169 	} while (0)
   1170 #define GLOBAL()                            \
   1171 	do {                                \
   1172 		if (!global)                \
   1173 			goto global_option; \
   1174 	} while (0)
   1175 
   1176 	switch (cmd[0]) {
   1177 	case 'a':
   1178 		switch (cmd[1]) {
   1179 		case 'a': /* aaonly / aaflag */
   1180 			FULLCHECK2("aaonly", "aaflag");
   1181 			query->have_aaonly = state;
   1182 			break;
   1183 		case 'd':
   1184 			switch (cmd[2]) {
   1185 			case 'd': /* additional */
   1186 				FULLCHECK("additional");
   1187 				display_additional = state;
   1188 				break;
   1189 			case 'f':  /* adflag */
   1190 			case '\0': /* +ad is a synonym for +adflag */
   1191 				FULLCHECK("adflag");
   1192 				query->have_adflag = state;
   1193 				break;
   1194 			default:
   1195 				goto invalid_option;
   1196 			}
   1197 			break;
   1198 		case 'l': /* all */
   1199 			FULLCHECK("all");
   1200 			GLOBAL();
   1201 			display_question = state;
   1202 			display_answer = state;
   1203 			display_authority = state;
   1204 			display_additional = state;
   1205 			display_comments = state;
   1206 			display_rrcomments = state ? 1 : -1;
   1207 			break;
   1208 		case 'n': /* answer */
   1209 			FULLCHECK("answer");
   1210 			GLOBAL();
   1211 			display_answer = state;
   1212 			break;
   1213 		case 'u': /* authority */
   1214 			FULLCHECK("authority");
   1215 			GLOBAL();
   1216 			display_authority = state;
   1217 			break;
   1218 		default:
   1219 			goto invalid_option;
   1220 		}
   1221 		break;
   1222 	case 'b':
   1223 		switch (cmd[1]) {
   1224 		case 'e': /* besteffort */
   1225 			FULLCHECK("besteffort");
   1226 			GLOBAL();
   1227 			besteffort = state;
   1228 			break;
   1229 		case 'u': /* bufsize */
   1230 			FULLCHECK("bufsize");
   1231 			if (value == NULL) {
   1232 				goto need_value;
   1233 			}
   1234 			if (!state) {
   1235 				goto invalid_option;
   1236 			}
   1237 			result = parse_uint(&num, value, COMMSIZE,
   1238 					    "buffer size");
   1239 			CHECK("parse_uint(buffer size)", result);
   1240 			query->udpsize = num;
   1241 			break;
   1242 		default:
   1243 			goto invalid_option;
   1244 		}
   1245 		break;
   1246 	case 'c':
   1247 		switch (cmd[1]) {
   1248 		case 'd': /* cdflag */
   1249 			switch (cmd[2]) {
   1250 			case 'f':  /* cdflag */
   1251 			case '\0': /* +cd is a synonym for +cdflag */
   1252 				FULLCHECK("cdflag");
   1253 				query->have_cdflag = state;
   1254 				break;
   1255 			default:
   1256 				goto invalid_option;
   1257 			}
   1258 			break;
   1259 		case 'l': /* cl */
   1260 			FULLCHECK("cl");
   1261 			GLOBAL();
   1262 			display_class = state;
   1263 			break;
   1264 		case 'o': /* comments */
   1265 			switch (cmd[2]) {
   1266 			case 'm':
   1267 				FULLCHECK("comments");
   1268 				GLOBAL();
   1269 				display_comments = state;
   1270 				break;
   1271 			case 'n':
   1272 				FULLCHECK("continue");
   1273 				GLOBAL();
   1274 				continue_on_error = state;
   1275 				break;
   1276 			case 'o':
   1277 				FULLCHECK("cookie");
   1278 				if (state && query->edns == -1) {
   1279 					query->edns = 0;
   1280 				}
   1281 				query->send_cookie = state;
   1282 				if (value != NULL) {
   1283 					n = strlcpy(hexcookie, value,
   1284 						    sizeof(hexcookie));
   1285 					if (n >= sizeof(hexcookie)) {
   1286 						fatal("COOKIE data too large");
   1287 					}
   1288 					query->cookie = hexcookie;
   1289 				} else {
   1290 					query->cookie = NULL;
   1291 				}
   1292 				break;
   1293 			default:
   1294 				goto invalid_option;
   1295 			}
   1296 			break;
   1297 		case 'r':
   1298 			FULLCHECK("crypto");
   1299 			GLOBAL();
   1300 			display_crypto = state;
   1301 			break;
   1302 		default:
   1303 			goto invalid_option;
   1304 		}
   1305 		break;
   1306 	case 'd':
   1307 		switch (cmd[1]) {
   1308 		case 'n': /* dnssec */
   1309 			FULLCHECK("dnssec");
   1310 			if (state && query->edns == -1) {
   1311 				query->edns = 0;
   1312 			}
   1313 			query->dnssec = state;
   1314 			break;
   1315 		case 's': /* dscp */
   1316 			FULLCHECK("dscp");
   1317 			GLOBAL();
   1318 			if (!state) {
   1319 				dscp = -1;
   1320 				break;
   1321 			}
   1322 			if (value == NULL) {
   1323 				goto need_value;
   1324 			}
   1325 			result = parse_uint(&num, value, 0x3f, "DSCP");
   1326 			CHECK("parse_uint(DSCP)", result);
   1327 			dscp = num;
   1328 			break;
   1329 		default:
   1330 			goto invalid_option;
   1331 		}
   1332 		break;
   1333 	case 'e':
   1334 		switch (cmd[1]) {
   1335 		case 'd':
   1336 			switch (cmd[2]) {
   1337 			case 'n':
   1338 				switch (cmd[3]) {
   1339 				case 's':
   1340 					switch (cmd[4]) {
   1341 					case 0:
   1342 						FULLCHECK("edns");
   1343 						if (!state) {
   1344 							query->edns = -1;
   1345 							break;
   1346 						}
   1347 						if (value == NULL) {
   1348 							query->edns = 0;
   1349 							break;
   1350 						}
   1351 						result = parse_uint(&num, value,
   1352 								    255,
   1353 								    "edns");
   1354 						CHECK("parse_uint(edns)",
   1355 						      result);
   1356 						query->edns = num;
   1357 						break;
   1358 					case 'f':
   1359 						FULLCHECK("ednsflags");
   1360 						if (!state) {
   1361 							query->ednsflags = 0;
   1362 							break;
   1363 						}
   1364 						if (value == NULL) {
   1365 							query->ednsflags = 0;
   1366 							break;
   1367 						}
   1368 						result = parse_xint(
   1369 							&num, value, 0xffff,
   1370 							"ednsflags");
   1371 						CHECK("parse_xint(ednsflags)",
   1372 						      result);
   1373 						query->ednsflags = num;
   1374 						break;
   1375 					case 'o':
   1376 						FULLCHECK("ednsopt");
   1377 						if (!state) {
   1378 							query->ednsoptscnt = 0;
   1379 							break;
   1380 						}
   1381 						code = NULL;
   1382 						if (value != NULL) {
   1383 							code = strtok_r(value,
   1384 									":",
   1385 									&last);
   1386 						}
   1387 						if (code == NULL) {
   1388 							fatal("ednsopt no "
   1389 							      "code point "
   1390 							      "specified");
   1391 						}
   1392 						value = strtok_r(NULL, "\0",
   1393 								 &last);
   1394 						save_opt(query, code, value);
   1395 						break;
   1396 					default:
   1397 						goto invalid_option;
   1398 					}
   1399 					break;
   1400 				default:
   1401 					goto invalid_option;
   1402 				}
   1403 				break;
   1404 			default:
   1405 				goto invalid_option;
   1406 			}
   1407 			break;
   1408 		case 'x':
   1409 			FULLCHECK("expire");
   1410 			query->expire = state;
   1411 			break;
   1412 		default:
   1413 			goto invalid_option;
   1414 		}
   1415 		break;
   1416 	case 'm': /* multiline */
   1417 		FULLCHECK("multiline");
   1418 		GLOBAL();
   1419 		display_multiline = state;
   1420 		break;
   1421 	case 'n':
   1422 		FULLCHECK("nsid");
   1423 		if (state && query->edns == -1) {
   1424 			query->edns = 0;
   1425 		}
   1426 		query->nsid = state;
   1427 		break;
   1428 	case 'q':
   1429 		FULLCHECK("question");
   1430 		GLOBAL();
   1431 		display_question = state;
   1432 		break;
   1433 	case 'r':
   1434 		switch (cmd[1]) {
   1435 		case 'e':
   1436 			switch (cmd[2]) {
   1437 			case 'c': /* recurse */
   1438 				FULLCHECK("recurse");
   1439 				query->recurse = state;
   1440 				break;
   1441 			case 't': /* retry / retries */
   1442 				FULLCHECK2("retry", "retries");
   1443 				if (value == NULL) {
   1444 					goto need_value;
   1445 				}
   1446 				if (!state) {
   1447 					goto invalid_option;
   1448 				}
   1449 				result = parse_uint(&query->udpretries, value,
   1450 						    MAXTRIES - 1, "udpretries");
   1451 				CHECK("parse_uint(udpretries)", result);
   1452 				query->udpretries++;
   1453 				break;
   1454 			default:
   1455 				goto invalid_option;
   1456 			}
   1457 			break;
   1458 		case 'r':
   1459 			FULLCHECK("rrcomments");
   1460 			GLOBAL();
   1461 			display_rrcomments = state ? 1 : -1;
   1462 			break;
   1463 		default:
   1464 			goto invalid_option;
   1465 		}
   1466 		break;
   1467 	case 's':
   1468 		switch (cmd[1]) {
   1469 		case 'h':
   1470 			FULLCHECK("short");
   1471 			GLOBAL();
   1472 			display_short_form = state;
   1473 			if (state) {
   1474 				display_question = false;
   1475 				display_answer = true;
   1476 				display_authority = false;
   1477 				display_additional = false;
   1478 				display_comments = false;
   1479 				display_rrcomments = -1;
   1480 			}
   1481 			break;
   1482 		case 'p': /* split */
   1483 			FULLCHECK("split");
   1484 			GLOBAL();
   1485 			if (value != NULL && !state) {
   1486 				goto invalid_option;
   1487 			}
   1488 			if (!state) {
   1489 				display_splitwidth = 0;
   1490 				break;
   1491 			} else if (value == NULL) {
   1492 				break;
   1493 			}
   1494 
   1495 			result = parse_uint(&display_splitwidth, value, 1023,
   1496 					    "split");
   1497 			if ((display_splitwidth % 4) != 0) {
   1498 				display_splitwidth =
   1499 					((display_splitwidth + 3) / 4) * 4;
   1500 				fprintf(stderr,
   1501 					";; Warning, split must be "
   1502 					"a multiple of 4; adjusting "
   1503 					"to %u\n",
   1504 					display_splitwidth);
   1505 			}
   1506 			/*
   1507 			 * There is an adjustment done in the
   1508 			 * totext_<rrtype>() functions which causes
   1509 			 * splitwidth to shrink.  This is okay when we're
   1510 			 * using the default width but incorrect in this
   1511 			 * case, so we correct for it
   1512 			 */
   1513 			if (display_splitwidth) {
   1514 				display_splitwidth += 3;
   1515 			}
   1516 			CHECK("parse_uint(split)", result);
   1517 			break;
   1518 		case 'u': /* subnet */
   1519 			FULLCHECK("subnet");
   1520 			if (state && value == NULL) {
   1521 				goto need_value;
   1522 			}
   1523 			if (!state) {
   1524 				if (query->ecs_addr != NULL) {
   1525 					isc_mem_free(mctx, query->ecs_addr);
   1526 					query->ecs_addr = NULL;
   1527 				}
   1528 				break;
   1529 			}
   1530 			if (query->edns == -1) {
   1531 				query->edns = 0;
   1532 			}
   1533 			result = parse_netprefix(&query->ecs_addr, value);
   1534 			CHECK("parse_netprefix", result);
   1535 			break;
   1536 		default:
   1537 			goto invalid_option;
   1538 		}
   1539 		break;
   1540 	case 't':
   1541 		switch (cmd[1]) {
   1542 		case 'c': /* tcp */
   1543 			FULLCHECK("tcp");
   1544 			GLOBAL();
   1545 			tcp_mode = state;
   1546 			break;
   1547 		case 'i': /* timeout */
   1548 			FULLCHECK("timeout");
   1549 			if (value == NULL) {
   1550 				goto need_value;
   1551 			}
   1552 			if (!state) {
   1553 				goto invalid_option;
   1554 			}
   1555 			result = parse_uint(&query->timeout, value, MAXTIMEOUT,
   1556 					    "timeout");
   1557 			CHECK("parse_uint(timeout)", result);
   1558 			if (query->timeout == 0) {
   1559 				query->timeout = 1;
   1560 			}
   1561 			break;
   1562 		case 'r':
   1563 			FULLCHECK("tries");
   1564 			if (value == NULL) {
   1565 				goto need_value;
   1566 			}
   1567 			if (!state) {
   1568 				goto invalid_option;
   1569 			}
   1570 			result = parse_uint(&query->udpretries, value, MAXTRIES,
   1571 					    "udpretries");
   1572 			CHECK("parse_uint(udpretries)", result);
   1573 			if (query->udpretries == 0) {
   1574 				query->udpretries = 1;
   1575 			}
   1576 			break;
   1577 		case 't':
   1578 			switch (cmd[2]) {
   1579 			case 'l':
   1580 				switch (cmd[3]) {
   1581 				case 0:
   1582 				case 'i': /* ttlid */
   1583 					FULLCHECK2("ttl", "ttlid");
   1584 					GLOBAL();
   1585 					display_ttl = state;
   1586 					break;
   1587 				case 'u': /* ttlunits */
   1588 					FULLCHECK("ttlunits");
   1589 					GLOBAL();
   1590 					display_ttl = true;
   1591 					display_ttlunits = state;
   1592 					break;
   1593 				default:
   1594 					goto invalid_option;
   1595 				}
   1596 				break;
   1597 			default:
   1598 				goto invalid_option;
   1599 			}
   1600 			break;
   1601 		default:
   1602 			goto invalid_option;
   1603 		}
   1604 		break;
   1605 	case 'u':
   1606 		switch (cmd[1]) {
   1607 		case 'd':
   1608 			FULLCHECK("udptimeout");
   1609 			if (value == NULL) {
   1610 				goto need_value;
   1611 			}
   1612 			if (!state) {
   1613 				goto invalid_option;
   1614 			}
   1615 			result = parse_uint(&query->udptimeout, value,
   1616 					    MAXTIMEOUT, "udptimeout");
   1617 			CHECK("parse_uint(udptimeout)", result);
   1618 			break;
   1619 		case 'n':
   1620 			FULLCHECK("unknownformat");
   1621 			display_unknown_format = state;
   1622 			break;
   1623 		default:
   1624 			goto invalid_option;
   1625 		}
   1626 		break;
   1627 	case 'v':
   1628 		FULLCHECK("vc");
   1629 		GLOBAL();
   1630 		tcp_mode = state;
   1631 		break;
   1632 	case 'y': /* yaml */
   1633 		FULLCHECK("yaml");
   1634 		yaml = state;
   1635 		if (state) {
   1636 			display_rrcomments = state;
   1637 		}
   1638 		break;
   1639 	case 'z': /* zflag */
   1640 		FULLCHECK("zflag");
   1641 		query->have_zflag = state;
   1642 		break;
   1643 	global_option:
   1644 		fprintf(stderr, "Ignored late global option: +%s\n", option);
   1645 		break;
   1646 	default:
   1647 	invalid_option:
   1648 	need_value:
   1649 		fprintf(stderr, "Invalid option: +%s\n", option);
   1650 		usage();
   1651 	}
   1652 	return;
   1653 }
   1654 
   1655 /*%
   1656  * #true returned if value was used
   1657  */
   1658 static const char *single_dash_opts = "46himv";
   1659 static const char *dash_opts = "46bcfhiptvx";
   1660 static bool
   1661 dash_option(const char *option, char *next, struct query *query, bool global,
   1662 	    bool *setname) {
   1663 	char opt;
   1664 	const char *value;
   1665 	isc_result_t result;
   1666 	bool value_from_next;
   1667 	isc_consttextregion_t tr;
   1668 	dns_rdatatype_t rdtype;
   1669 	dns_rdataclass_t rdclass;
   1670 	char textname[MXNAME];
   1671 	struct in_addr in4;
   1672 	struct in6_addr in6;
   1673 	in_port_t srcport;
   1674 	char *hash;
   1675 	uint32_t num;
   1676 
   1677 	while (strpbrk(option, single_dash_opts) == &option[0]) {
   1678 		/*
   1679 		 * Since the -[46hiv] options do not take an argument,
   1680 		 * account for them (in any number and/or combination)
   1681 		 * if they appear as the first character(s) of an opt.
   1682 		 */
   1683 		opt = option[0];
   1684 		switch (opt) {
   1685 		case '4':
   1686 			GLOBAL();
   1687 			if (have_ipv4) {
   1688 				isc_net_disableipv6();
   1689 				have_ipv6 = false;
   1690 			} else {
   1691 				fatal("can't find IPv4 networking");
   1692 				/* NOTREACHED */
   1693 				return (false);
   1694 			}
   1695 			break;
   1696 		case '6':
   1697 			GLOBAL();
   1698 			if (have_ipv6) {
   1699 				isc_net_disableipv4();
   1700 				have_ipv4 = false;
   1701 			} else {
   1702 				fatal("can't find IPv6 networking");
   1703 				/* NOTREACHED */
   1704 				return (false);
   1705 			}
   1706 			break;
   1707 		case 'h':
   1708 			help();
   1709 			exit(0);
   1710 			break;
   1711 		case 'i':
   1712 			/* deprecated */
   1713 			break;
   1714 		case 'm':
   1715 			/*
   1716 			 * handled by preparse_args()
   1717 			 */
   1718 			break;
   1719 		case 'v':
   1720 			fputs("mDiG " VERSION "\n", stderr);
   1721 			exit(0);
   1722 			break;
   1723 		}
   1724 		if (strlen(option) > 1U) {
   1725 			option = &option[1];
   1726 		} else {
   1727 			return (false);
   1728 		}
   1729 	}
   1730 	opt = option[0];
   1731 	if (strlen(option) > 1U) {
   1732 		value_from_next = false;
   1733 		value = &option[1];
   1734 	} else {
   1735 		value_from_next = true;
   1736 		value = next;
   1737 	}
   1738 	if (value == NULL) {
   1739 		goto invalid_option;
   1740 	}
   1741 	switch (opt) {
   1742 	case 'b':
   1743 		GLOBAL();
   1744 		hash = strchr(value, '#');
   1745 		if (hash != NULL) {
   1746 			result = parse_uint(&num, hash + 1, MAXPORT,
   1747 					    "port number");
   1748 			CHECK("parse_uint(srcport)", result);
   1749 			srcport = num;
   1750 			*hash = '\0';
   1751 		} else {
   1752 			srcport = 0;
   1753 		}
   1754 		if (have_ipv6 && inet_pton(AF_INET6, value, &in6) == 1) {
   1755 			isc_sockaddr_fromin6(&srcaddr, &in6, srcport);
   1756 			isc_net_disableipv4();
   1757 		} else if (have_ipv4 && inet_pton(AF_INET, value, &in4) == 1) {
   1758 			isc_sockaddr_fromin(&srcaddr, &in4, srcport);
   1759 			isc_net_disableipv6();
   1760 		} else {
   1761 			if (hash != NULL) {
   1762 				*hash = '#';
   1763 			}
   1764 			fatal("invalid address %s", value);
   1765 		}
   1766 		if (hash != NULL) {
   1767 			*hash = '#';
   1768 		}
   1769 		have_src = true;
   1770 		return (value_from_next);
   1771 	case 'c':
   1772 		tr.base = value;
   1773 		tr.length = strlen(value);
   1774 		result = dns_rdataclass_fromtext(&rdclass,
   1775 						 (isc_textregion_t *)&tr);
   1776 		CHECK("dns_rdataclass_fromtext", result);
   1777 		query->rdclass = rdclass;
   1778 		return (value_from_next);
   1779 	case 'f':
   1780 		batchname = value;
   1781 		return (value_from_next);
   1782 	case 'p':
   1783 		GLOBAL();
   1784 		result = parse_uint(&num, value, MAXPORT, "port number");
   1785 		CHECK("parse_uint(port)", result);
   1786 		port = num;
   1787 		return (value_from_next);
   1788 	case 't':
   1789 		tr.base = value;
   1790 		tr.length = strlen(value);
   1791 		result = dns_rdatatype_fromtext(&rdtype,
   1792 						(isc_textregion_t *)&tr);
   1793 		CHECK("dns_rdatatype_fromtext", result);
   1794 		query->rdtype = rdtype;
   1795 		return (value_from_next);
   1796 	case 'x':
   1797 		get_reverse(textname, sizeof(textname), value);
   1798 		strlcpy(query->textname, textname, sizeof(query->textname));
   1799 		query->rdtype = dns_rdatatype_ptr;
   1800 		query->rdclass = dns_rdataclass_in;
   1801 		*setname = true;
   1802 		return (value_from_next);
   1803 	global_option:
   1804 		fprintf(stderr, "Ignored late global option: -%s\n", option);
   1805 		usage();
   1806 	default:
   1807 	invalid_option:
   1808 		fprintf(stderr, "Invalid option: -%s\n", option);
   1809 		usage();
   1810 	}
   1811 	/* NOTREACHED */
   1812 	return (false);
   1813 }
   1814 
   1815 static struct query *
   1816 clone_default_query() {
   1817 	struct query *query;
   1818 
   1819 	query = isc_mem_allocate(mctx, sizeof(struct query));
   1820 	memmove(query, &default_query, sizeof(struct query));
   1821 	if (default_query.ecs_addr != NULL) {
   1822 		size_t len = sizeof(isc_sockaddr_t);
   1823 
   1824 		query->ecs_addr = isc_mem_allocate(mctx, len);
   1825 		memmove(query->ecs_addr, default_query.ecs_addr, len);
   1826 	}
   1827 
   1828 	if (query->timeout == 0) {
   1829 		query->timeout = tcp_mode ? TCPTIMEOUT : UDPTIMEOUT;
   1830 	}
   1831 
   1832 	return (query);
   1833 }
   1834 
   1835 /*%
   1836  * Because we may be trying to do memory allocation recording, we're going
   1837  * to need to parse the arguments for the -m *before* we start the main
   1838  * argument parsing routine.
   1839  *
   1840  * I'd prefer not to have to do this, but I am not quite sure how else to
   1841  * fix the problem.  Argument parsing in mdig involves memory allocation
   1842  * by its nature, so it can't be done in the main argument parser.
   1843  */
   1844 static void
   1845 preparse_args(int argc, char **argv) {
   1846 	int rc;
   1847 	char **rv;
   1848 	char *option;
   1849 	bool ipv4only = false, ipv6only = false;
   1850 
   1851 	rc = argc;
   1852 	rv = argv;
   1853 	for (rc--, rv++; rc > 0; rc--, rv++) {
   1854 		if (rv[0][0] != '-') {
   1855 			continue;
   1856 		}
   1857 		option = &rv[0][1];
   1858 		while (strpbrk(option, single_dash_opts) == &option[0]) {
   1859 			switch (option[0]) {
   1860 			case 'm':
   1861 				isc_mem_debugging = ISC_MEM_DEBUGTRACE |
   1862 						    ISC_MEM_DEBUGRECORD;
   1863 				break;
   1864 			case '4':
   1865 				if (ipv6only) {
   1866 					fatal("only one of -4 and -6 allowed");
   1867 				}
   1868 				ipv4only = true;
   1869 				break;
   1870 			case '6':
   1871 				if (ipv4only) {
   1872 					fatal("only one of -4 and -6 allowed");
   1873 				}
   1874 				ipv6only = true;
   1875 				break;
   1876 			}
   1877 			option = &option[1];
   1878 		}
   1879 		if (strlen(option) == 0U) {
   1880 			continue;
   1881 		}
   1882 		/* Look for dash value option. */
   1883 		if (strpbrk(option, dash_opts) != &option[0] ||
   1884 		    strlen(option) > 1U) {
   1885 			/* Error or value in option. */
   1886 			continue;
   1887 		}
   1888 		/* Dash value is next argument so we need to skip it. */
   1889 		rc--, rv++;
   1890 		/* Handle missing argument */
   1891 		if (rc == 0) {
   1892 			break;
   1893 		}
   1894 	}
   1895 }
   1896 
   1897 static void
   1898 parse_args(bool is_batchfile, int argc, char **argv) {
   1899 	struct query *query = NULL;
   1900 	char batchline[MXNAME];
   1901 	int bargc;
   1902 	char *bargv[64];
   1903 	int rc;
   1904 	char **rv;
   1905 	bool global = true;
   1906 	char *last;
   1907 
   1908 	/*
   1909 	 * The semantics for parsing the args is a bit complex; if
   1910 	 * we don't have a host yet, make the arg apply globally,
   1911 	 * otherwise make it apply to the latest host.  This is
   1912 	 * a bit different than the previous versions, but should
   1913 	 * form a consistent user interface.
   1914 	 *
   1915 	 * First, create a "default query" which won't actually be used
   1916 	 * anywhere, except for cloning into new queries
   1917 	 */
   1918 
   1919 	if (!is_batchfile) {
   1920 		default_query.textname[0] = 0;
   1921 		default_query.recurse = true;
   1922 		default_query.have_aaonly = false;
   1923 		default_query.have_adflag = true; /*XXX*/
   1924 		default_query.have_cdflag = false;
   1925 		default_query.have_zflag = false;
   1926 		default_query.dnssec = false;
   1927 		default_query.expire = false;
   1928 		default_query.send_cookie = false;
   1929 		default_query.cookie = NULL;
   1930 		default_query.nsid = false;
   1931 		default_query.rdtype = dns_rdatatype_a;
   1932 		default_query.rdclass = dns_rdataclass_in;
   1933 		default_query.udpsize = 0;
   1934 		default_query.edns = 0; /*XXX*/
   1935 		default_query.ednsopts = NULL;
   1936 		default_query.ednsoptscnt = 0;
   1937 		default_query.ednsflags = 0;
   1938 		default_query.ecs_addr = NULL;
   1939 		default_query.timeout = 0;
   1940 		default_query.udptimeout = 0;
   1941 		default_query.udpretries = 3;
   1942 		ISC_LINK_INIT(&default_query, link);
   1943 	}
   1944 
   1945 	if (is_batchfile) {
   1946 		/* Processing '-f batchfile'. */
   1947 		query = clone_default_query();
   1948 		global = false;
   1949 	} else {
   1950 		query = &default_query;
   1951 	}
   1952 
   1953 	rc = argc;
   1954 	rv = argv;
   1955 	for (rc--, rv++; rc > 0; rc--, rv++) {
   1956 		if (strncmp(rv[0], "%", 1) == 0) {
   1957 			break;
   1958 		}
   1959 		if (rv[0][0] == '@') {
   1960 			if (server != NULL) {
   1961 				fatal("server already set to @%s", server);
   1962 			}
   1963 			server = &rv[0][1];
   1964 		} else if (rv[0][0] == '+') {
   1965 			plus_option(&rv[0][1], query, global);
   1966 		} else if (rv[0][0] == '-') {
   1967 			bool setname = false;
   1968 
   1969 			if (rc <= 1) {
   1970 				if (dash_option(&rv[0][1], NULL, query, global,
   1971 						&setname)) {
   1972 					rc--;
   1973 					rv++;
   1974 				}
   1975 			} else {
   1976 				if (dash_option(&rv[0][1], rv[1], query, global,
   1977 						&setname)) {
   1978 					rc--;
   1979 					rv++;
   1980 				}
   1981 			}
   1982 			if (setname) {
   1983 				if (query == &default_query) {
   1984 					query = clone_default_query();
   1985 				}
   1986 				ISC_LIST_APPEND(queries, query, link);
   1987 
   1988 				default_query.textname[0] = 0;
   1989 				query = clone_default_query();
   1990 				global = false;
   1991 			}
   1992 		} else {
   1993 			/*
   1994 			 * Anything which isn't an option
   1995 			 */
   1996 			if (query == &default_query) {
   1997 				query = clone_default_query();
   1998 			}
   1999 			strlcpy(query->textname, rv[0],
   2000 				sizeof(query->textname));
   2001 			ISC_LIST_APPEND(queries, query, link);
   2002 
   2003 			query = clone_default_query();
   2004 			global = false;
   2005 			/* XXX Error message */
   2006 		}
   2007 	}
   2008 
   2009 	/*
   2010 	 * If we have a batchfile, read the query list from it.
   2011 	 */
   2012 	if ((batchname != NULL) && !is_batchfile) {
   2013 		if (strcmp(batchname, "-") == 0) {
   2014 			batchfp = stdin;
   2015 		} else {
   2016 			batchfp = fopen(batchname, "r");
   2017 		}
   2018 		if (batchfp == NULL) {
   2019 			perror(batchname);
   2020 			fatal("couldn't open batch file '%s'", batchname);
   2021 		}
   2022 		while (fgets(batchline, sizeof(batchline), batchfp) != 0) {
   2023 			if (batchline[0] == '\r' || batchline[0] == '\n' ||
   2024 			    batchline[0] == '#' || batchline[0] == ';')
   2025 			{
   2026 				continue;
   2027 			}
   2028 			for (bargc = 1, bargv[bargc] = strtok_r(
   2029 						batchline, " \t\r\n", &last);
   2030 			     (bargc < 14) && bargv[bargc]; bargc++,
   2031 			    bargv[bargc] = strtok_r(NULL, " \t\r\n", &last))
   2032 			{
   2033 				/* empty body */
   2034 			}
   2035 
   2036 			bargv[0] = argv[0];
   2037 			parse_args(true, bargc, (char **)bargv);
   2038 		}
   2039 		if (batchfp != stdin) {
   2040 			fclose(batchfp);
   2041 		}
   2042 	}
   2043 	if (query != &default_query) {
   2044 		if (query->ecs_addr != NULL) {
   2045 			isc_mem_free(mctx, query->ecs_addr);
   2046 		}
   2047 		isc_mem_free(mctx, query);
   2048 	}
   2049 }
   2050 
   2051 /*% Main processing routine for mdig */
   2052 int
   2053 main(int argc, char *argv[]) {
   2054 	struct query *query;
   2055 	isc_result_t result;
   2056 	isc_sockaddr_t bind_any;
   2057 	isc_log_t *lctx;
   2058 	isc_logconfig_t *lcfg;
   2059 	isc_taskmgr_t *taskmgr;
   2060 	isc_task_t *task;
   2061 	isc_timermgr_t *timermgr;
   2062 	isc_socketmgr_t *socketmgr;
   2063 	dns_dispatchmgr_t *dispatchmgr;
   2064 	unsigned int attrs, attrmask;
   2065 	dns_dispatch_t *dispatchvx;
   2066 	dns_view_t *view;
   2067 	int ns;
   2068 	unsigned int i;
   2069 
   2070 	RUNCHECK(isc_app_start());
   2071 
   2072 	dns_result_register();
   2073 
   2074 	if (isc_net_probeipv4() == ISC_R_SUCCESS) {
   2075 		have_ipv4 = true;
   2076 	}
   2077 	if (isc_net_probeipv6() == ISC_R_SUCCESS) {
   2078 		have_ipv6 = true;
   2079 	}
   2080 	if (!have_ipv4 && !have_ipv6) {
   2081 		fatal("could not find either IPv4 or IPv6");
   2082 	}
   2083 
   2084 	preparse_args(argc, argv);
   2085 
   2086 	mctx = NULL;
   2087 	isc_mem_create(&mctx);
   2088 
   2089 	lctx = NULL;
   2090 	lcfg = NULL;
   2091 	isc_log_create(mctx, &lctx, &lcfg);
   2092 
   2093 	RUNCHECK(dst_lib_init(mctx, NULL));
   2094 	isc_nonce_buf(cookie_secret, sizeof(cookie_secret));
   2095 
   2096 	ISC_LIST_INIT(queries);
   2097 	parse_args(false, argc, argv);
   2098 	if (server == NULL) {
   2099 		fatal("a server '@xxx' is required");
   2100 	}
   2101 
   2102 	ns = 0;
   2103 	result = bind9_getaddresses(server, port, &dstaddr, 1, &ns);
   2104 	if (result != ISC_R_SUCCESS) {
   2105 		fatal("couldn't get address for '%s': %s", server,
   2106 		      isc_result_totext(result));
   2107 	}
   2108 
   2109 	if (isc_sockaddr_pf(&dstaddr) == PF_INET && have_ipv6) {
   2110 		isc_net_disableipv6();
   2111 		have_ipv6 = false;
   2112 	} else if (isc_sockaddr_pf(&dstaddr) == PF_INET6 && have_ipv4) {
   2113 		isc_net_disableipv4();
   2114 		have_ipv4 = false;
   2115 	}
   2116 	if (have_ipv4 && have_ipv6) {
   2117 		fatal("can't choose between IPv4 and IPv6");
   2118 	}
   2119 
   2120 	taskmgr = NULL;
   2121 	RUNCHECK(isc_taskmgr_create(mctx, 1, 0, NULL, &taskmgr));
   2122 	task = NULL;
   2123 	RUNCHECK(isc_task_create(taskmgr, 0, &task));
   2124 	timermgr = NULL;
   2125 
   2126 	RUNCHECK(isc_timermgr_create(mctx, &timermgr));
   2127 	socketmgr = NULL;
   2128 	RUNCHECK(isc_socketmgr_create(mctx, &socketmgr));
   2129 	dispatchmgr = NULL;
   2130 	RUNCHECK(dns_dispatchmgr_create(mctx, &dispatchmgr));
   2131 
   2132 	attrs = DNS_DISPATCHATTR_UDP | DNS_DISPATCHATTR_MAKEQUERY;
   2133 	if (have_ipv4) {
   2134 		isc_sockaddr_any(&bind_any);
   2135 		attrs |= DNS_DISPATCHATTR_IPV4;
   2136 	} else {
   2137 		isc_sockaddr_any6(&bind_any);
   2138 		attrs |= DNS_DISPATCHATTR_IPV6;
   2139 	}
   2140 	attrmask = DNS_DISPATCHATTR_UDP | DNS_DISPATCHATTR_TCP |
   2141 		   DNS_DISPATCHATTR_IPV4 | DNS_DISPATCHATTR_IPV6;
   2142 	dispatchvx = NULL;
   2143 	RUNCHECK(dns_dispatch_getudp(dispatchmgr, socketmgr, taskmgr,
   2144 				     have_src ? &srcaddr : &bind_any, 4096, 100,
   2145 				     100, 17, 19, attrs, attrmask,
   2146 				     &dispatchvx));
   2147 	requestmgr = NULL;
   2148 	RUNCHECK(dns_requestmgr_create(
   2149 		mctx, timermgr, socketmgr, taskmgr, dispatchmgr,
   2150 		have_ipv4 ? dispatchvx : NULL, have_ipv6 ? dispatchvx : NULL,
   2151 		&requestmgr));
   2152 
   2153 	view = NULL;
   2154 	RUNCHECK(dns_view_create(mctx, 0, "_test", &view));
   2155 
   2156 	query = ISC_LIST_HEAD(queries);
   2157 	RUNCHECK(isc_app_onrun(mctx, task, sendqueries, query));
   2158 
   2159 	(void)isc_app_run();
   2160 
   2161 	query = ISC_LIST_HEAD(queries);
   2162 	while (query != NULL) {
   2163 		struct query *next = ISC_LIST_NEXT(query, link);
   2164 
   2165 		if (query->ednsopts != NULL) {
   2166 			for (i = 0; i < EDNSOPTS; i++) {
   2167 				if (query->ednsopts[i].value != NULL) {
   2168 					isc_mem_free(mctx,
   2169 						     query->ednsopts[i].value);
   2170 				}
   2171 			}
   2172 			isc_mem_free(mctx, query->ednsopts);
   2173 		}
   2174 		if (query->ecs_addr != NULL) {
   2175 			isc_mem_free(mctx, query->ecs_addr);
   2176 			query->ecs_addr = NULL;
   2177 		}
   2178 		isc_mem_free(mctx, query);
   2179 		query = next;
   2180 	}
   2181 
   2182 	if (default_query.ecs_addr != NULL) {
   2183 		isc_mem_free(mctx, default_query.ecs_addr);
   2184 	}
   2185 
   2186 	dns_view_detach(&view);
   2187 
   2188 	dns_requestmgr_shutdown(requestmgr);
   2189 	dns_requestmgr_detach(&requestmgr);
   2190 
   2191 	dns_dispatch_detach(&dispatchvx);
   2192 	dns_dispatchmgr_destroy(&dispatchmgr);
   2193 
   2194 	isc_socketmgr_destroy(&socketmgr);
   2195 	isc_timermgr_destroy(&timermgr);
   2196 
   2197 	isc_task_shutdown(task);
   2198 	isc_task_detach(&task);
   2199 	isc_taskmgr_destroy(&taskmgr);
   2200 
   2201 	dst_lib_destroy();
   2202 
   2203 	isc_log_destroy(&lctx);
   2204 
   2205 	isc_mem_destroy(&mctx);
   2206 
   2207 	isc_app_finish();
   2208 
   2209 	return (0);
   2210 }
   2211