xref: /openbsd-src/usr.bin/dig/dig.c (revision 9835a5e128ac1db1a0a5e36c27d1a17b7926c0b3)
15185a700Sflorian /*
25185a700Sflorian  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
35185a700Sflorian  *
45185a700Sflorian  * Permission to use, copy, modify, and/or distribute this software for any
55185a700Sflorian  * purpose with or without fee is hereby granted, provided that the above
65185a700Sflorian  * copyright notice and this permission notice appear in all copies.
75185a700Sflorian  *
85185a700Sflorian  * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
95185a700Sflorian  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
105185a700Sflorian  * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
115185a700Sflorian  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
125185a700Sflorian  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
135185a700Sflorian  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
145185a700Sflorian  * PERFORMANCE OF THIS SOFTWARE.
155185a700Sflorian  */
165185a700Sflorian 
17*9835a5e1Sflorian /* $Id: dig.c,v 1.21 2024/12/27 09:04:48 florian Exp $ */
185185a700Sflorian 
195185a700Sflorian /*! \file */
2033e1be9eSflorian #include <sys/types.h>
2133e1be9eSflorian #include <sys/socket.h>
2233e1be9eSflorian 
2333e1be9eSflorian #include <netdb.h>
245185a700Sflorian 
25c49c5f05Sflorian #include <errno.h>
265185a700Sflorian #include <stdlib.h>
275185a700Sflorian #include <time.h>
285185a700Sflorian #include <unistd.h>
295185a700Sflorian 
305185a700Sflorian #include <isc/app.h>
315185a700Sflorian 
325185a700Sflorian #include <string.h>
335185a700Sflorian #include <isc/util.h>
345185a700Sflorian 
355185a700Sflorian #include <dns/fixedname.h>
365185a700Sflorian #include <dns/masterdump.h>
375185a700Sflorian #include <dns/message.h>
385185a700Sflorian #include <dns/name.h>
395185a700Sflorian #include <dns/rdata.h>
405185a700Sflorian #include <dns/rdataset.h>
415185a700Sflorian #include <dns/rdatatype.h>
425185a700Sflorian #include <dns/rdataclass.h>
435185a700Sflorian #include <dns/result.h>
445185a700Sflorian #include <dns/tsig.h>
455185a700Sflorian 
465185a700Sflorian #include "dig.h"
475185a700Sflorian 
485185a700Sflorian #define ADD_STRING(b, s) { 				\
495185a700Sflorian 	if (strlen(s) >= isc_buffer_availablelength(b)) \
505185a700Sflorian 		return (ISC_R_NOSPACE); 		\
515185a700Sflorian 	else 						\
525185a700Sflorian 		isc_buffer_putstr(b, s); 		\
535185a700Sflorian }
545185a700Sflorian 
555185a700Sflorian dig_lookup_t *default_lookup = NULL;
565185a700Sflorian 
575185a700Sflorian static char *batchname = NULL;
585185a700Sflorian static FILE *batchfp = NULL;
595185a700Sflorian static char *argv0;
605185a700Sflorian static int addresscount = 0;
615185a700Sflorian 
625185a700Sflorian static char domainopt[DNS_NAME_MAXTEXT];
635185a700Sflorian static char sitvalue[256];
645185a700Sflorian 
651fb015a8Sflorian static int short_form = 0, printcmd = 1,
661fb015a8Sflorian 	ip6_int = 0, plusquest = 0, pluscomm = 0,
671fb015a8Sflorian 	multiline = 0, nottl = 0, noclass = 0,
681fb015a8Sflorian 	onesoa = 0, use_usec = 0, nocrypto = 0,
691fb015a8Sflorian 	ipv4only = 0, ipv6only = 0;
705185a700Sflorian static uint32_t splitwidth = 0xffffffff;
715185a700Sflorian 
725185a700Sflorian /*% rrcomments are neither explicitly enabled nor disabled by default */
735185a700Sflorian static int rrcomments = 0;
745185a700Sflorian 
755185a700Sflorian /*% opcode text */
765185a700Sflorian static const char * const opcodetext[] = {
775185a700Sflorian 	"QUERY",
785185a700Sflorian 	"IQUERY",
795185a700Sflorian 	"STATUS",
805185a700Sflorian 	"RESERVED3",
815185a700Sflorian 	"NOTIFY",
825185a700Sflorian 	"UPDATE",
835185a700Sflorian 	"RESERVED6",
845185a700Sflorian 	"RESERVED7",
855185a700Sflorian 	"RESERVED8",
865185a700Sflorian 	"RESERVED9",
875185a700Sflorian 	"RESERVED10",
885185a700Sflorian 	"RESERVED11",
895185a700Sflorian 	"RESERVED12",
905185a700Sflorian 	"RESERVED13",
915185a700Sflorian 	"RESERVED14",
925185a700Sflorian 	"RESERVED15"
935185a700Sflorian };
945185a700Sflorian 
955185a700Sflorian /*% return code text */
965185a700Sflorian static const char * const rcodetext[] = {
975185a700Sflorian 	"NOERROR",
985185a700Sflorian 	"FORMERR",
995185a700Sflorian 	"SERVFAIL",
1005185a700Sflorian 	"NXDOMAIN",
1015185a700Sflorian 	"NOTIMP",
1025185a700Sflorian 	"REFUSED",
1035185a700Sflorian 	"YXDOMAIN",
1045185a700Sflorian 	"YXRRSET",
1055185a700Sflorian 	"NXRRSET",
1065185a700Sflorian 	"NOTAUTH",
1075185a700Sflorian 	"NOTZONE",
1085185a700Sflorian 	"RESERVED11",
1095185a700Sflorian 	"RESERVED12",
1105185a700Sflorian 	"RESERVED13",
1115185a700Sflorian 	"RESERVED14",
1125185a700Sflorian 	"RESERVED15",
1135185a700Sflorian 	"BADVERS"
1145185a700Sflorian };
1155185a700Sflorian 
1165185a700Sflorian /*% safe rcodetext[] */
1175185a700Sflorian static const char *
1185185a700Sflorian rcode_totext(dns_rcode_t rcode)
1195185a700Sflorian {
1205185a700Sflorian 	static char buf[sizeof("?65535")];
1215185a700Sflorian 
1225185a700Sflorian 	if (rcode == dns_rcode_badcookie)
1235185a700Sflorian 		return ("BADCOOKIE");
1245185a700Sflorian 	if (rcode >= (sizeof(rcodetext)/sizeof(rcodetext[0]))) {
1255185a700Sflorian 		snprintf(buf, sizeof(buf), "?%u", rcode);
1265185a700Sflorian 		return (buf);
1275185a700Sflorian 	}
1285185a700Sflorian 	return (rcodetext[rcode]);
1295185a700Sflorian }
1305185a700Sflorian 
1315185a700Sflorian /*% print usage */
1325185a700Sflorian static void
1335185a700Sflorian print_usage(FILE *fp) {
1345185a700Sflorian 	fputs(
13565e34726Sschwarze "usage: dig [@server] [-46hiuv] [-b sourceaddr[#port]] [-c class] [-f file]\n"
1365185a700Sflorian "           [-k keyfile] [-p port] [-q name] [-t type] [-x addr]\n"
1375185a700Sflorian "           [-y [hmac:]name:key] [name] [type] [class]\n"
1385185a700Sflorian "           +[no]aaonly +[no]additional +[no]adflag +[no]all +[no]answer\n"
1395185a700Sflorian "           +[no]authority +[no]besteffort +bufsize=# +[no]cdflag +[no]class\n"
1405185a700Sflorian "           +[no]cmd +[no]comments +[no]cookie[=value] +[no]crypto +[no]dnssec\n"
1415185a700Sflorian "           +domain=name +[no]edns[=#] +ednsflags[=#] +[no]ednsnegotiation\n"
1425185a700Sflorian "           +[no]ednsopt[=code[:value]] +[no]expire +[no]fail +[no]identify\n"
1435185a700Sflorian "           +[no]ignore +[no]keepopen +[no]multiline +ndots=# +[no]nsid\n"
1445185a700Sflorian "           +[no]nssearch +[no]onesoa +[no]opcode=# +[no]qr +[no]question\n"
1455185a700Sflorian "           +[no]recurse +retry=# +[no]rrcomments +[no]search +[no]short\n"
1465185a700Sflorian "           +[no]showsearch +[no]split=# +[no]stats +[no]subnet=addr[/prefix]\n"
1475185a700Sflorian "           +[no]tcp +timeout=# +[no]trace +tries=# +[no]ttlid +[no]vc\n", fp);
1485185a700Sflorian }
1495185a700Sflorian 
1505185a700Sflorian static __dead void
1515185a700Sflorian usage(void);
1525185a700Sflorian 
1535185a700Sflorian static void
1545185a700Sflorian usage(void) {
1555185a700Sflorian 	print_usage(stderr);
1565185a700Sflorian 	exit(1);
1575185a700Sflorian }
1585185a700Sflorian 
1595185a700Sflorian /*% version */
1605185a700Sflorian static void
1615185a700Sflorian version(void) {
1625185a700Sflorian 	fputs("dig " VERSION "\n", stderr);
1635185a700Sflorian }
1645185a700Sflorian 
1655185a700Sflorian /*% help */
1665185a700Sflorian static void
1675185a700Sflorian help(void) {
1685185a700Sflorian 	print_usage(stdout);
1695185a700Sflorian }
1705185a700Sflorian 
1715185a700Sflorian /*%
1725185a700Sflorian  * Callback from dighost.c to print the received message.
1735185a700Sflorian  */
1745185a700Sflorian static void
175b1a294b5Sflorian received(unsigned int bytes, struct sockaddr_storage *from, dig_query_t *query) {
1765185a700Sflorian 	time_t tnow;
1775185a700Sflorian 	struct tm tmnow;
1785185a700Sflorian 	char time_str[100];
1795185a700Sflorian 	char fromtext[ISC_SOCKADDR_FORMATSIZE];
1805185a700Sflorian 
1815185a700Sflorian 	isc_sockaddr_format(from, fromtext, sizeof(fromtext));
1825185a700Sflorian 
1835185a700Sflorian 	if (query->lookup->stats && !short_form) {
1845185a700Sflorian 		if (use_usec)
185427f8978Sflorian 			printf(";; Query time: %lld usec\n",
186427f8978Sflorian 			    uelapsed(&query->time_recv, &query->time_sent));
1875185a700Sflorian 		else
188427f8978Sflorian 			printf(";; Query time: %lld msec\n",
189427f8978Sflorian 			    uelapsed(&query->time_recv, &query->time_sent) /
190427f8978Sflorian 			    1000);
1915185a700Sflorian 		printf(";; SERVER: %s(%s)\n", fromtext, query->servname);
1925185a700Sflorian 		time(&tnow);
1935185a700Sflorian 		tmnow  = *localtime(&tnow);
1945185a700Sflorian 
1955185a700Sflorian 		if (strftime(time_str, sizeof(time_str),
1965185a700Sflorian 			     "%a %b %d %H:%M:%S %Z %Y", &tmnow) > 0U)
1975185a700Sflorian 			printf(";; WHEN: %s\n", time_str);
1985185a700Sflorian 		if (query->lookup->doing_xfr) {
1995185a700Sflorian 			printf(";; XFR size: %u records (messages %u, "
2005185a700Sflorian 			       "bytes %llu)\n",
2015185a700Sflorian 			       query->rr_count, query->msg_count,
2025185a700Sflorian 			       query->byte_count);
2035185a700Sflorian 		} else {
2045185a700Sflorian 			printf(";; MSG SIZE  rcvd: %u\n", bytes);
2055185a700Sflorian 		}
2065185a700Sflorian 		if (tsigkey != NULL) {
2075185a700Sflorian 			if (!validated)
2085185a700Sflorian 				puts(";; WARNING -- Some TSIG could not "
2095185a700Sflorian 				     "be validated");
2105185a700Sflorian 		}
2115185a700Sflorian 		if ((tsigkey == NULL) && (keysecret[0] != 0)) {
2125185a700Sflorian 			puts(";; WARNING -- TSIG key was not used.");
2135185a700Sflorian 		}
2145185a700Sflorian 		puts("");
2155185a700Sflorian 	} else if (query->lookup->identify && !short_form) {
2165185a700Sflorian 		if (use_usec)
2175185a700Sflorian 			printf(";; Received %llu bytes "
218427f8978Sflorian 			    "from %s(%s) in %lld us\n\n",
2195185a700Sflorian 			    query->lookup->doing_xfr
2205185a700Sflorian 			    ? query->byte_count
2215185a700Sflorian 			    : (uint64_t)bytes,
222427f8978Sflorian 			    fromtext, query->userarg,
223427f8978Sflorian 			    uelapsed(&query->time_recv, &query->time_sent));
2245185a700Sflorian 		else
2255185a700Sflorian 			printf(";; Received %llu bytes "
226427f8978Sflorian 			    "from %s(%s) in %lld ms\n\n",
2275185a700Sflorian 			    query->lookup->doing_xfr
2285185a700Sflorian 			    ?  query->byte_count
2295185a700Sflorian 			    : (uint64_t)bytes,
230427f8978Sflorian 			    fromtext, query->userarg,
231427f8978Sflorian 			    uelapsed(&query->time_recv, &query->time_sent) /
232427f8978Sflorian 			    1000);
2335185a700Sflorian 	}
2345185a700Sflorian }
2355185a700Sflorian 
2365185a700Sflorian /*
2375185a700Sflorian  * Callback from dighost.c to print that it is trying a server.
2385185a700Sflorian  * Not used in dig.
2395185a700Sflorian  * XXX print_trying
2405185a700Sflorian  */
2415185a700Sflorian static void
2425185a700Sflorian trying(char *frm, dig_lookup_t *lookup) {
2435185a700Sflorian 	UNUSED(frm);
2445185a700Sflorian 	UNUSED(lookup);
2455185a700Sflorian }
2465185a700Sflorian 
2475185a700Sflorian /*%
2485185a700Sflorian  * Internal print routine used to print short form replies.
2495185a700Sflorian  */
2505185a700Sflorian static isc_result_t
2515185a700Sflorian say_message(dns_rdata_t *rdata, dig_query_t *query, isc_buffer_t *buf) {
2525185a700Sflorian 	isc_result_t result;
2535185a700Sflorian 	char store[sizeof(" in 18446744073709551616 us.")];
2545185a700Sflorian 	unsigned int styleflags = 0;
2555185a700Sflorian 
2565185a700Sflorian 	if (query->lookup->trace || query->lookup->ns_search_only) {
2575185a700Sflorian 		result = dns_rdatatype_totext(rdata->type, buf);
2585185a700Sflorian 		if (result != ISC_R_SUCCESS)
2595185a700Sflorian 			return (result);
2605185a700Sflorian 		ADD_STRING(buf, " ");
2615185a700Sflorian 	}
2625185a700Sflorian 
2635185a700Sflorian 	/* Turn on rrcomments if explicitly enabled */
2645185a700Sflorian 	if (rrcomments > 0)
2655185a700Sflorian 		styleflags |= DNS_STYLEFLAG_RRCOMMENT;
2665185a700Sflorian 	if (nocrypto)
2675185a700Sflorian 		styleflags |= DNS_STYLEFLAG_NOCRYPTO;
2685185a700Sflorian 	result = dns_rdata_tofmttext(rdata, NULL, styleflags, 0,
2695185a700Sflorian 				     splitwidth, " ", buf);
2705185a700Sflorian 	if (result == ISC_R_NOSPACE)
2715185a700Sflorian 		return (result);
2725185a700Sflorian 	check_result(result, "dns_rdata_totext");
2735185a700Sflorian 	if (query->lookup->identify) {
2745185a700Sflorian 		ADD_STRING(buf, " from server ");
2755185a700Sflorian 		ADD_STRING(buf, query->servname);
2765185a700Sflorian 		if (use_usec)
277427f8978Sflorian 			snprintf(store, sizeof(store), " in %lld us.",
278427f8978Sflorian 			    uelapsed(&query->time_recv, &query->time_sent));
2795185a700Sflorian 		else
280427f8978Sflorian 			snprintf(store, sizeof(store), " in %lld ms.",
281427f8978Sflorian 			    uelapsed(&query->time_recv, &query->time_sent) /
282427f8978Sflorian 			    1000);
2835185a700Sflorian 		ADD_STRING(buf, store);
2845185a700Sflorian 	}
2855185a700Sflorian 	ADD_STRING(buf, "\n");
2865185a700Sflorian 	return (ISC_R_SUCCESS);
2875185a700Sflorian }
2885185a700Sflorian 
2895185a700Sflorian /*%
2905185a700Sflorian  * short_form message print handler.  Calls above say_message()
2915185a700Sflorian  */
2925185a700Sflorian static isc_result_t
2935185a700Sflorian short_answer(dns_message_t *msg, dns_messagetextflag_t flags,
2945185a700Sflorian 	     isc_buffer_t *buf, dig_query_t *query)
2955185a700Sflorian {
2965185a700Sflorian 	dns_name_t *name;
2975185a700Sflorian 	dns_rdataset_t *rdataset;
2985185a700Sflorian 	isc_result_t result, loopresult;
2995185a700Sflorian 	dns_name_t empty_name;
3005185a700Sflorian 	dns_rdata_t rdata = DNS_RDATA_INIT;
3015185a700Sflorian 
3025185a700Sflorian 	UNUSED(flags);
3035185a700Sflorian 
3045185a700Sflorian 	dns_name_init(&empty_name, NULL);
3055185a700Sflorian 	result = dns_message_firstname(msg, DNS_SECTION_ANSWER);
3065185a700Sflorian 	if (result == ISC_R_NOMORE)
3075185a700Sflorian 		return (ISC_R_SUCCESS);
3085185a700Sflorian 	else if (result != ISC_R_SUCCESS)
3095185a700Sflorian 		return (result);
3105185a700Sflorian 
3115185a700Sflorian 	for (;;) {
3125185a700Sflorian 		name = NULL;
3135185a700Sflorian 		dns_message_currentname(msg, DNS_SECTION_ANSWER, &name);
3145185a700Sflorian 
3155185a700Sflorian 		for (rdataset = ISC_LIST_HEAD(name->list);
3165185a700Sflorian 		     rdataset != NULL;
3175185a700Sflorian 		     rdataset = ISC_LIST_NEXT(rdataset, link)) {
3185185a700Sflorian 			loopresult = dns_rdataset_first(rdataset);
3195185a700Sflorian 			while (loopresult == ISC_R_SUCCESS) {
3205185a700Sflorian 				dns_rdataset_current(rdataset, &rdata);
3215185a700Sflorian 				result = say_message(&rdata, query,
3225185a700Sflorian 						     buf);
3235185a700Sflorian 				if (result == ISC_R_NOSPACE)
3245185a700Sflorian 					return (result);
3255185a700Sflorian 				check_result(result, "say_message");
3265185a700Sflorian 				loopresult = dns_rdataset_next(rdataset);
3275185a700Sflorian 				dns_rdata_reset(&rdata);
3285185a700Sflorian 			}
3295185a700Sflorian 		}
3305185a700Sflorian 		result = dns_message_nextname(msg, DNS_SECTION_ANSWER);
3315185a700Sflorian 		if (result == ISC_R_NOMORE)
3325185a700Sflorian 			break;
3335185a700Sflorian 		else if (result != ISC_R_SUCCESS)
3345185a700Sflorian 			return (result);
3355185a700Sflorian 	}
3365185a700Sflorian 
3375185a700Sflorian 	return (ISC_R_SUCCESS);
3385185a700Sflorian }
3395185a700Sflorian 
3401fb015a8Sflorian static int
3415185a700Sflorian isdotlocal(dns_message_t *msg) {
3425185a700Sflorian 	isc_result_t result;
3435185a700Sflorian 	static unsigned char local_ndata[] = { "\005local\0" };
3445185a700Sflorian 	static unsigned char local_offsets[] = { 0, 6 };
3455185a700Sflorian 	static dns_name_t local =
3465185a700Sflorian 		DNS_NAME_INITABSOLUTE(local_ndata, local_offsets);
3475185a700Sflorian 
3485185a700Sflorian 	for (result = dns_message_firstname(msg, DNS_SECTION_QUESTION);
3495185a700Sflorian 	     result == ISC_R_SUCCESS;
3505185a700Sflorian 	     result = dns_message_nextname(msg, DNS_SECTION_QUESTION))
3515185a700Sflorian 	{
3525185a700Sflorian 		dns_name_t *name = NULL;
3535185a700Sflorian 		dns_message_currentname(msg, DNS_SECTION_QUESTION, &name);
3545185a700Sflorian 		if (dns_name_issubdomain(name, &local))
3551fb015a8Sflorian 			return (1);
3565185a700Sflorian 	}
3571fb015a8Sflorian 	return (0);
3585185a700Sflorian }
3595185a700Sflorian 
3605185a700Sflorian /*
3615185a700Sflorian  * Callback from dighost.c to print the reply from a server
3625185a700Sflorian  */
3635185a700Sflorian static isc_result_t
3641fb015a8Sflorian printmessage(dig_query_t *query, dns_message_t *msg, int headers) {
3655185a700Sflorian 	isc_result_t result;
3665185a700Sflorian 	dns_messagetextflag_t flags;
3675185a700Sflorian 	isc_buffer_t *buf = NULL;
3685185a700Sflorian 	unsigned int len = OUTPUTBUF;
3695185a700Sflorian 	dns_master_style_t *style = NULL;
3705185a700Sflorian 	unsigned int styleflags = 0;
3715185a700Sflorian 
3725185a700Sflorian 	styleflags |= DNS_STYLEFLAG_REL_OWNER;
3735185a700Sflorian 	if (query->lookup->comments)
3745185a700Sflorian 		styleflags |= DNS_STYLEFLAG_COMMENT;
3755185a700Sflorian 	/* Turn on rrcomments if explicitly enabled */
3765185a700Sflorian 	if (rrcomments > 0)
3775185a700Sflorian 		styleflags |= DNS_STYLEFLAG_RRCOMMENT;
3785185a700Sflorian 	if (nottl)
3795185a700Sflorian 		styleflags |= DNS_STYLEFLAG_NO_TTL;
3805185a700Sflorian 	if (noclass)
3815185a700Sflorian 		styleflags |= DNS_STYLEFLAG_NO_CLASS;
3825185a700Sflorian 	if (nocrypto)
3835185a700Sflorian 		styleflags |= DNS_STYLEFLAG_NOCRYPTO;
3845185a700Sflorian 	if (multiline) {
3855185a700Sflorian 		styleflags |= DNS_STYLEFLAG_OMIT_OWNER;
3865185a700Sflorian 		styleflags |= DNS_STYLEFLAG_OMIT_CLASS;
3875185a700Sflorian 		styleflags |= DNS_STYLEFLAG_REL_DATA;
3885185a700Sflorian 		styleflags |= DNS_STYLEFLAG_OMIT_TTL;
3895185a700Sflorian 		styleflags |= DNS_STYLEFLAG_TTL;
3905185a700Sflorian 		styleflags |= DNS_STYLEFLAG_MULTILINE;
3915185a700Sflorian 		/* Turn on rrcomments unless explicitly disabled */
3925185a700Sflorian 		if (rrcomments >= 0)
3935185a700Sflorian 			styleflags |= DNS_STYLEFLAG_RRCOMMENT;
3945185a700Sflorian 	}
3955185a700Sflorian 	if (multiline || (nottl && noclass))
3965185a700Sflorian 		result = dns_master_stylecreate2(&style, styleflags,
3975185a700Sflorian 						 24, 24, 24, 32, 80, 8,
3985185a700Sflorian 						 splitwidth);
3995185a700Sflorian 	else if (nottl || noclass)
4005185a700Sflorian 		result = dns_master_stylecreate2(&style, styleflags,
4015185a700Sflorian 						 24, 24, 32, 40, 80, 8,
4025185a700Sflorian 						 splitwidth);
4035185a700Sflorian 	else
4045185a700Sflorian 		result = dns_master_stylecreate2(&style, styleflags,
4055185a700Sflorian 						 24, 32, 40, 48, 80, 8,
4065185a700Sflorian 						 splitwidth);
4075185a700Sflorian 	check_result(result, "dns_master_stylecreate");
4085185a700Sflorian 
4095185a700Sflorian 	if (query->lookup->cmdline[0] != 0) {
4105185a700Sflorian 		if (!short_form)
4115185a700Sflorian 			fputs(query->lookup->cmdline, stdout);
4125185a700Sflorian 		query->lookup->cmdline[0]=0;
4135185a700Sflorian 	}
4145185a700Sflorian 	debug("printmessage(%s %s %s)", headers ? "headers" : "noheaders",
4155185a700Sflorian 	      query->lookup->comments ? "comments" : "nocomments",
4165185a700Sflorian 	      short_form ? "short_form" : "long_form");
4175185a700Sflorian 
4185185a700Sflorian 	flags = 0;
4195185a700Sflorian 	if (!headers) {
4205185a700Sflorian 		flags |= DNS_MESSAGETEXTFLAG_NOHEADERS;
4215185a700Sflorian 		flags |= DNS_MESSAGETEXTFLAG_NOCOMMENTS;
4225185a700Sflorian 	}
4235185a700Sflorian 	if (onesoa && query->lookup->rdtype == dns_rdatatype_axfr)
4245185a700Sflorian 		flags |= (query->msg_count == 0) ? DNS_MESSAGETEXTFLAG_ONESOA :
4255185a700Sflorian 						   DNS_MESSAGETEXTFLAG_OMITSOA;
4265185a700Sflorian 	if (!query->lookup->comments)
4275185a700Sflorian 		flags |= DNS_MESSAGETEXTFLAG_NOCOMMENTS;
4285185a700Sflorian 
4295185a700Sflorian 	result = isc_buffer_allocate(&buf, len);
4305185a700Sflorian 	check_result(result, "isc_buffer_allocate");
4315185a700Sflorian 
4325185a700Sflorian 	if (query->lookup->comments && !short_form) {
4335185a700Sflorian 		if (query->lookup->cmdline[0] != 0)
4345185a700Sflorian 			printf("; %s\n", query->lookup->cmdline);
4355185a700Sflorian 		if (msg == query->lookup->sendmsg)
4365185a700Sflorian 			printf(";; Sending:\n");
4375185a700Sflorian 		else
4385185a700Sflorian 			printf(";; Got answer:\n");
4395185a700Sflorian 
4405185a700Sflorian 		if (headers) {
4415185a700Sflorian 			if (isdotlocal(msg)) {
4425185a700Sflorian 				printf(";; WARNING: .local is reserved for "
4435185a700Sflorian 				       "Multicast DNS\n;; You are currently "
4445185a700Sflorian 				       "testing what happens when an mDNS "
4455185a700Sflorian 				       "query is leaked to DNS\n");
4465185a700Sflorian 			}
4475185a700Sflorian 			printf(";; ->>HEADER<<- opcode: %s, status: %s, "
4485185a700Sflorian 			       "id: %u\n",
4495185a700Sflorian 			       opcodetext[msg->opcode],
4505185a700Sflorian 			       rcode_totext(msg->rcode),
4515185a700Sflorian 			       msg->id);
4525185a700Sflorian 			printf(";; flags:");
4535185a700Sflorian 			if ((msg->flags & DNS_MESSAGEFLAG_QR) != 0)
4545185a700Sflorian 				printf(" qr");
4555185a700Sflorian 			if ((msg->flags & DNS_MESSAGEFLAG_AA) != 0)
4565185a700Sflorian 				printf(" aa");
4575185a700Sflorian 			if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0)
4585185a700Sflorian 				printf(" tc");
4595185a700Sflorian 			if ((msg->flags & DNS_MESSAGEFLAG_RD) != 0)
4605185a700Sflorian 				printf(" rd");
4615185a700Sflorian 			if ((msg->flags & DNS_MESSAGEFLAG_RA) != 0)
4625185a700Sflorian 				printf(" ra");
4635185a700Sflorian 			if ((msg->flags & DNS_MESSAGEFLAG_AD) != 0)
4645185a700Sflorian 				printf(" ad");
4655185a700Sflorian 			if ((msg->flags & DNS_MESSAGEFLAG_CD) != 0)
4665185a700Sflorian 				printf(" cd");
4675185a700Sflorian 			if ((msg->flags & 0x0040U) != 0)
4685185a700Sflorian 				printf("; MBZ: 0x4");
4695185a700Sflorian 
4705185a700Sflorian 			printf("; QUERY: %u, ANSWER: %u, "
4715185a700Sflorian 			       "AUTHORITY: %u, ADDITIONAL: %u\n",
4725185a700Sflorian 			       msg->counts[DNS_SECTION_QUESTION],
4735185a700Sflorian 			       msg->counts[DNS_SECTION_ANSWER],
4745185a700Sflorian 			       msg->counts[DNS_SECTION_AUTHORITY],
4755185a700Sflorian 			       msg->counts[DNS_SECTION_ADDITIONAL]);
4765185a700Sflorian 
4775185a700Sflorian 			if (msg != query->lookup->sendmsg &&
4785185a700Sflorian 			    (msg->flags & DNS_MESSAGEFLAG_RD) != 0 &&
4795185a700Sflorian 			    (msg->flags & DNS_MESSAGEFLAG_RA) == 0)
4805185a700Sflorian 				printf(";; WARNING: recursion requested "
4815185a700Sflorian 				       "but not available\n");
4825185a700Sflorian 		}
4835185a700Sflorian 		if (msg != query->lookup->sendmsg &&
4845185a700Sflorian 		    query->lookup->edns != -1 && msg->opt == NULL &&
4855185a700Sflorian 		    (msg->rcode == dns_rcode_formerr ||
4865185a700Sflorian 		     msg->rcode == dns_rcode_notimp))
4875185a700Sflorian 			printf("\n;; WARNING: EDNS query returned status "
4885185a700Sflorian 			       "%s - retry with '%s+noedns'\n",
4895185a700Sflorian 			       rcode_totext(msg->rcode),
4905185a700Sflorian 			       query->lookup->dnssec ? "+nodnssec ": "");
4915185a700Sflorian 		if (msg != query->lookup->sendmsg && extrabytes != 0U)
4925185a700Sflorian 			printf(";; WARNING: Message has %u extra byte%s at "
4935185a700Sflorian 			       "end\n", extrabytes, extrabytes != 0 ? "s" : "");
4945185a700Sflorian 	}
4955185a700Sflorian 
4965185a700Sflorian repopulate_buffer:
4975185a700Sflorian 
4985185a700Sflorian 	if (query->lookup->comments && headers && !short_form) {
4995185a700Sflorian 		result = dns_message_pseudosectiontotext(msg,
5005185a700Sflorian 			 DNS_PSEUDOSECTION_OPT,
501*9835a5e1Sflorian 			 style, flags, query->lookup->textname, buf);
5025185a700Sflorian 		if (result == ISC_R_NOSPACE) {
5035185a700Sflorian buftoosmall:
5045185a700Sflorian 			len += OUTPUTBUF;
5055185a700Sflorian 			isc_buffer_free(&buf);
5065185a700Sflorian 			result = isc_buffer_allocate(&buf, len);
5075185a700Sflorian 			if (result == ISC_R_SUCCESS)
5085185a700Sflorian 				goto repopulate_buffer;
5095185a700Sflorian 			else
5105185a700Sflorian 				goto cleanup;
5115185a700Sflorian 		}
5125185a700Sflorian 		check_result(result,
5135185a700Sflorian 		     "dns_message_pseudosectiontotext");
5145185a700Sflorian 	}
5155185a700Sflorian 
5165185a700Sflorian 	if (query->lookup->section_question && headers) {
5175185a700Sflorian 		if (!short_form) {
5185185a700Sflorian 			result = dns_message_sectiontotext(msg,
5195185a700Sflorian 						       DNS_SECTION_QUESTION,
5205185a700Sflorian 						       style, flags, buf);
5215185a700Sflorian 			if (result == ISC_R_NOSPACE)
5225185a700Sflorian 				goto buftoosmall;
5235185a700Sflorian 			check_result(result, "dns_message_sectiontotext");
5245185a700Sflorian 		}
5255185a700Sflorian 	}
5265185a700Sflorian 	if (query->lookup->section_answer) {
5275185a700Sflorian 		if (!short_form) {
5285185a700Sflorian 			result = dns_message_sectiontotext(msg,
5295185a700Sflorian 						       DNS_SECTION_ANSWER,
5305185a700Sflorian 						       style, flags, buf);
5315185a700Sflorian 			if (result == ISC_R_NOSPACE)
5325185a700Sflorian 				goto buftoosmall;
5335185a700Sflorian 			check_result(result, "dns_message_sectiontotext");
5345185a700Sflorian 		} else {
5355185a700Sflorian 			result = short_answer(msg, flags, buf, query);
5365185a700Sflorian 			if (result == ISC_R_NOSPACE)
5375185a700Sflorian 				goto buftoosmall;
5385185a700Sflorian 			check_result(result, "short_answer");
5395185a700Sflorian 		}
5405185a700Sflorian 	}
5415185a700Sflorian 	if (query->lookup->section_authority) {
5425185a700Sflorian 		if (!short_form) {
5435185a700Sflorian 			result = dns_message_sectiontotext(msg,
5445185a700Sflorian 						       DNS_SECTION_AUTHORITY,
5455185a700Sflorian 						       style, flags, buf);
5465185a700Sflorian 			if (result == ISC_R_NOSPACE)
5475185a700Sflorian 				goto buftoosmall;
5485185a700Sflorian 			check_result(result, "dns_message_sectiontotext");
5495185a700Sflorian 		}
5505185a700Sflorian 	}
5515185a700Sflorian 	if (query->lookup->section_additional) {
5525185a700Sflorian 		if (!short_form) {
5535185a700Sflorian 			result = dns_message_sectiontotext(msg,
5545185a700Sflorian 						      DNS_SECTION_ADDITIONAL,
5555185a700Sflorian 						      style, flags, buf);
5565185a700Sflorian 			if (result == ISC_R_NOSPACE)
5575185a700Sflorian 				goto buftoosmall;
5585185a700Sflorian 			check_result(result, "dns_message_sectiontotext");
5595185a700Sflorian 			/*
5605185a700Sflorian 			 * Only print the signature on the first record.
5615185a700Sflorian 			 */
5625185a700Sflorian 			if (headers) {
5635185a700Sflorian 				result = dns_message_pseudosectiontotext(
5645185a700Sflorian 						   msg,
5655185a700Sflorian 						   DNS_PSEUDOSECTION_TSIG,
566*9835a5e1Sflorian 						   style, flags,
567*9835a5e1Sflorian 						   query->lookup->textname,
568*9835a5e1Sflorian 						   buf);
5695185a700Sflorian 				if (result == ISC_R_NOSPACE)
5705185a700Sflorian 					goto buftoosmall;
5715185a700Sflorian 				check_result(result,
5725185a700Sflorian 					  "dns_message_pseudosectiontotext");
5735185a700Sflorian 				result = dns_message_pseudosectiontotext(
5745185a700Sflorian 						   msg,
5755185a700Sflorian 						   DNS_PSEUDOSECTION_SIG0,
576*9835a5e1Sflorian 						   style, flags,
577*9835a5e1Sflorian 						   query->lookup->textname,
578*9835a5e1Sflorian 						   buf);
5795185a700Sflorian 				if (result == ISC_R_NOSPACE)
5805185a700Sflorian 					goto buftoosmall;
5815185a700Sflorian 				check_result(result,
5825185a700Sflorian 					   "dns_message_pseudosectiontotext");
5835185a700Sflorian 			}
5845185a700Sflorian 		}
5855185a700Sflorian 	}
5865185a700Sflorian 
5875185a700Sflorian 	if (headers && query->lookup->comments && !short_form)
5885185a700Sflorian 		printf("\n");
5895185a700Sflorian 
5905185a700Sflorian 	printf("%.*s", (int)isc_buffer_usedlength(buf),
5915185a700Sflorian 	       (char *)isc_buffer_base(buf));
5925185a700Sflorian 	isc_buffer_free(&buf);
5935185a700Sflorian 
5945185a700Sflorian cleanup:
5955185a700Sflorian 	if (style != NULL)
5965185a700Sflorian 		dns_master_styledestroy(&style);
5975185a700Sflorian 	return (result);
5985185a700Sflorian }
5995185a700Sflorian 
6005185a700Sflorian /*%
6015185a700Sflorian  * print the greeting message when the program first starts up.
6025185a700Sflorian  */
6035185a700Sflorian static void
6045185a700Sflorian printgreeting(int argc, char **argv, dig_lookup_t *lookup) {
6055185a700Sflorian 	int i;
6061fb015a8Sflorian 	static int first = 1;
6075185a700Sflorian 	char append[MXNAME];
6085185a700Sflorian 
6095185a700Sflorian 	if (printcmd) {
6105185a700Sflorian 		snprintf(lookup->cmdline, sizeof(lookup->cmdline),
6115185a700Sflorian 			 "%s; <<>> dig " VERSION " <<>>",
6125185a700Sflorian 			 first?"\n":"");
6135185a700Sflorian 		i = 1;
6145185a700Sflorian 		while (i < argc) {
6155185a700Sflorian 			snprintf(append, sizeof(append), " %s", argv[i++]);
6165185a700Sflorian 			strlcat(lookup->cmdline, append,
6175185a700Sflorian 				sizeof(lookup->cmdline));
6185185a700Sflorian 		}
6195185a700Sflorian 		strlcat(lookup->cmdline, "\n", sizeof(lookup->cmdline));
6205185a700Sflorian 		if (first && addresscount != 0) {
6215185a700Sflorian 			snprintf(append, sizeof(append),
6225185a700Sflorian 				 "; (%d server%s found)\n",
6235185a700Sflorian 				 addresscount,
6245185a700Sflorian 				 addresscount > 1 ? "s" : "");
6255185a700Sflorian 			strlcat(lookup->cmdline, append,
6265185a700Sflorian 				sizeof(lookup->cmdline));
6275185a700Sflorian 		}
6285185a700Sflorian 		if (first) {
6295185a700Sflorian 			snprintf(append, sizeof(append),
6305185a700Sflorian 				 ";; global options:%s%s\n",
6315185a700Sflorian 				 short_form ? " +short" : "",
6325185a700Sflorian 				 printcmd ? " +cmd" : "");
6331fb015a8Sflorian 			first = 0;
6345185a700Sflorian 			strlcat(lookup->cmdline, append,
6355185a700Sflorian 				sizeof(lookup->cmdline));
6365185a700Sflorian 		}
6375185a700Sflorian 	}
6385185a700Sflorian }
6395185a700Sflorian 
6405185a700Sflorian static void
6411fb015a8Sflorian plus_option(const char *option, int is_batchfile,
6425185a700Sflorian 	    dig_lookup_t *lookup)
6435185a700Sflorian {
6445185a700Sflorian 	isc_result_t result;
6455185a700Sflorian 	char option_store[256];
646c49c5f05Sflorian 	char *cmd, *value, *ptr, *code, *ep;
647c49c5f05Sflorian 	const char *errstr;
648c49c5f05Sflorian 	long lval;
6495185a700Sflorian 	uint32_t num;
6501fb015a8Sflorian 	int state = 1;
6515185a700Sflorian 	size_t n;
6525185a700Sflorian 
6535185a700Sflorian 	strlcpy(option_store, option, sizeof(option_store));
6545185a700Sflorian 	ptr = option_store;
6555185a700Sflorian 	cmd = next_token(&ptr, "=");
6565185a700Sflorian 	if (cmd == NULL) {
6575185a700Sflorian 		printf(";; Invalid option %s\n", option_store);
6585185a700Sflorian 		return;
6595185a700Sflorian 	}
6605185a700Sflorian 	value = ptr;
6615185a700Sflorian 	if (strncasecmp(cmd, "no", 2)==0) {
6625185a700Sflorian 		cmd += 2;
6631fb015a8Sflorian 		state = 0;
6645185a700Sflorian 	}
6655185a700Sflorian 
6665185a700Sflorian #define FULLCHECK(A) \
6675185a700Sflorian 	do { \
6685185a700Sflorian 		size_t _l = strlen(cmd); \
6695185a700Sflorian 		if (_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) \
6705185a700Sflorian 			goto invalid_option; \
6715185a700Sflorian 	} while (0)
6725185a700Sflorian #define FULLCHECK2(A, B) \
6735185a700Sflorian 	do { \
6745185a700Sflorian 		size_t _l = strlen(cmd); \
6755185a700Sflorian 		if ((_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) && \
6765185a700Sflorian 		    (_l >= sizeof(B) || strncasecmp(cmd, B, _l) != 0)) \
6775185a700Sflorian 			goto invalid_option; \
6785185a700Sflorian 	} while (0)
6795185a700Sflorian 
6805185a700Sflorian 	switch (cmd[0]) {
6815185a700Sflorian 	case 'a':
6825185a700Sflorian 		switch (cmd[1]) {
6835185a700Sflorian 		case 'a': /* aaonly / aaflag */
6845185a700Sflorian 			FULLCHECK2("aaonly", "aaflag");
6855185a700Sflorian 			lookup->aaonly = state;
6865185a700Sflorian 			break;
6875185a700Sflorian 		case 'd':
6885185a700Sflorian 			switch (cmd[2]) {
6895185a700Sflorian 			case 'd': /* additional */
6905185a700Sflorian 				FULLCHECK("additional");
6915185a700Sflorian 				lookup->section_additional = state;
6925185a700Sflorian 				break;
6935185a700Sflorian 			case 'f': /* adflag */
6945185a700Sflorian 			case '\0': /* +ad is a synonym for +adflag */
6955185a700Sflorian 				FULLCHECK("adflag");
6965185a700Sflorian 				lookup->adflag = state;
6975185a700Sflorian 				break;
6985185a700Sflorian 			default:
6995185a700Sflorian 				goto invalid_option;
7005185a700Sflorian 			}
7015185a700Sflorian 			break;
7025185a700Sflorian 		case 'l': /* all */
7035185a700Sflorian 			FULLCHECK("all");
7045185a700Sflorian 			lookup->section_question = state;
7055185a700Sflorian 			lookup->section_authority = state;
7065185a700Sflorian 			lookup->section_answer = state;
7075185a700Sflorian 			lookup->section_additional = state;
7085185a700Sflorian 			lookup->comments = state;
7095185a700Sflorian 			lookup->stats = state;
7105185a700Sflorian 			printcmd = state;
7115185a700Sflorian 			break;
7125185a700Sflorian 		case 'n': /* answer */
7135185a700Sflorian 			FULLCHECK("answer");
7145185a700Sflorian 			lookup->section_answer = state;
7155185a700Sflorian 			break;
7165185a700Sflorian 		case 'u': /* authority */
7175185a700Sflorian 			FULLCHECK("authority");
7185185a700Sflorian 			lookup->section_authority = state;
7195185a700Sflorian 			break;
7205185a700Sflorian 		default:
7215185a700Sflorian 			goto invalid_option;
7225185a700Sflorian 		}
7235185a700Sflorian 		break;
7245185a700Sflorian 	case 'b':
7255185a700Sflorian 		switch (cmd[1]) {
7265185a700Sflorian 		case 'e':/* besteffort */
7275185a700Sflorian 			FULLCHECK("besteffort");
7285185a700Sflorian 			lookup->besteffort = state;
7295185a700Sflorian 			break;
7305185a700Sflorian 		case 'u':/* bufsize */
7315185a700Sflorian 			FULLCHECK("bufsize");
7325185a700Sflorian 			if (value == NULL)
7335185a700Sflorian 				goto need_value;
7345185a700Sflorian 			if (!state)
7355185a700Sflorian 				goto invalid_option;
736c49c5f05Sflorian 			num = strtonum(value, 0, COMMSIZE, &errstr);
737c49c5f05Sflorian 			if (errstr != NULL)
738c49c5f05Sflorian 				fatal("buffer size is %s: '%s'", errstr, value);
7395185a700Sflorian 			lookup->udpsize = num;
7405185a700Sflorian 			break;
7415185a700Sflorian 		default:
7425185a700Sflorian 			goto invalid_option;
7435185a700Sflorian 		}
7445185a700Sflorian 		break;
7455185a700Sflorian 	case 'c':
7465185a700Sflorian 		switch (cmd[1]) {
7475185a700Sflorian 		case 'd':/* cdflag */
7485185a700Sflorian 			switch (cmd[2]) {
7495185a700Sflorian 			case 'f': /* cdflag */
7505185a700Sflorian 			case '\0': /* +cd is a synonym for +cdflag */
7515185a700Sflorian 				FULLCHECK("cdflag");
7525185a700Sflorian 				lookup->cdflag = state;
7535185a700Sflorian 				break;
7545185a700Sflorian 			default:
7555185a700Sflorian 				goto invalid_option;
7565185a700Sflorian 			}
7575185a700Sflorian 			break;
7585185a700Sflorian 		case 'l': /* class */
7595185a700Sflorian 			/* keep +cl for backwards compatibility */
7605185a700Sflorian 			FULLCHECK2("cl", "class");
7611fb015a8Sflorian 			noclass = !state;
7625185a700Sflorian 			break;
7635185a700Sflorian 		case 'm': /* cmd */
7645185a700Sflorian 			FULLCHECK("cmd");
7655185a700Sflorian 			printcmd = state;
7665185a700Sflorian 			break;
7675185a700Sflorian 		case 'o': /* comments */
7685185a700Sflorian 			switch (cmd[2]) {
7695185a700Sflorian 			case 'o':
7705185a700Sflorian 				FULLCHECK("cookie");
7715185a700Sflorian 				goto sit;
7725185a700Sflorian 			case 'm':
7735185a700Sflorian 				FULLCHECK("comments");
7745185a700Sflorian 				lookup->comments = state;
7755185a700Sflorian 				if (lookup == default_lookup)
7765185a700Sflorian 					pluscomm = state;
7775185a700Sflorian 				break;
7785185a700Sflorian 			default:
7795185a700Sflorian 				goto invalid_option;
7805185a700Sflorian 			}
7815185a700Sflorian 			break;
7825185a700Sflorian 		case 'r':
7835185a700Sflorian 			FULLCHECK("crypto");
7841fb015a8Sflorian 			nocrypto = !state;
7855185a700Sflorian 			break;
7865185a700Sflorian 		default:
7875185a700Sflorian 			goto invalid_option;
7885185a700Sflorian 		}
7895185a700Sflorian 		break;
7905185a700Sflorian 	case 'd':
7915185a700Sflorian 		switch (cmd[1]) {
7925185a700Sflorian 		case 'e': /* defname */
7935185a700Sflorian 			FULLCHECK("defname");
7945185a700Sflorian 			if (!lookup->trace) {
7955185a700Sflorian 				usesearch = state;
7965185a700Sflorian 			}
7975185a700Sflorian 			break;
7985185a700Sflorian 		case 'n': /* dnssec */
7995185a700Sflorian 			FULLCHECK("dnssec");
8005185a700Sflorian 			if (state && lookup->edns == -1)
8015185a700Sflorian 				lookup->edns = 0;
8025185a700Sflorian 			lookup->dnssec = state;
8035185a700Sflorian 			break;
8045185a700Sflorian 		case 'o': /* domain */
8055185a700Sflorian 			FULLCHECK("domain");
8065185a700Sflorian 			if (value == NULL)
8075185a700Sflorian 				goto need_value;
8085185a700Sflorian 			if (!state)
8095185a700Sflorian 				goto invalid_option;
8105185a700Sflorian 			strlcpy(domainopt, value, sizeof(domainopt));
8115185a700Sflorian 			break;
8125185a700Sflorian 		default:
8135185a700Sflorian 			goto invalid_option;
8145185a700Sflorian 		}
8155185a700Sflorian 		break;
8165185a700Sflorian 	case 'e':
8175185a700Sflorian 		switch (cmd[1]) {
8185185a700Sflorian 		case 'd':
8195185a700Sflorian 			switch(cmd[2]) {
8205185a700Sflorian 			case 'n':
8215185a700Sflorian 				switch (cmd[3]) {
8225185a700Sflorian 				case 's':
8235185a700Sflorian 					switch (cmd[4]) {
8245185a700Sflorian 					case 0:
8255185a700Sflorian 						FULLCHECK("edns");
8265185a700Sflorian 						if (!state) {
8275185a700Sflorian 							lookup->edns = -1;
8285185a700Sflorian 							break;
8295185a700Sflorian 						}
8305185a700Sflorian 						if (value == NULL) {
8315185a700Sflorian 							lookup->edns = 0;
8325185a700Sflorian 							break;
8335185a700Sflorian 						}
834c49c5f05Sflorian 						num = strtonum(value, 0, 255,
835c49c5f05Sflorian 						    &errstr);
836c49c5f05Sflorian 						if (errstr != NULL)
837c49c5f05Sflorian 							fatal("edns is %s: "
838c49c5f05Sflorian 							    "'%s'", errstr,
839c49c5f05Sflorian 							    value);
8405185a700Sflorian 						lookup->edns = num;
8415185a700Sflorian 						break;
8425185a700Sflorian 					case 'f':
8435185a700Sflorian 						FULLCHECK("ednsflags");
8445185a700Sflorian 						if (!state) {
8455185a700Sflorian 							lookup->ednsflags = 0;
8465185a700Sflorian 							break;
8475185a700Sflorian 						}
8485185a700Sflorian 						if (value == NULL) {
8495185a700Sflorian 							lookup->ednsflags = 0;
8505185a700Sflorian 							break;
8515185a700Sflorian 						}
852c49c5f05Sflorian 						errno = 0;
853c49c5f05Sflorian 						lval = strtol(value, &ep, 0);
854c49c5f05Sflorian 						if (value[0] == '\0' || *ep !=
855c49c5f05Sflorian 						    '\0' || lval < 0 || lval >
856c49c5f05Sflorian 						    0xffff || errno != 0)
8575185a700Sflorian 							fatal("Couldn't parse "
8585185a700Sflorian 							      "ednsflags");
859c49c5f05Sflorian 						lookup->ednsflags = lval;
8605185a700Sflorian 						break;
8615185a700Sflorian 					case 'n':
8625185a700Sflorian 						FULLCHECK("ednsnegotiation");
8635185a700Sflorian 						lookup->ednsneg = state;
8645185a700Sflorian 						break;
8655185a700Sflorian 					case 'o':
8665185a700Sflorian 						FULLCHECK("ednsopt");
8675185a700Sflorian 						if (!state) {
8685185a700Sflorian 							lookup->ednsoptscnt = 0;
8695185a700Sflorian 							break;
8705185a700Sflorian 						}
8715185a700Sflorian 						if (value == NULL)
8725185a700Sflorian 							fatal("ednsopt no "
8735185a700Sflorian 							      "code point "
8745185a700Sflorian 							      "specified");
8755185a700Sflorian 						code = next_token(&value, ":");
8765185a700Sflorian 						save_opt(lookup, code, value);
8775185a700Sflorian 						break;
8785185a700Sflorian 					default:
8795185a700Sflorian 						goto invalid_option;
8805185a700Sflorian 					}
8815185a700Sflorian 					break;
8825185a700Sflorian 				default:
8835185a700Sflorian 					goto invalid_option;
8845185a700Sflorian 				}
8855185a700Sflorian 				break;
8865185a700Sflorian 			default:
8875185a700Sflorian 				goto invalid_option;
8885185a700Sflorian 			}
8895185a700Sflorian 			break;
8905185a700Sflorian 		case 'x':
8915185a700Sflorian 			FULLCHECK("expire");
8925185a700Sflorian 			lookup->expire = state;
8935185a700Sflorian 			break;
8945185a700Sflorian 		default:
8955185a700Sflorian 			goto invalid_option;
8965185a700Sflorian 		}
8975185a700Sflorian 		break;
8985185a700Sflorian 	case 'f': /* fail */
8995185a700Sflorian 		FULLCHECK("fail");
9005185a700Sflorian 		lookup->servfail_stops = state;
9015185a700Sflorian 		break;
9025185a700Sflorian 	case 'i':
9035185a700Sflorian 		switch (cmd[1]) {
9045185a700Sflorian 		case 'd': /* identify */
9055185a700Sflorian 			switch (cmd[2]) {
9065185a700Sflorian 			case 'e':
9075185a700Sflorian 				FULLCHECK("identify");
9085185a700Sflorian 				lookup->identify = state;
9095185a700Sflorian 				break;
9105185a700Sflorian 			case 'n':
9115185a700Sflorian 				FULLCHECK("idnout");
9125185a700Sflorian 				fprintf(stderr, ";; IDN support not enabled\n");
9135185a700Sflorian 				break;
9145185a700Sflorian 			default:
9155185a700Sflorian 				goto invalid_option;
9165185a700Sflorian 			}
9175185a700Sflorian 			break;
9185185a700Sflorian 		case 'g': /* ignore */
9195185a700Sflorian 		default: /*
9205185a700Sflorian 			  * Inherits default for compatibility (+[no]i*).
9215185a700Sflorian 			  */
9225185a700Sflorian 			FULLCHECK("ignore");
9235185a700Sflorian 			lookup->ignore = state;
9245185a700Sflorian 		}
9255185a700Sflorian 		break;
9265185a700Sflorian 	case 'k':
9275185a700Sflorian 		FULLCHECK("keepopen");
9285185a700Sflorian 		keep_open = state;
9295185a700Sflorian 		break;
9305185a700Sflorian 	case 'm': /* multiline */
9315185a700Sflorian 		FULLCHECK("multiline");
9325185a700Sflorian 		multiline = state;
9335185a700Sflorian 		break;
9345185a700Sflorian 	case 'n':
9355185a700Sflorian 		switch (cmd[1]) {
9365185a700Sflorian 		case 'd': /* ndots */
9375185a700Sflorian 			FULLCHECK("ndots");
9385185a700Sflorian 			if (value == NULL)
9395185a700Sflorian 				goto need_value;
9405185a700Sflorian 			if (!state)
9415185a700Sflorian 				goto invalid_option;
942c49c5f05Sflorian 			num = strtonum(value, 0, MAXNDOTS, &errstr);
943c49c5f05Sflorian 			if (errstr != NULL)
944c49c5f05Sflorian 				fatal("ndots is %s: '%s'", errstr, value);
9455185a700Sflorian 			ndots = num;
9465185a700Sflorian 			break;
9475185a700Sflorian 		case 's':
9485185a700Sflorian 			switch (cmd[2]) {
9495185a700Sflorian 			case 'i': /* nsid */
9505185a700Sflorian 				FULLCHECK("nsid");
9515185a700Sflorian 				if (state && lookup->edns == -1)
9525185a700Sflorian 					lookup->edns = 0;
9535185a700Sflorian 				lookup->nsid = state;
9545185a700Sflorian 				break;
9555185a700Sflorian 			case 's': /* nssearch */
9565185a700Sflorian 				FULLCHECK("nssearch");
9575185a700Sflorian 				lookup->ns_search_only = state;
9585185a700Sflorian 				if (state) {
9591fb015a8Sflorian 					lookup->trace_root = 1;
9601fb015a8Sflorian 					lookup->recurse = 1;
9611fb015a8Sflorian 					lookup->identify = 1;
9621fb015a8Sflorian 					lookup->stats = 0;
9631fb015a8Sflorian 					lookup->comments = 0;
9641fb015a8Sflorian 					lookup->section_additional = 0;
9651fb015a8Sflorian 					lookup->section_authority = 0;
9661fb015a8Sflorian 					lookup->section_question = 0;
9675185a700Sflorian 					lookup->rdtype = dns_rdatatype_ns;
9681fb015a8Sflorian 					lookup->rdtypeset = 1;
9691fb015a8Sflorian 					short_form = 1;
9705185a700Sflorian 					rrcomments = 0;
9715185a700Sflorian 				}
9725185a700Sflorian 				break;
9735185a700Sflorian 			default:
9745185a700Sflorian 				goto invalid_option;
9755185a700Sflorian 			}
9765185a700Sflorian 			break;
9775185a700Sflorian 		default:
9785185a700Sflorian 			goto invalid_option;
9795185a700Sflorian 		}
9805185a700Sflorian 		break;
9815185a700Sflorian 	case 'o':
9825185a700Sflorian 		switch (cmd[1]) {
9835185a700Sflorian 		case 'n':
9845185a700Sflorian 			FULLCHECK("onesoa");
9855185a700Sflorian 			onesoa = state;
9865185a700Sflorian 			break;
9875185a700Sflorian 		case 'p':
9885185a700Sflorian 			FULLCHECK("opcode");
9895185a700Sflorian 			if (!state) {
9905185a700Sflorian 				lookup->opcode = 0;	/* default - query */
9915185a700Sflorian 				break;
9925185a700Sflorian 			}
9935185a700Sflorian 			if (value == NULL)
9945185a700Sflorian 				goto need_value;
9955185a700Sflorian 			for (num = 0;
9965185a700Sflorian 			     num < sizeof(opcodetext)/sizeof(opcodetext[0]);
9975185a700Sflorian 			     num++) {
9985185a700Sflorian 				if (strcasecmp(opcodetext[num], value) == 0)
9995185a700Sflorian 					break;
10005185a700Sflorian 			}
10015185a700Sflorian 			if (num < 16) {
10025185a700Sflorian 				lookup->opcode = (dns_opcode_t)num;
10035185a700Sflorian 				break;
10045185a700Sflorian 			}
1005c49c5f05Sflorian 			num = strtonum(value, 0, 15, &errstr);
1006c49c5f05Sflorian 			if (errstr != NULL)
1007c49c5f05Sflorian 				fatal("opcode is %s: '%s'", errstr, value);
10085185a700Sflorian 			lookup->opcode = (dns_opcode_t)num;
10095185a700Sflorian 			break;
10105185a700Sflorian 		default:
10115185a700Sflorian 			goto invalid_option;
10125185a700Sflorian 		}
10135185a700Sflorian 		break;
10145185a700Sflorian 	case 'q':
10155185a700Sflorian 		switch (cmd[1]) {
10165185a700Sflorian 		case 'r': /* qr */
10175185a700Sflorian 			FULLCHECK("qr");
10185185a700Sflorian 			qr = state;
10195185a700Sflorian 			break;
10205185a700Sflorian 		case 'u': /* question */
10215185a700Sflorian 			FULLCHECK("question");
10225185a700Sflorian 			lookup->section_question = state;
10235185a700Sflorian 			if (lookup == default_lookup)
10245185a700Sflorian 				plusquest = state;
10255185a700Sflorian 			break;
10265185a700Sflorian 		default:
10275185a700Sflorian 			goto invalid_option;
10285185a700Sflorian 		}
10295185a700Sflorian 		break;
10305185a700Sflorian 	case 'r':
10315185a700Sflorian 		switch (cmd[1]) {
10325185a700Sflorian 		case 'd': /* rdflag */
10335185a700Sflorian 			FULLCHECK("rdflag");
10345185a700Sflorian 			lookup->recurse = state;
10355185a700Sflorian 			break;
10365185a700Sflorian 		case 'e':
10375185a700Sflorian 			switch (cmd[2]) {
10385185a700Sflorian 			case 'c': /* recurse */
10395185a700Sflorian 				FULLCHECK("recurse");
10405185a700Sflorian 				lookup->recurse = state;
10415185a700Sflorian 				break;
10425185a700Sflorian 			case 't': /* retry / retries */
10435185a700Sflorian 				FULLCHECK2("retry", "retries");
10445185a700Sflorian 				if (value == NULL)
10455185a700Sflorian 					goto need_value;
10465185a700Sflorian 				if (!state)
10475185a700Sflorian 					goto invalid_option;
1048c49c5f05Sflorian 				lookup->retries = strtonum(value, 0,
1049c49c5f05Sflorian 				    MAXTRIES - 1, &errstr);
1050c49c5f05Sflorian 				if (errstr != NULL)
1051c49c5f05Sflorian 					fatal("retries is %s: '%s'", errstr,
1052c49c5f05Sflorian 					    value);
10535185a700Sflorian 				lookup->retries++;
10545185a700Sflorian 				break;
10555185a700Sflorian 			default:
10565185a700Sflorian 				goto invalid_option;
10575185a700Sflorian 			}
10585185a700Sflorian 			break;
10595185a700Sflorian 		case 'r': /* rrcomments */
10605185a700Sflorian 			FULLCHECK("rrcomments");
10615185a700Sflorian 			rrcomments = state ? 1 : -1;
10625185a700Sflorian 			break;
10635185a700Sflorian 		default:
10645185a700Sflorian 			goto invalid_option;
10655185a700Sflorian 		}
10665185a700Sflorian 		break;
10675185a700Sflorian 	case 's':
10685185a700Sflorian 		switch (cmd[1]) {
10695185a700Sflorian 		case 'e': /* search */
10705185a700Sflorian 			FULLCHECK("search");
10715185a700Sflorian 			if (!lookup->trace) {
10725185a700Sflorian 				usesearch = state;
10735185a700Sflorian 			}
10745185a700Sflorian 			break;
10755185a700Sflorian 		case 'h':
10765185a700Sflorian 			if (cmd[2] != 'o')
10775185a700Sflorian 				goto invalid_option;
10785185a700Sflorian 			switch (cmd[3]) {
10795185a700Sflorian 			case 'r': /* short */
10805185a700Sflorian 				FULLCHECK("short");
10815185a700Sflorian 				short_form = state;
10825185a700Sflorian 				if (state) {
10831fb015a8Sflorian 					printcmd = 0;
10841fb015a8Sflorian 					lookup->section_additional = 0;
10851fb015a8Sflorian 					lookup->section_answer = 1;
10861fb015a8Sflorian 					lookup->section_authority = 0;
10871fb015a8Sflorian 					lookup->section_question = 0;
10881fb015a8Sflorian 					lookup->comments = 0;
10891fb015a8Sflorian 					lookup->stats = 0;
10905185a700Sflorian 					rrcomments = -1;
10915185a700Sflorian 				}
10925185a700Sflorian 				break;
10935185a700Sflorian 			case 'w': /* showsearch */
10945185a700Sflorian 				FULLCHECK("showsearch");
10955185a700Sflorian 				if (!lookup->trace) {
10965185a700Sflorian 					showsearch = state;
10975185a700Sflorian 					usesearch = state;
10985185a700Sflorian 				}
10995185a700Sflorian 				break;
11005185a700Sflorian 			default:
11015185a700Sflorian 				goto invalid_option;
11025185a700Sflorian 			}
11035185a700Sflorian 			break;
11045185a700Sflorian 		case 'i':
11055185a700Sflorian 			switch (cmd[2]) {
11065185a700Sflorian 			case 't': /* sit */
11075185a700Sflorian 				FULLCHECK("sit");
11085185a700Sflorian  sit:
11095185a700Sflorian 				if (state && lookup->edns == -1)
11105185a700Sflorian 					lookup->edns = 0;
11115185a700Sflorian 				lookup->sit = state;
11125185a700Sflorian 				if (value != NULL) {
11135185a700Sflorian 					n = strlcpy(sitvalue, value,
11145185a700Sflorian 						    sizeof(sitvalue));
11155185a700Sflorian 					if (n >= sizeof(sitvalue))
11165185a700Sflorian 						fatal("SIT data too large");
11175185a700Sflorian 					lookup->sitvalue = sitvalue;
11185185a700Sflorian 				} else
11195185a700Sflorian 					lookup->sitvalue = NULL;
11205185a700Sflorian 				break;
11215185a700Sflorian 			default:
11225185a700Sflorian 				goto invalid_option;
11235185a700Sflorian 			}
11245185a700Sflorian 			break;
11255185a700Sflorian 		case 'p': /* split */
11265185a700Sflorian 			FULLCHECK("split");
11275185a700Sflorian 			if (value != NULL && !state)
11285185a700Sflorian 				goto invalid_option;
11295185a700Sflorian 			if (!state) {
11305185a700Sflorian 				splitwidth = 0;
11315185a700Sflorian 				break;
11325185a700Sflorian 			} else if (value == NULL)
11335185a700Sflorian 				break;
11345185a700Sflorian 
1135c49c5f05Sflorian 			splitwidth = strtonum(value, 0, 1023, &errstr);
1136c49c5f05Sflorian 			if (errstr != NULL)
1137c49c5f05Sflorian 				fatal("split is %s: '%s'", errstr, value);
11385185a700Sflorian 			if ((splitwidth % 4) != 0U) {
11395185a700Sflorian 				splitwidth = ((splitwidth + 3) / 4) * 4;
11405185a700Sflorian 				fprintf(stderr, ";; Warning, split must be "
11415185a700Sflorian 						"a multiple of 4; adjusting "
11425185a700Sflorian 						"to %u\n", splitwidth);
11435185a700Sflorian 			}
11445185a700Sflorian 			/*
11455185a700Sflorian 			 * There is an adjustment done in the
11465185a700Sflorian 			 * totext_<rrtype>() functions which causes
11475185a700Sflorian 			 * splitwidth to shrink.  This is okay when we're
11485185a700Sflorian 			 * using the default width but incorrect in this
11495185a700Sflorian 			 * case, so we correct for it
11505185a700Sflorian 			 */
11515185a700Sflorian 			if (splitwidth)
11525185a700Sflorian 				splitwidth += 3;
11535185a700Sflorian 			break;
11545185a700Sflorian 		case 't': /* stats */
11555185a700Sflorian 			FULLCHECK("stats");
11565185a700Sflorian 			lookup->stats = state;
11575185a700Sflorian 			break;
11585185a700Sflorian 		case 'u': /* subnet */
11595185a700Sflorian 			FULLCHECK("subnet");
11605185a700Sflorian 			if (state && value == NULL)
11615185a700Sflorian 				goto need_value;
11625185a700Sflorian 			if (!state) {
11635185a700Sflorian 				if (lookup->ecs_addr != NULL) {
11645185a700Sflorian 					free(lookup->ecs_addr);
11655185a700Sflorian 					lookup->ecs_addr = NULL;
11665185a700Sflorian 				}
11675185a700Sflorian 				break;
11685185a700Sflorian 			}
11695185a700Sflorian 			if (lookup->edns == -1)
11705185a700Sflorian 				lookup->edns = 0;
11715185a700Sflorian 			if (lookup->ecs_addr != NULL) {
11725185a700Sflorian 				free(lookup->ecs_addr);
11735185a700Sflorian 				lookup->ecs_addr = NULL;
11745185a700Sflorian 			}
11758c9fac11Sflorian 			result = parse_netprefix(&lookup->ecs_addr,
11768c9fac11Sflorian 			    &lookup->ecs_plen, value);
11775185a700Sflorian 			if (result != ISC_R_SUCCESS)
11785185a700Sflorian 				fatal("Couldn't parse client");
11795185a700Sflorian 			break;
11805185a700Sflorian 		default:
11815185a700Sflorian 			goto invalid_option;
11825185a700Sflorian 		}
11835185a700Sflorian 		break;
11845185a700Sflorian 	case 't':
11855185a700Sflorian 		switch (cmd[1]) {
11865185a700Sflorian 		case 'c': /* tcp */
11875185a700Sflorian 			FULLCHECK("tcp");
11885185a700Sflorian 			if (!is_batchfile) {
11895185a700Sflorian 				lookup->tcp_mode = state;
11901fb015a8Sflorian 				lookup->tcp_mode_set = 1;
11915185a700Sflorian 			}
11925185a700Sflorian 			break;
11935185a700Sflorian 		case 'i': /* timeout */
11945185a700Sflorian 			FULLCHECK("timeout");
11955185a700Sflorian 			if (value == NULL)
11965185a700Sflorian 				goto need_value;
11975185a700Sflorian 			if (!state)
11985185a700Sflorian 				goto invalid_option;
1199c49c5f05Sflorian 			timeout = strtonum(value, 0, MAXTIMEOUT, &errstr);
1200c49c5f05Sflorian 			if (errstr != NULL)
1201c49c5f05Sflorian 				fatal("timeout is %s: '%s'", errstr, value);
12025185a700Sflorian 			if (timeout == 0)
12035185a700Sflorian 				timeout = 1;
12045185a700Sflorian 			break;
12055185a700Sflorian 		case 'r':
12065185a700Sflorian 			switch (cmd[2]) {
12075185a700Sflorian 			case 'a': /* trace */
12085185a700Sflorian 				FULLCHECK("trace");
12095185a700Sflorian 				lookup->trace = state;
12105185a700Sflorian 				lookup->trace_root = state;
12115185a700Sflorian 				if (state) {
12121fb015a8Sflorian 					lookup->recurse = 0;
12131fb015a8Sflorian 					lookup->identify = 1;
12141fb015a8Sflorian 					lookup->comments = 0;
12155185a700Sflorian 					rrcomments = 0;
12161fb015a8Sflorian 					lookup->stats = 0;
12171fb015a8Sflorian 					lookup->section_additional = 0;
12181fb015a8Sflorian 					lookup->section_authority = 1;
12191fb015a8Sflorian 					lookup->section_question = 0;
12201fb015a8Sflorian 					lookup->dnssec = 1;
12211fb015a8Sflorian 					usesearch = 0;
12225185a700Sflorian 				}
12235185a700Sflorian 				break;
12245185a700Sflorian 			case 'i': /* tries */
12255185a700Sflorian 				FULLCHECK("tries");
12265185a700Sflorian 				if (value == NULL)
12275185a700Sflorian 					goto need_value;
12285185a700Sflorian 				if (!state)
12295185a700Sflorian 					goto invalid_option;
1230c49c5f05Sflorian 				lookup->retries = strtonum(value, 0, MAXTRIES,
1231c49c5f05Sflorian 				    &errstr);
1232c49c5f05Sflorian 				if (errstr != NULL)
1233c49c5f05Sflorian 					fatal("tries is %s: '%s'", errstr,
1234c49c5f05Sflorian 					    value);
12355185a700Sflorian 				if (lookup->retries == 0)
12365185a700Sflorian 					lookup->retries = 1;
12375185a700Sflorian 				break;
12385185a700Sflorian 			default:
12395185a700Sflorian 				goto invalid_option;
12405185a700Sflorian 			}
12415185a700Sflorian 			break;
12425185a700Sflorian 		case 't': /* ttlid */
12435185a700Sflorian 			FULLCHECK("ttlid");
12441fb015a8Sflorian 			nottl = !state;
12455185a700Sflorian 			break;
12465185a700Sflorian 		default:
12475185a700Sflorian 			goto invalid_option;
12485185a700Sflorian 		}
12495185a700Sflorian 		break;
12505185a700Sflorian 	case 'v':
12515185a700Sflorian 		FULLCHECK("vc");
12525185a700Sflorian 		if (!is_batchfile) {
12535185a700Sflorian 			lookup->tcp_mode = state;
12541fb015a8Sflorian 			lookup->tcp_mode_set = 1;
12555185a700Sflorian 		}
12565185a700Sflorian 		break;
1257*9835a5e1Sflorian 	case 'z':
1258*9835a5e1Sflorian 		FULLCHECK("zoneversion");
1259*9835a5e1Sflorian 		if (!state)
1260*9835a5e1Sflorian 			break;
1261*9835a5e1Sflorian 		save_opt(lookup, "zoneversion", NULL);
1262*9835a5e1Sflorian 		break;
12635185a700Sflorian 	default:
12645185a700Sflorian 	invalid_option:
12655185a700Sflorian 	need_value:
12665185a700Sflorian 		fprintf(stderr, "Invalid option: +%s\n",
12675185a700Sflorian 			option);
12685185a700Sflorian 		usage();
12695185a700Sflorian 	}
12705185a700Sflorian 	return;
12715185a700Sflorian }
12725185a700Sflorian 
12735185a700Sflorian /*%
12741fb015a8Sflorian  * #1 returned if value was used
12755185a700Sflorian  */
12765185a700Sflorian static const char *single_dash_opts = "46dhinuv";
12775185a700Sflorian static const char *dash_opts = "46bcdfhikmnptvyx";
12781fb015a8Sflorian static int
12795185a700Sflorian dash_option(char *option, char *next, dig_lookup_t **lookup,
12801fb015a8Sflorian 	    int *open_type_class, int *need_clone,
12811fb015a8Sflorian 	    int config_only, int argc, char **argv,
12821fb015a8Sflorian 	    int *firstarg)
12835185a700Sflorian {
12845185a700Sflorian 	char opt, *value, *ptr, *ptr2, *ptr3;
12855185a700Sflorian 	isc_result_t result;
12861fb015a8Sflorian 	int value_from_next;
12875185a700Sflorian 	isc_textregion_t tr;
12885185a700Sflorian 	dns_rdatatype_t rdtype;
12895185a700Sflorian 	dns_rdataclass_t rdclass;
12905185a700Sflorian 	char textname[MXNAME];
129133e1be9eSflorian 	char *cmd;
12925185a700Sflorian 	uint32_t num;
1293c49c5f05Sflorian 	const char *errstr;
12945185a700Sflorian 
12955185a700Sflorian 	while (strpbrk(option, single_dash_opts) == &option[0]) {
12965185a700Sflorian 		/*
12975185a700Sflorian 		 * Since the -[46dhinuv] options do not take an argument,
12985185a700Sflorian 		 * account for them (in any number and/or combination)
12995185a700Sflorian 		 * if they appear as the first character(s) of a q-opt.
13005185a700Sflorian 		 */
13015185a700Sflorian 		opt = option[0];
13025185a700Sflorian 		switch (opt) {
13035185a700Sflorian 		case '4':
13041bf56eb0Sflorian 			if (have_ipv4)
13051fb015a8Sflorian 				have_ipv6 = 0;
13061bf56eb0Sflorian 			else
13075185a700Sflorian 				fatal("can't find IPv4 networking");
13085185a700Sflorian 			break;
13095185a700Sflorian 		case '6':
13101bf56eb0Sflorian 			if (have_ipv6)
13111fb015a8Sflorian 				have_ipv4 = 0;
13121bf56eb0Sflorian 			else
13135185a700Sflorian 				fatal("can't find IPv6 networking");
13145185a700Sflorian 			break;
13155185a700Sflorian 		case 'd':
13165185a700Sflorian 			ptr = strpbrk(&option[1], dash_opts);
13175185a700Sflorian 			if (ptr != &option[1]) {
13185185a700Sflorian 				cmd = option;
13195185a700Sflorian 				FULLCHECK("debug");
13201fb015a8Sflorian 				debugging = 1;
13211fb015a8Sflorian 				return (0);
13225185a700Sflorian 			} else
13231fb015a8Sflorian 				debugging = 1;
13245185a700Sflorian 			break;
13255185a700Sflorian 		case 'h':
13265185a700Sflorian 			help();
13275185a700Sflorian 			exit(0);
13285185a700Sflorian 			break;
13295185a700Sflorian 		case 'i':
13301fb015a8Sflorian 			ip6_int = 1;
13315185a700Sflorian 			break;
13325185a700Sflorian 		case 'n':
13335185a700Sflorian 			/* deprecated */
13345185a700Sflorian 			break;
13355185a700Sflorian 		case 'u':
13361fb015a8Sflorian 			use_usec = 1;
13375185a700Sflorian 			break;
13385185a700Sflorian 		case 'v':
13395185a700Sflorian 			version();
13405185a700Sflorian 			exit(0);
13415185a700Sflorian 			break;
13425185a700Sflorian 		}
13435185a700Sflorian 		if (strlen(option) > 1U)
13445185a700Sflorian 			option = &option[1];
13455185a700Sflorian 		else
13461fb015a8Sflorian 			return (0);
13475185a700Sflorian 	}
13485185a700Sflorian 	opt = option[0];
13495185a700Sflorian 	if (strlen(option) > 1U) {
13501fb015a8Sflorian 		value_from_next = 0;
13515185a700Sflorian 		value = &option[1];
13525185a700Sflorian 	} else {
13531fb015a8Sflorian 		value_from_next = 1;
13545185a700Sflorian 		value = next;
13555185a700Sflorian 	}
13565185a700Sflorian 	if (value == NULL)
13575185a700Sflorian 		goto invalid_option;
13585185a700Sflorian 	switch (opt) {
135933e1be9eSflorian 	case 'b': {
136033e1be9eSflorian 		struct addrinfo *ai = NULL, hints;
136133e1be9eSflorian 		int error;
136233e1be9eSflorian 		char *hash;
136333e1be9eSflorian 
136433e1be9eSflorian 		memset(&hints, 0, sizeof(hints));
136533e1be9eSflorian 		hints.ai_flags = AI_NUMERICHOST;
136633e1be9eSflorian 		hints.ai_socktype = SOCK_DGRAM;
136733e1be9eSflorian 
13685185a700Sflorian 		hash = strchr(value, '#');
13695185a700Sflorian 		if (hash != NULL) {
13705185a700Sflorian 			*hash = '\0';
137133e1be9eSflorian 			error = getaddrinfo(value, hash + 1, &hints, &ai);
13725185a700Sflorian 			*hash = '#';
137333e1be9eSflorian 		} else
137433e1be9eSflorian 			error = getaddrinfo(value, NULL, &hints, &ai);
137533e1be9eSflorian 
137633e1be9eSflorian 		if (error)
137733e1be9eSflorian 			fatal("invalid address %s: %s", value,
137833e1be9eSflorian 			    gai_strerror(error));
137933e1be9eSflorian 		if (ai == NULL || ai->ai_addrlen > sizeof(bind_address))
138033e1be9eSflorian 			fatal("invalid address %s", value);
138133e1be9eSflorian 		if (!have_ipv4 && ai->ai_family == AF_INET)
138233e1be9eSflorian 			fatal("%s: wrong address family", value);
138333e1be9eSflorian 		if (!have_ipv6 && ai->ai_family == AF_INET6)
138433e1be9eSflorian 			fatal("%s: wrong address family", value);
138533e1be9eSflorian 
138633e1be9eSflorian 		memset(&bind_address, 0, sizeof(bind_address));
138733e1be9eSflorian 		memcpy(&bind_address, ai->ai_addr, ai->ai_addrlen);
138833e1be9eSflorian 
13891fb015a8Sflorian 		specified_source = 1;
13905185a700Sflorian 		return (value_from_next);
139133e1be9eSflorian 	}
13925185a700Sflorian 	case 'c':
13935185a700Sflorian 		if ((*lookup)->rdclassset) {
13945185a700Sflorian 			fprintf(stderr, ";; Warning, extra class option\n");
13955185a700Sflorian 		}
13961fb015a8Sflorian 		*open_type_class = 0;
13975185a700Sflorian 		tr.base = value;
13985185a700Sflorian 		tr.length = (unsigned int) strlen(value);
13995185a700Sflorian 		result = dns_rdataclass_fromtext(&rdclass,
14005185a700Sflorian 						 (isc_textregion_t *)&tr);
14015185a700Sflorian 		if (result == ISC_R_SUCCESS) {
14025185a700Sflorian 			(*lookup)->rdclass = rdclass;
14031fb015a8Sflorian 			(*lookup)->rdclassset = 1;
14045185a700Sflorian 		} else
14055185a700Sflorian 			fprintf(stderr, ";; Warning, ignoring "
14065185a700Sflorian 				"invalid class %s\n",
14075185a700Sflorian 				value);
14085185a700Sflorian 		return (value_from_next);
14095185a700Sflorian 	case 'f':
14105185a700Sflorian 		batchname = value;
14115185a700Sflorian 		return (value_from_next);
14125185a700Sflorian 	case 'k':
14135185a700Sflorian 		strlcpy(keyfile, value, sizeof(keyfile));
14145185a700Sflorian 		return (value_from_next);
14155185a700Sflorian 	case 'p':
1416c49c5f05Sflorian 		num = strtonum(value, 0, MAXPORT, &errstr);
1417c49c5f05Sflorian 		if (errstr != NULL)
1418c49c5f05Sflorian 			fatal("port number is %s: '%s'", errstr, value);
14195185a700Sflorian 		port = num;
14205185a700Sflorian 		return (value_from_next);
14215185a700Sflorian 	case 'q':
14225185a700Sflorian 		if (!config_only) {
14235185a700Sflorian 			if (*need_clone)
14245185a700Sflorian 				(*lookup) = clone_lookup(default_lookup,
14251fb015a8Sflorian 							 1);
14261fb015a8Sflorian 			*need_clone = 1;
14275185a700Sflorian 			strlcpy((*lookup)->textname, value,
14285185a700Sflorian 				sizeof((*lookup)->textname));
14291fb015a8Sflorian 			(*lookup)->trace_root = (*lookup)->trace  ||
14301fb015a8Sflorian 						     (*lookup)->ns_search_only;
14311fb015a8Sflorian 			(*lookup)->new_search = 1;
14325185a700Sflorian 			if (*firstarg) {
14335185a700Sflorian 				printgreeting(argc, argv, *lookup);
14341fb015a8Sflorian 				*firstarg = 0;
14355185a700Sflorian 			}
14365185a700Sflorian 			ISC_LIST_APPEND(lookup_list, (*lookup), link);
14375185a700Sflorian 			debug("looking up %s", (*lookup)->textname);
14385185a700Sflorian 		}
14395185a700Sflorian 		return (value_from_next);
14405185a700Sflorian 	case 't':
14411fb015a8Sflorian 		*open_type_class = 0;
14425185a700Sflorian 		if (strncasecmp(value, "ixfr=", 5) == 0) {
14435185a700Sflorian 			rdtype = dns_rdatatype_ixfr;
14445185a700Sflorian 			result = ISC_R_SUCCESS;
14455185a700Sflorian 		} else {
14465185a700Sflorian 			tr.base = value;
14475185a700Sflorian 			tr.length = (unsigned int) strlen(value);
14485185a700Sflorian 			result = dns_rdatatype_fromtext(&rdtype,
14495185a700Sflorian 						(isc_textregion_t *)&tr);
14505185a700Sflorian 			if (result == ISC_R_SUCCESS &&
14515185a700Sflorian 			    rdtype == dns_rdatatype_ixfr) {
14525185a700Sflorian 				result = DNS_R_UNKNOWN;
14535185a700Sflorian 			}
14545185a700Sflorian 		}
14555185a700Sflorian 		if (result == ISC_R_SUCCESS) {
14565185a700Sflorian 			if ((*lookup)->rdtypeset) {
14575185a700Sflorian 				fprintf(stderr, ";; Warning, "
14585185a700Sflorian 						"extra type option\n");
14595185a700Sflorian 			}
14605185a700Sflorian 			if (rdtype == dns_rdatatype_ixfr) {
14615185a700Sflorian 				uint32_t serial;
14625185a700Sflorian 				(*lookup)->rdtype = dns_rdatatype_ixfr;
14631fb015a8Sflorian 				(*lookup)->rdtypeset = 1;
1464c49c5f05Sflorian 				serial = strtonum(&value[5], 0, MAXSERIAL,
1465c49c5f05Sflorian 				    &errstr);
1466c49c5f05Sflorian 				if (errstr != NULL)
1467c49c5f05Sflorian 					fatal("serial number is %s: '%s'",
1468c49c5f05Sflorian 					    errstr, &value[5]);
14695185a700Sflorian 				(*lookup)->ixfr_serial = serial;
14705185a700Sflorian 				(*lookup)->section_question = plusquest;
14715185a700Sflorian 				(*lookup)->comments = pluscomm;
14725185a700Sflorian 				if (!(*lookup)->tcp_mode_set)
14731fb015a8Sflorian 					(*lookup)->tcp_mode = 1;
14745185a700Sflorian 			} else {
14755185a700Sflorian 				(*lookup)->rdtype = rdtype;
14765185a700Sflorian 				if (!config_only)
14771fb015a8Sflorian 					(*lookup)->rdtypeset = 1;
14785185a700Sflorian 				if (rdtype == dns_rdatatype_axfr) {
14795185a700Sflorian 					(*lookup)->section_question = plusquest;
14805185a700Sflorian 					(*lookup)->comments = pluscomm;
14815185a700Sflorian 				}
14821fb015a8Sflorian 				(*lookup)->ixfr_serial = 0;
14835185a700Sflorian 			}
14845185a700Sflorian 		} else
14855185a700Sflorian 			fprintf(stderr, ";; Warning, ignoring "
14865185a700Sflorian 				 "invalid type %s\n",
14875185a700Sflorian 				 value);
14885185a700Sflorian 		return (value_from_next);
14895185a700Sflorian 	case 'y':
14905185a700Sflorian 		ptr = next_token(&value, ":");	/* hmac type or name */
14915185a700Sflorian 		if (ptr == NULL) {
14925185a700Sflorian 			usage();
14935185a700Sflorian 		}
14945185a700Sflorian 		ptr2 = next_token(&value, ":");	/* name or secret */
14955185a700Sflorian 		if (ptr2 == NULL)
14965185a700Sflorian 			usage();
14975185a700Sflorian 		ptr3 = next_token(&value, ":"); /* secret or NULL */
14985185a700Sflorian 		if (ptr3 != NULL) {
14995185a700Sflorian 			parse_hmac(ptr);
15005185a700Sflorian 			ptr = ptr2;
15015185a700Sflorian 			ptr2 = ptr3;
15025185a700Sflorian 		} else  {
15035185a700Sflorian 			hmacname = DNS_TSIG_HMACSHA256_NAME;
15045185a700Sflorian 			digestbits = 0;
15055185a700Sflorian 		}
15065185a700Sflorian 		strlcpy(keynametext, ptr, sizeof(keynametext));
15075185a700Sflorian 		strlcpy(keysecret, ptr2, sizeof(keysecret));
15085185a700Sflorian 		return (value_from_next);
15095185a700Sflorian 	case 'x':
15105185a700Sflorian 		if (*need_clone)
15111fb015a8Sflorian 			*lookup = clone_lookup(default_lookup, 1);
15121fb015a8Sflorian 		*need_clone = 1;
15135185a700Sflorian 		if (get_reverse(textname, sizeof(textname), value,
15141fb015a8Sflorian 				ip6_int, 0) == ISC_R_SUCCESS) {
15155185a700Sflorian 			strlcpy((*lookup)->textname, textname,
15165185a700Sflorian 				sizeof((*lookup)->textname));
15175185a700Sflorian 			debug("looking up %s", (*lookup)->textname);
15181fb015a8Sflorian 			(*lookup)->trace_root = (*lookup)->trace  ||
15191fb015a8Sflorian 						(*lookup)->ns_search_only;
15205185a700Sflorian 			(*lookup)->ip6_int = ip6_int;
15215185a700Sflorian 			if (!(*lookup)->rdtypeset)
15225185a700Sflorian 				(*lookup)->rdtype = dns_rdatatype_ptr;
15235185a700Sflorian 			if (!(*lookup)->rdclassset)
15245185a700Sflorian 				(*lookup)->rdclass = dns_rdataclass_in;
15251fb015a8Sflorian 			(*lookup)->new_search = 1;
15265185a700Sflorian 			if (*firstarg) {
15275185a700Sflorian 				printgreeting(argc, argv, *lookup);
15281fb015a8Sflorian 				*firstarg = 0;
15295185a700Sflorian 			}
15305185a700Sflorian 			ISC_LIST_APPEND(lookup_list, *lookup, link);
15315185a700Sflorian 		} else {
15325185a700Sflorian 			fprintf(stderr, "Invalid IP address %s\n", value);
15335185a700Sflorian 			exit(1);
15345185a700Sflorian 		}
15355185a700Sflorian 		return (value_from_next);
15365185a700Sflorian 	invalid_option:
15375185a700Sflorian 	default:
15385185a700Sflorian 		fprintf(stderr, "Invalid option: -%s\n", option);
15395185a700Sflorian 		usage();
15405185a700Sflorian 	}
15415185a700Sflorian 	/* NOTREACHED */
15421fb015a8Sflorian 	return (0);
15435185a700Sflorian }
15445185a700Sflorian 
15455185a700Sflorian /*%
15465185a700Sflorian  * Because we may be trying to do memory allocation recording, we're going
15475185a700Sflorian  * to need to parse the arguments for the -m *before* we start the main
15485185a700Sflorian  * argument parsing routine.
15495185a700Sflorian  *
15505185a700Sflorian  * I'd prefer not to have to do this, but I am not quite sure how else to
15515185a700Sflorian  * fix the problem.  Argument parsing in dig involves memory allocation
15525185a700Sflorian  * by its nature, so it can't be done in the main argument parser.
15535185a700Sflorian  */
15545185a700Sflorian static void
15555185a700Sflorian preparse_args(int argc, char **argv) {
15565185a700Sflorian 	int rc;
15575185a700Sflorian 	char **rv;
15585185a700Sflorian 	char *option;
15595185a700Sflorian 
15605185a700Sflorian 	rc = argc;
15615185a700Sflorian 	rv = argv;
15625185a700Sflorian 	for (rc--, rv++; rc > 0; rc--, rv++) {
15635185a700Sflorian 		if (rv[0][0] != '-')
15645185a700Sflorian 			continue;
15655185a700Sflorian 		option = &rv[0][1];
15665185a700Sflorian 		while (strpbrk(option, single_dash_opts) == &option[0]) {
15675185a700Sflorian 			switch (option[0]) {
15685185a700Sflorian 			case '4':
15695185a700Sflorian 				if (ipv6only)
15705185a700Sflorian 					fatal("only one of -4 and -6 allowed");
15711fb015a8Sflorian 				ipv4only = 1;
15725185a700Sflorian 				break;
15735185a700Sflorian 			case '6':
15745185a700Sflorian 				if (ipv4only)
15755185a700Sflorian 					fatal("only one of -4 and -6 allowed");
15761fb015a8Sflorian 				ipv6only = 1;
15775185a700Sflorian 				break;
15785185a700Sflorian 			}
15795185a700Sflorian 			option = &option[1];
15805185a700Sflorian 		}
15815185a700Sflorian 	}
15825185a700Sflorian }
15835185a700Sflorian 
15845185a700Sflorian static void
15851fb015a8Sflorian parse_args(int is_batchfile, int config_only,
15865185a700Sflorian 	   int argc, char **argv)
15875185a700Sflorian {
15885185a700Sflorian 	isc_result_t result;
15895185a700Sflorian 	isc_textregion_t tr;
15901fb015a8Sflorian 	int firstarg = 1;
15915185a700Sflorian 	dig_lookup_t *lookup = NULL;
15925185a700Sflorian 	dns_rdatatype_t rdtype;
15935185a700Sflorian 	dns_rdataclass_t rdclass;
15941fb015a8Sflorian 	int open_type_class = 1;
15955185a700Sflorian 	char batchline[MXNAME];
15965185a700Sflorian 	int bargc;
15975185a700Sflorian 	char *bargv[64];
15985185a700Sflorian 	int rc;
15995185a700Sflorian 	char **rv;
16005185a700Sflorian 	char *input;
16015185a700Sflorian 	int i;
16021fb015a8Sflorian 	int need_clone = 1;
1603c49c5f05Sflorian 	const char *errstr;
16045185a700Sflorian 
16055185a700Sflorian 	/*
16065185a700Sflorian 	 * The semantics for parsing the args is a bit complex; if
16075185a700Sflorian 	 * we don't have a host yet, make the arg apply globally,
16085185a700Sflorian 	 * otherwise make it apply to the latest host.  This is
16095185a700Sflorian 	 * a bit different than the previous versions, but should
16105185a700Sflorian 	 * form a consistent user interface.
16115185a700Sflorian 	 *
16125185a700Sflorian 	 * First, create a "default lookup" which won't actually be used
16135185a700Sflorian 	 * anywhere, except for cloning into new lookups
16145185a700Sflorian 	 */
16155185a700Sflorian 
16165185a700Sflorian 	debug("parse_args()");
16175185a700Sflorian 	if (!is_batchfile) {
16185185a700Sflorian 		debug("making new lookup");
16195185a700Sflorian 		default_lookup = make_empty_lookup();
16201fb015a8Sflorian 		default_lookup->adflag = 1;
16215185a700Sflorian 		default_lookup->edns = 0;
16225185a700Sflorian 	}
16235185a700Sflorian 
16245185a700Sflorian 	if (is_batchfile && !config_only) {
16255185a700Sflorian 		/* Processing '-f batchfile'. */
16261fb015a8Sflorian 		lookup = clone_lookup(default_lookup, 1);
16271fb015a8Sflorian 		need_clone = 0;
16285185a700Sflorian 	} else
16295185a700Sflorian 		lookup = default_lookup;
16305185a700Sflorian 
16315185a700Sflorian 	rc = argc;
16325185a700Sflorian 	rv = argv;
16335185a700Sflorian 	for (rc--, rv++; rc > 0; rc--, rv++) {
16345185a700Sflorian 		debug("main parsing %s", rv[0]);
16355185a700Sflorian 		if (strncmp(rv[0], "%", 1) == 0)
16365185a700Sflorian 			break;
16375185a700Sflorian 		if (rv[0][0] == '@') {
16385185a700Sflorian 
16395185a700Sflorian 			if (is_batchfile && !config_only) {
16405185a700Sflorian 				addresscount = getaddresses(lookup, &rv[0][1],
16415185a700Sflorian 							     &result);
16425185a700Sflorian 				if (result != ISC_R_SUCCESS) {
16435185a700Sflorian 					fprintf(stderr, "couldn't get address "
16445185a700Sflorian 						"for '%s': %s: skipping "
16455185a700Sflorian 						"lookup\n", &rv[0][1],
16465185a700Sflorian 						isc_result_totext(result));
16475185a700Sflorian 					if (ISC_LINK_LINKED(lookup, link))
16485185a700Sflorian 						ISC_LIST_DEQUEUE(lookup_list,
16495185a700Sflorian 								 lookup, link);
16505185a700Sflorian 					destroy_lookup(lookup);
16515185a700Sflorian 					return;
16525185a700Sflorian 				}
16535185a700Sflorian 			} else
16545185a700Sflorian 				addresscount = getaddresses(lookup, &rv[0][1],
16555185a700Sflorian 							    NULL);
16565185a700Sflorian 		} else if (rv[0][0] == '+') {
16575185a700Sflorian 			plus_option(&rv[0][1], is_batchfile,
16585185a700Sflorian 				    lookup);
16595185a700Sflorian 		} else if (rv[0][0] == '-') {
16605185a700Sflorian 			if (rc <= 1) {
16615185a700Sflorian 				if (dash_option(&rv[0][1], NULL,
16625185a700Sflorian 						&lookup, &open_type_class,
16635185a700Sflorian 						&need_clone, config_only,
16645185a700Sflorian 						argc, argv, &firstarg)) {
16655185a700Sflorian 					rc--;
16665185a700Sflorian 					rv++;
16675185a700Sflorian 				}
16685185a700Sflorian 			} else {
16695185a700Sflorian 				if (dash_option(&rv[0][1], rv[1],
16705185a700Sflorian 						&lookup, &open_type_class,
16715185a700Sflorian 						&need_clone, config_only,
16725185a700Sflorian 						argc, argv, &firstarg)) {
16735185a700Sflorian 					rc--;
16745185a700Sflorian 					rv++;
16755185a700Sflorian 				}
16765185a700Sflorian 			}
16775185a700Sflorian 		} else {
16785185a700Sflorian 			/*
16795185a700Sflorian 			 * Anything which isn't an option
16805185a700Sflorian 			 */
16815185a700Sflorian 			if (open_type_class) {
16825185a700Sflorian 				if (strncasecmp(rv[0], "ixfr=", 5) == 0) {
16835185a700Sflorian 					rdtype = dns_rdatatype_ixfr;
16845185a700Sflorian 					result = ISC_R_SUCCESS;
16855185a700Sflorian 				} else {
16865185a700Sflorian 					tr.base = rv[0];
16875185a700Sflorian 					tr.length =
16885185a700Sflorian 						(unsigned int) strlen(rv[0]);
16895185a700Sflorian 					result = dns_rdatatype_fromtext(&rdtype,
16905185a700Sflorian 						(isc_textregion_t *)&tr);
16915185a700Sflorian 					if (result == ISC_R_SUCCESS &&
16925185a700Sflorian 					    rdtype == dns_rdatatype_ixfr) {
16935185a700Sflorian 						fprintf(stderr, ";; Warning, "
16945185a700Sflorian 							"ixfr requires a "
16955185a700Sflorian 							"serial number\n");
16965185a700Sflorian 						continue;
16975185a700Sflorian 					}
16985185a700Sflorian 				}
16995185a700Sflorian 				if (result == ISC_R_SUCCESS) {
17005185a700Sflorian 					if (lookup->rdtypeset) {
17015185a700Sflorian 						fprintf(stderr, ";; Warning, "
17025185a700Sflorian 							"extra type option\n");
17035185a700Sflorian 					}
17045185a700Sflorian 					if (rdtype == dns_rdatatype_ixfr) {
17055185a700Sflorian 						uint32_t serial;
17065185a700Sflorian 						lookup->rdtype =
17075185a700Sflorian 							dns_rdatatype_ixfr;
17081fb015a8Sflorian 						lookup->rdtypeset = 1;
1709c49c5f05Sflorian 						serial = strtonum(&rv[0][5], 0,
1710c49c5f05Sflorian 						    MAXSERIAL, &errstr);
1711c49c5f05Sflorian 						if (errstr != NULL)
1712c49c5f05Sflorian 							fatal("serial number "
1713c49c5f05Sflorian 							    "is %s: '%s'",
1714c49c5f05Sflorian 							    errstr, &rv[0][5]);
17155185a700Sflorian 						lookup->ixfr_serial = serial;
17165185a700Sflorian 						lookup->section_question =
17175185a700Sflorian 							plusquest;
17185185a700Sflorian 						lookup->comments = pluscomm;
17195185a700Sflorian 						if (!lookup->tcp_mode_set)
17201fb015a8Sflorian 							lookup->tcp_mode = 1;
17215185a700Sflorian 					} else {
17225185a700Sflorian 						lookup->rdtype = rdtype;
17231fb015a8Sflorian 						lookup->rdtypeset = 1;
17245185a700Sflorian 						if (rdtype ==
17255185a700Sflorian 						    dns_rdatatype_axfr) {
17265185a700Sflorian 						    lookup->section_question =
17275185a700Sflorian 								plusquest;
17285185a700Sflorian 						    lookup->comments = pluscomm;
17295185a700Sflorian 						}
17301fb015a8Sflorian 						lookup->ixfr_serial = 0;
17315185a700Sflorian 					}
17325185a700Sflorian 					continue;
17335185a700Sflorian 				}
17345185a700Sflorian 				result = dns_rdataclass_fromtext(&rdclass,
17355185a700Sflorian 						     (isc_textregion_t *)&tr);
17365185a700Sflorian 				if (result == ISC_R_SUCCESS) {
17375185a700Sflorian 					if (lookup->rdclassset) {
17385185a700Sflorian 						fprintf(stderr, ";; Warning, "
17395185a700Sflorian 							"extra class option\n");
17405185a700Sflorian 					}
17415185a700Sflorian 					lookup->rdclass = rdclass;
17421fb015a8Sflorian 					lookup->rdclassset = 1;
17435185a700Sflorian 					continue;
17445185a700Sflorian 				}
17455185a700Sflorian 			}
17465185a700Sflorian 
17475185a700Sflorian 			if (!config_only) {
17485185a700Sflorian 				if (need_clone)
17495185a700Sflorian 					lookup = clone_lookup(default_lookup,
17501fb015a8Sflorian 								      1);
17511fb015a8Sflorian 				need_clone = 1;
17525185a700Sflorian 				strlcpy(lookup->textname, rv[0],
17535185a700Sflorian 					sizeof(lookup->textname));
17541fb015a8Sflorian 				lookup->trace_root = lookup->trace  ||
17551fb015a8Sflorian 						     lookup->ns_search_only;
17561fb015a8Sflorian 				lookup->new_search = 1;
17575185a700Sflorian 				if (firstarg) {
17585185a700Sflorian 					printgreeting(argc, argv, lookup);
17591fb015a8Sflorian 					firstarg = 0;
17605185a700Sflorian 				}
17615185a700Sflorian 				ISC_LIST_APPEND(lookup_list, lookup, link);
17625185a700Sflorian 				debug("looking up %s", lookup->textname);
17635185a700Sflorian 			}
17645185a700Sflorian 			/* XXX Error message */
17655185a700Sflorian 		}
17665185a700Sflorian 	}
17675185a700Sflorian 
17685185a700Sflorian 	/*
17695185a700Sflorian 	 * If we have a batchfile, seed the lookup list with the
17705185a700Sflorian 	 * first entry, then trust the callback in dighost_shutdown
17715185a700Sflorian 	 * to get the rest
17725185a700Sflorian 	 */
17735185a700Sflorian 	if ((batchname != NULL) && !(is_batchfile)) {
17745185a700Sflorian 		if (strcmp(batchname, "-") == 0)
17755185a700Sflorian 			batchfp = stdin;
17765185a700Sflorian 		else
17775185a700Sflorian 			batchfp = fopen(batchname, "r");
17785185a700Sflorian 		if (batchfp == NULL) {
17795185a700Sflorian 			perror(batchname);
17805185a700Sflorian 			if (exitcode < 8)
17815185a700Sflorian 				exitcode = 8;
17825185a700Sflorian 			fatal("couldn't open specified batch file");
17835185a700Sflorian 		}
17845185a700Sflorian 		/* XXX Remove code dup from shutdown code */
17855185a700Sflorian 	next_line:
1786267a2de3Sjsg 		if (fgets(batchline, sizeof(batchline), batchfp) != NULL) {
17875185a700Sflorian 			bargc = 1;
17885185a700Sflorian 			debug("batch line %s", batchline);
17895185a700Sflorian 			if (batchline[0] == '\r' || batchline[0] == '\n'
17905185a700Sflorian 			    || batchline[0] == '#' || batchline[0] == ';')
17915185a700Sflorian 				goto next_line;
17925185a700Sflorian 			input = batchline;
17935185a700Sflorian 			bargv[bargc] = next_token(&input, " \t\r\n");
17945185a700Sflorian 			while ((bargc < 14) && (bargv[bargc] != NULL)) {
17955185a700Sflorian 				bargc++;
17965185a700Sflorian 				bargv[bargc] = next_token(&input, " \t\r\n");
17975185a700Sflorian 			}
17985185a700Sflorian 
17995185a700Sflorian 			bargv[0] = argv[0];
18005185a700Sflorian 			argv0 = argv[0];
18015185a700Sflorian 
18025185a700Sflorian 			for(i = 0; i < bargc; i++)
18035185a700Sflorian 				debug("batch argv %d: %s", i, bargv[i]);
18041fb015a8Sflorian 			parse_args(1, 0, bargc, (char **)bargv);
18055185a700Sflorian 			return;
18065185a700Sflorian 		}
18075185a700Sflorian 		return;
18085185a700Sflorian 	}
18095185a700Sflorian 	/*
18105185a700Sflorian 	 * If no lookup specified, search for root
18115185a700Sflorian 	 */
18125185a700Sflorian 	if ((lookup_list.head == NULL) && !config_only) {
18135185a700Sflorian 		if (need_clone)
18141fb015a8Sflorian 			lookup = clone_lookup(default_lookup, 1);
18151fb015a8Sflorian 		need_clone = 1;
18161fb015a8Sflorian 		lookup->trace_root = lookup->trace || lookup->ns_search_only;
18171fb015a8Sflorian 		lookup->new_search = 1;
18185185a700Sflorian 		strlcpy(lookup->textname, ".", sizeof(lookup->textname));
18195185a700Sflorian 		lookup->rdtype = dns_rdatatype_ns;
18201fb015a8Sflorian 		lookup->rdtypeset = 1;
18215185a700Sflorian 		if (firstarg) {
18225185a700Sflorian 			printgreeting(argc, argv, lookup);
18231fb015a8Sflorian 			firstarg = 0;
18245185a700Sflorian 		}
18255185a700Sflorian 		ISC_LIST_APPEND(lookup_list, lookup, link);
18265185a700Sflorian 	}
18275185a700Sflorian 	if (!need_clone)
18285185a700Sflorian 		destroy_lookup(lookup);
18295185a700Sflorian }
18305185a700Sflorian 
18315185a700Sflorian /*
18325185a700Sflorian  * Callback from dighost.c to allow program-specific shutdown code.
18335185a700Sflorian  * Here, we're possibly reading from a batch file, then shutting down
18345185a700Sflorian  * for real if there's nothing in the batch file to read.
18355185a700Sflorian  */
18365185a700Sflorian static void
18375185a700Sflorian query_finished(void) {
18385185a700Sflorian 	char batchline[MXNAME];
18395185a700Sflorian 	int bargc;
18405185a700Sflorian 	char *bargv[16];
18415185a700Sflorian 	char *input;
18425185a700Sflorian 	int i;
18435185a700Sflorian 
18445185a700Sflorian 	if (batchname == NULL) {
18455185a700Sflorian 		isc_app_shutdown();
18465185a700Sflorian 		return;
18475185a700Sflorian 	}
18485185a700Sflorian 
18495185a700Sflorian 	fflush(stdout);
18505185a700Sflorian 	if (feof(batchfp)) {
18515185a700Sflorian 		batchname = NULL;
18525185a700Sflorian 		isc_app_shutdown();
18535185a700Sflorian 		if (batchfp != stdin)
18545185a700Sflorian 			fclose(batchfp);
18555185a700Sflorian 		return;
18565185a700Sflorian 	}
18575185a700Sflorian 
1858267a2de3Sjsg 	if (fgets(batchline, sizeof(batchline), batchfp) != NULL) {
18595185a700Sflorian 		debug("batch line %s", batchline);
18605185a700Sflorian 		bargc = 1;
18615185a700Sflorian 		input = batchline;
18625185a700Sflorian 		bargv[bargc] = next_token(&input, " \t\r\n");
18635185a700Sflorian 		while ((bargc < 14) && (bargv[bargc] != NULL)) {
18645185a700Sflorian 			bargc++;
18655185a700Sflorian 			bargv[bargc] = next_token(&input, " \t\r\n");
18665185a700Sflorian 		}
18675185a700Sflorian 
18685185a700Sflorian 		bargv[0] = argv0;
18695185a700Sflorian 
18705185a700Sflorian 		for(i = 0; i < bargc; i++)
18715185a700Sflorian 			debug("batch argv %d: %s", i, bargv[i]);
18721fb015a8Sflorian 		parse_args(1, 0, bargc, (char **)bargv);
18735185a700Sflorian 		start_lookup();
18745185a700Sflorian 	} else {
18755185a700Sflorian 		batchname = NULL;
18765185a700Sflorian 		if (batchfp != stdin)
18775185a700Sflorian 			fclose(batchfp);
18785185a700Sflorian 		isc_app_shutdown();
18795185a700Sflorian 		return;
18805185a700Sflorian 	}
18815185a700Sflorian }
18825185a700Sflorian 
18835185a700Sflorian void dig_setup(int argc, char **argv)
18845185a700Sflorian {
18855185a700Sflorian 	isc_result_t result;
18865185a700Sflorian 
18875185a700Sflorian 	ISC_LIST_INIT(lookup_list);
18885185a700Sflorian 	ISC_LIST_INIT(server_list);
1889927deb69Sflorian 	ISC_LIST_INIT(root_hints_server_list);
18905185a700Sflorian 	ISC_LIST_INIT(search_list);
18915185a700Sflorian 
18925185a700Sflorian 	if (pledge("stdio rpath inet dns", NULL) == -1) {
18935185a700Sflorian 		perror("pledge");
18945185a700Sflorian 		exit(1);
18955185a700Sflorian 	}
18965185a700Sflorian 
18975185a700Sflorian 	debug("dig_setup()");
18985185a700Sflorian 
18995185a700Sflorian 	/* setup dighost callbacks */
19005185a700Sflorian 	dighost_printmessage = printmessage;
19015185a700Sflorian 	dighost_received = received;
19025185a700Sflorian 	dighost_trying = trying;
19035185a700Sflorian 	dighost_shutdown = query_finished;
19045185a700Sflorian 
19055185a700Sflorian 	progname = argv[0];
19065185a700Sflorian 	preparse_args(argc, argv);
19075185a700Sflorian 
19085185a700Sflorian 	result = isc_app_start();
19095185a700Sflorian 	check_result(result, "isc_app_start");
19105185a700Sflorian 
19115185a700Sflorian 	setup_libs();
19125185a700Sflorian 	setup_system(ipv4only, ipv6only);
19135185a700Sflorian }
19145185a700Sflorian 
19151fb015a8Sflorian void dig_query_setup(int is_batchfile, int config_only,
19165185a700Sflorian 		int argc, char **argv)
19175185a700Sflorian {
19185185a700Sflorian 	debug("dig_query_setup");
19195185a700Sflorian 
19205185a700Sflorian 	parse_args(is_batchfile, config_only, argc, argv);
19215185a700Sflorian 	if (keyfile[0] != 0)
19225185a700Sflorian 		setup_file_key();
19235185a700Sflorian 	else if (keysecret[0] != 0)
19245185a700Sflorian 		setup_text_key();
19255185a700Sflorian 
19265185a700Sflorian 	if (pledge("stdio inet dns", NULL) == -1) {
19275185a700Sflorian 		perror("pledge");
19285185a700Sflorian 		exit(1);
19295185a700Sflorian 	}
19305185a700Sflorian 
19315185a700Sflorian 	if (domainopt[0] != '\0') {
19325185a700Sflorian 		set_search_domain(domainopt);
19331fb015a8Sflorian 		usesearch = 1;
19345185a700Sflorian 	}
19355185a700Sflorian }
19365185a700Sflorian 
1937a83f8be2Sjsg void dig_startup(void) {
19385185a700Sflorian 	isc_result_t result;
19395185a700Sflorian 
19405185a700Sflorian 	debug("dig_startup()");
19415185a700Sflorian 
19425185a700Sflorian 	result = isc_app_onrun(global_task, onrun_callback, NULL);
19435185a700Sflorian 	check_result(result, "isc_app_onrun");
19445185a700Sflorian 	isc_app_run();
19455185a700Sflorian }
19465185a700Sflorian 
19475185a700Sflorian void
1948a83f8be2Sjsg dig_shutdown(void) {
19495185a700Sflorian 	destroy_lookup(default_lookup);
19505185a700Sflorian 	if (batchname != NULL) {
19515185a700Sflorian 		if (batchfp != stdin)
19525185a700Sflorian 			fclose(batchfp);
19535185a700Sflorian 		batchname = NULL;
19545185a700Sflorian 	}
19555185a700Sflorian 
19565185a700Sflorian 	cancel_all();
19575185a700Sflorian 	destroy_libs();
19585185a700Sflorian }
19595185a700Sflorian 
19605185a700Sflorian /*% Main processing routine for dig */
19615185a700Sflorian int
19625185a700Sflorian main(int argc, char **argv) {
19635185a700Sflorian 	extern char *__progname;
19645185a700Sflorian 
19655185a700Sflorian 	if (strcmp("host", __progname) == 0)
19665185a700Sflorian 		return host_main(argc, argv);
19675185a700Sflorian 	if (strcmp("nslookup", __progname) == 0)
19685185a700Sflorian 		return nslookup_main(argc, argv);
19695185a700Sflorian 
19705185a700Sflorian 	dig_setup(argc, argv);
19711fb015a8Sflorian 	dig_query_setup(0, 0, argc, argv);
19725185a700Sflorian 	dig_startup();
19735185a700Sflorian 	dig_shutdown();
19745185a700Sflorian 
19755185a700Sflorian 	return (exitcode);
19765185a700Sflorian }
1977