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