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