dig.c revision 1.20
1/*
2 * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
9 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10 * AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
11 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
13 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14 * PERFORMANCE OF THIS SOFTWARE.
15 */
16
17/* $Id: dig.c,v 1.20 2023/09/06 04:57:28 jsg Exp $ */
18
19/*! \file */
20#include <sys/types.h>
21#include <sys/socket.h>
22
23#include <netdb.h>
24
25#include <errno.h>
26#include <stdlib.h>
27#include <time.h>
28#include <unistd.h>
29
30#include <isc/app.h>
31
32#include <string.h>
33#include <isc/util.h>
34
35#include <dns/fixedname.h>
36#include <dns/masterdump.h>
37#include <dns/message.h>
38#include <dns/name.h>
39#include <dns/rdata.h>
40#include <dns/rdataset.h>
41#include <dns/rdatatype.h>
42#include <dns/rdataclass.h>
43#include <dns/result.h>
44#include <dns/tsig.h>
45
46#include "dig.h"
47
48#define ADD_STRING(b, s) { 				\
49	if (strlen(s) >= isc_buffer_availablelength(b)) \
50		return (ISC_R_NOSPACE); 		\
51	else 						\
52		isc_buffer_putstr(b, s); 		\
53}
54
55dig_lookup_t *default_lookup = NULL;
56
57static char *batchname = NULL;
58static FILE *batchfp = NULL;
59static char *argv0;
60static int addresscount = 0;
61
62static char domainopt[DNS_NAME_MAXTEXT];
63static char sitvalue[256];
64
65static int short_form = 0, printcmd = 1,
66	ip6_int = 0, plusquest = 0, pluscomm = 0,
67	multiline = 0, nottl = 0, noclass = 0,
68	onesoa = 0, use_usec = 0, nocrypto = 0,
69	ipv4only = 0, ipv6only = 0;
70static uint32_t splitwidth = 0xffffffff;
71
72/*% rrcomments are neither explicitly enabled nor disabled by default */
73static int rrcomments = 0;
74
75/*% opcode text */
76static const char * const opcodetext[] = {
77	"QUERY",
78	"IQUERY",
79	"STATUS",
80	"RESERVED3",
81	"NOTIFY",
82	"UPDATE",
83	"RESERVED6",
84	"RESERVED7",
85	"RESERVED8",
86	"RESERVED9",
87	"RESERVED10",
88	"RESERVED11",
89	"RESERVED12",
90	"RESERVED13",
91	"RESERVED14",
92	"RESERVED15"
93};
94
95/*% return code text */
96static const char * const rcodetext[] = {
97	"NOERROR",
98	"FORMERR",
99	"SERVFAIL",
100	"NXDOMAIN",
101	"NOTIMP",
102	"REFUSED",
103	"YXDOMAIN",
104	"YXRRSET",
105	"NXRRSET",
106	"NOTAUTH",
107	"NOTZONE",
108	"RESERVED11",
109	"RESERVED12",
110	"RESERVED13",
111	"RESERVED14",
112	"RESERVED15",
113	"BADVERS"
114};
115
116/*% safe rcodetext[] */
117static const char *
118rcode_totext(dns_rcode_t rcode)
119{
120	static char buf[sizeof("?65535")];
121
122	if (rcode == dns_rcode_badcookie)
123		return ("BADCOOKIE");
124	if (rcode >= (sizeof(rcodetext)/sizeof(rcodetext[0]))) {
125		snprintf(buf, sizeof(buf), "?%u", rcode);
126		return (buf);
127	}
128	return (rcodetext[rcode]);
129}
130
131/*% print usage */
132static void
133print_usage(FILE *fp) {
134	fputs(
135"usage: dig [@server] [-46hiuv] [-b sourceaddr[#port]] [-c class] [-f file]\n"
136"           [-k keyfile] [-p port] [-q name] [-t type] [-x addr]\n"
137"           [-y [hmac:]name:key] [name] [type] [class]\n"
138"           +[no]aaonly +[no]additional +[no]adflag +[no]all +[no]answer\n"
139"           +[no]authority +[no]besteffort +bufsize=# +[no]cdflag +[no]class\n"
140"           +[no]cmd +[no]comments +[no]cookie[=value] +[no]crypto +[no]dnssec\n"
141"           +domain=name +[no]edns[=#] +ednsflags[=#] +[no]ednsnegotiation\n"
142"           +[no]ednsopt[=code[:value]] +[no]expire +[no]fail +[no]identify\n"
143"           +[no]ignore +[no]keepopen +[no]multiline +ndots=# +[no]nsid\n"
144"           +[no]nssearch +[no]onesoa +[no]opcode=# +[no]qr +[no]question\n"
145"           +[no]recurse +retry=# +[no]rrcomments +[no]search +[no]short\n"
146"           +[no]showsearch +[no]split=# +[no]stats +[no]subnet=addr[/prefix]\n"
147"           +[no]tcp +timeout=# +[no]trace +tries=# +[no]ttlid +[no]vc\n", fp);
148}
149
150static __dead void
151usage(void);
152
153static void
154usage(void) {
155	print_usage(stderr);
156	exit(1);
157}
158
159/*% version */
160static void
161version(void) {
162	fputs("dig " VERSION "\n", stderr);
163}
164
165/*% help */
166static void
167help(void) {
168	print_usage(stdout);
169}
170
171/*%
172 * Callback from dighost.c to print the received message.
173 */
174static void
175received(unsigned int bytes, struct sockaddr_storage *from, dig_query_t *query) {
176	time_t tnow;
177	struct tm tmnow;
178	char time_str[100];
179	char fromtext[ISC_SOCKADDR_FORMATSIZE];
180
181	isc_sockaddr_format(from, fromtext, sizeof(fromtext));
182
183	if (query->lookup->stats && !short_form) {
184		if (use_usec)
185			printf(";; Query time: %lld usec\n",
186			    uelapsed(&query->time_recv, &query->time_sent));
187		else
188			printf(";; Query time: %lld msec\n",
189			    uelapsed(&query->time_recv, &query->time_sent) /
190			    1000);
191		printf(";; SERVER: %s(%s)\n", fromtext, query->servname);
192		time(&tnow);
193		tmnow  = *localtime(&tnow);
194
195		if (strftime(time_str, sizeof(time_str),
196			     "%a %b %d %H:%M:%S %Z %Y", &tmnow) > 0U)
197			printf(";; WHEN: %s\n", time_str);
198		if (query->lookup->doing_xfr) {
199			printf(";; XFR size: %u records (messages %u, "
200			       "bytes %llu)\n",
201			       query->rr_count, query->msg_count,
202			       query->byte_count);
203		} else {
204			printf(";; MSG SIZE  rcvd: %u\n", bytes);
205		}
206		if (tsigkey != NULL) {
207			if (!validated)
208				puts(";; WARNING -- Some TSIG could not "
209				     "be validated");
210		}
211		if ((tsigkey == NULL) && (keysecret[0] != 0)) {
212			puts(";; WARNING -- TSIG key was not used.");
213		}
214		puts("");
215	} else if (query->lookup->identify && !short_form) {
216		if (use_usec)
217			printf(";; Received %llu bytes "
218			    "from %s(%s) in %lld us\n\n",
219			    query->lookup->doing_xfr
220			    ? query->byte_count
221			    : (uint64_t)bytes,
222			    fromtext, query->userarg,
223			    uelapsed(&query->time_recv, &query->time_sent));
224		else
225			printf(";; Received %llu bytes "
226			    "from %s(%s) in %lld ms\n\n",
227			    query->lookup->doing_xfr
228			    ?  query->byte_count
229			    : (uint64_t)bytes,
230			    fromtext, query->userarg,
231			    uelapsed(&query->time_recv, &query->time_sent) /
232			    1000);
233	}
234}
235
236/*
237 * Callback from dighost.c to print that it is trying a server.
238 * Not used in dig.
239 * XXX print_trying
240 */
241static void
242trying(char *frm, dig_lookup_t *lookup) {
243	UNUSED(frm);
244	UNUSED(lookup);
245}
246
247/*%
248 * Internal print routine used to print short form replies.
249 */
250static isc_result_t
251say_message(dns_rdata_t *rdata, dig_query_t *query, isc_buffer_t *buf) {
252	isc_result_t result;
253	char store[sizeof(" in 18446744073709551616 us.")];
254	unsigned int styleflags = 0;
255
256	if (query->lookup->trace || query->lookup->ns_search_only) {
257		result = dns_rdatatype_totext(rdata->type, buf);
258		if (result != ISC_R_SUCCESS)
259			return (result);
260		ADD_STRING(buf, " ");
261	}
262
263	/* Turn on rrcomments if explicitly enabled */
264	if (rrcomments > 0)
265		styleflags |= DNS_STYLEFLAG_RRCOMMENT;
266	if (nocrypto)
267		styleflags |= DNS_STYLEFLAG_NOCRYPTO;
268	result = dns_rdata_tofmttext(rdata, NULL, styleflags, 0,
269				     splitwidth, " ", buf);
270	if (result == ISC_R_NOSPACE)
271		return (result);
272	check_result(result, "dns_rdata_totext");
273	if (query->lookup->identify) {
274		ADD_STRING(buf, " from server ");
275		ADD_STRING(buf, query->servname);
276		if (use_usec)
277			snprintf(store, sizeof(store), " in %lld us.",
278			    uelapsed(&query->time_recv, &query->time_sent));
279		else
280			snprintf(store, sizeof(store), " in %lld ms.",
281			    uelapsed(&query->time_recv, &query->time_sent) /
282			    1000);
283		ADD_STRING(buf, store);
284	}
285	ADD_STRING(buf, "\n");
286	return (ISC_R_SUCCESS);
287}
288
289/*%
290 * short_form message print handler.  Calls above say_message()
291 */
292static isc_result_t
293short_answer(dns_message_t *msg, dns_messagetextflag_t flags,
294	     isc_buffer_t *buf, dig_query_t *query)
295{
296	dns_name_t *name;
297	dns_rdataset_t *rdataset;
298	isc_result_t result, loopresult;
299	dns_name_t empty_name;
300	dns_rdata_t rdata = DNS_RDATA_INIT;
301
302	UNUSED(flags);
303
304	dns_name_init(&empty_name, NULL);
305	result = dns_message_firstname(msg, DNS_SECTION_ANSWER);
306	if (result == ISC_R_NOMORE)
307		return (ISC_R_SUCCESS);
308	else if (result != ISC_R_SUCCESS)
309		return (result);
310
311	for (;;) {
312		name = NULL;
313		dns_message_currentname(msg, DNS_SECTION_ANSWER, &name);
314
315		for (rdataset = ISC_LIST_HEAD(name->list);
316		     rdataset != NULL;
317		     rdataset = ISC_LIST_NEXT(rdataset, link)) {
318			loopresult = dns_rdataset_first(rdataset);
319			while (loopresult == ISC_R_SUCCESS) {
320				dns_rdataset_current(rdataset, &rdata);
321				result = say_message(&rdata, query,
322						     buf);
323				if (result == ISC_R_NOSPACE)
324					return (result);
325				check_result(result, "say_message");
326				loopresult = dns_rdataset_next(rdataset);
327				dns_rdata_reset(&rdata);
328			}
329		}
330		result = dns_message_nextname(msg, DNS_SECTION_ANSWER);
331		if (result == ISC_R_NOMORE)
332			break;
333		else if (result != ISC_R_SUCCESS)
334			return (result);
335	}
336
337	return (ISC_R_SUCCESS);
338}
339
340static int
341isdotlocal(dns_message_t *msg) {
342	isc_result_t result;
343	static unsigned char local_ndata[] = { "\005local\0" };
344	static unsigned char local_offsets[] = { 0, 6 };
345	static dns_name_t local =
346		DNS_NAME_INITABSOLUTE(local_ndata, local_offsets);
347
348	for (result = dns_message_firstname(msg, DNS_SECTION_QUESTION);
349	     result == ISC_R_SUCCESS;
350	     result = dns_message_nextname(msg, DNS_SECTION_QUESTION))
351	{
352		dns_name_t *name = NULL;
353		dns_message_currentname(msg, DNS_SECTION_QUESTION, &name);
354		if (dns_name_issubdomain(name, &local))
355			return (1);
356	}
357	return (0);
358}
359
360/*
361 * Callback from dighost.c to print the reply from a server
362 */
363static isc_result_t
364printmessage(dig_query_t *query, dns_message_t *msg, int headers) {
365	isc_result_t result;
366	dns_messagetextflag_t flags;
367	isc_buffer_t *buf = NULL;
368	unsigned int len = OUTPUTBUF;
369	dns_master_style_t *style = NULL;
370	unsigned int styleflags = 0;
371
372	styleflags |= DNS_STYLEFLAG_REL_OWNER;
373	if (query->lookup->comments)
374		styleflags |= DNS_STYLEFLAG_COMMENT;
375	/* Turn on rrcomments if explicitly enabled */
376	if (rrcomments > 0)
377		styleflags |= DNS_STYLEFLAG_RRCOMMENT;
378	if (nottl)
379		styleflags |= DNS_STYLEFLAG_NO_TTL;
380	if (noclass)
381		styleflags |= DNS_STYLEFLAG_NO_CLASS;
382	if (nocrypto)
383		styleflags |= DNS_STYLEFLAG_NOCRYPTO;
384	if (multiline) {
385		styleflags |= DNS_STYLEFLAG_OMIT_OWNER;
386		styleflags |= DNS_STYLEFLAG_OMIT_CLASS;
387		styleflags |= DNS_STYLEFLAG_REL_DATA;
388		styleflags |= DNS_STYLEFLAG_OMIT_TTL;
389		styleflags |= DNS_STYLEFLAG_TTL;
390		styleflags |= DNS_STYLEFLAG_MULTILINE;
391		/* Turn on rrcomments unless explicitly disabled */
392		if (rrcomments >= 0)
393			styleflags |= DNS_STYLEFLAG_RRCOMMENT;
394	}
395	if (multiline || (nottl && noclass))
396		result = dns_master_stylecreate2(&style, styleflags,
397						 24, 24, 24, 32, 80, 8,
398						 splitwidth);
399	else if (nottl || noclass)
400		result = dns_master_stylecreate2(&style, styleflags,
401						 24, 24, 32, 40, 80, 8,
402						 splitwidth);
403	else
404		result = dns_master_stylecreate2(&style, styleflags,
405						 24, 32, 40, 48, 80, 8,
406						 splitwidth);
407	check_result(result, "dns_master_stylecreate");
408
409	if (query->lookup->cmdline[0] != 0) {
410		if (!short_form)
411			fputs(query->lookup->cmdline, stdout);
412		query->lookup->cmdline[0]=0;
413	}
414	debug("printmessage(%s %s %s)", headers ? "headers" : "noheaders",
415	      query->lookup->comments ? "comments" : "nocomments",
416	      short_form ? "short_form" : "long_form");
417
418	flags = 0;
419	if (!headers) {
420		flags |= DNS_MESSAGETEXTFLAG_NOHEADERS;
421		flags |= DNS_MESSAGETEXTFLAG_NOCOMMENTS;
422	}
423	if (onesoa && query->lookup->rdtype == dns_rdatatype_axfr)
424		flags |= (query->msg_count == 0) ? DNS_MESSAGETEXTFLAG_ONESOA :
425						   DNS_MESSAGETEXTFLAG_OMITSOA;
426	if (!query->lookup->comments)
427		flags |= DNS_MESSAGETEXTFLAG_NOCOMMENTS;
428
429	result = isc_buffer_allocate(&buf, len);
430	check_result(result, "isc_buffer_allocate");
431
432	if (query->lookup->comments && !short_form) {
433		if (query->lookup->cmdline[0] != 0)
434			printf("; %s\n", query->lookup->cmdline);
435		if (msg == query->lookup->sendmsg)
436			printf(";; Sending:\n");
437		else
438			printf(";; Got answer:\n");
439
440		if (headers) {
441			if (isdotlocal(msg)) {
442				printf(";; WARNING: .local is reserved for "
443				       "Multicast DNS\n;; You are currently "
444				       "testing what happens when an mDNS "
445				       "query is leaked to DNS\n");
446			}
447			printf(";; ->>HEADER<<- opcode: %s, status: %s, "
448			       "id: %u\n",
449			       opcodetext[msg->opcode],
450			       rcode_totext(msg->rcode),
451			       msg->id);
452			printf(";; flags:");
453			if ((msg->flags & DNS_MESSAGEFLAG_QR) != 0)
454				printf(" qr");
455			if ((msg->flags & DNS_MESSAGEFLAG_AA) != 0)
456				printf(" aa");
457			if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0)
458				printf(" tc");
459			if ((msg->flags & DNS_MESSAGEFLAG_RD) != 0)
460				printf(" rd");
461			if ((msg->flags & DNS_MESSAGEFLAG_RA) != 0)
462				printf(" ra");
463			if ((msg->flags & DNS_MESSAGEFLAG_AD) != 0)
464				printf(" ad");
465			if ((msg->flags & DNS_MESSAGEFLAG_CD) != 0)
466				printf(" cd");
467			if ((msg->flags & 0x0040U) != 0)
468				printf("; MBZ: 0x4");
469
470			printf("; QUERY: %u, ANSWER: %u, "
471			       "AUTHORITY: %u, ADDITIONAL: %u\n",
472			       msg->counts[DNS_SECTION_QUESTION],
473			       msg->counts[DNS_SECTION_ANSWER],
474			       msg->counts[DNS_SECTION_AUTHORITY],
475			       msg->counts[DNS_SECTION_ADDITIONAL]);
476
477			if (msg != query->lookup->sendmsg &&
478			    (msg->flags & DNS_MESSAGEFLAG_RD) != 0 &&
479			    (msg->flags & DNS_MESSAGEFLAG_RA) == 0)
480				printf(";; WARNING: recursion requested "
481				       "but not available\n");
482		}
483		if (msg != query->lookup->sendmsg &&
484		    query->lookup->edns != -1 && msg->opt == NULL &&
485		    (msg->rcode == dns_rcode_formerr ||
486		     msg->rcode == dns_rcode_notimp))
487			printf("\n;; WARNING: EDNS query returned status "
488			       "%s - retry with '%s+noedns'\n",
489			       rcode_totext(msg->rcode),
490			       query->lookup->dnssec ? "+nodnssec ": "");
491		if (msg != query->lookup->sendmsg && extrabytes != 0U)
492			printf(";; WARNING: Message has %u extra byte%s at "
493			       "end\n", extrabytes, extrabytes != 0 ? "s" : "");
494	}
495
496repopulate_buffer:
497
498	if (query->lookup->comments && headers && !short_form) {
499		result = dns_message_pseudosectiontotext(msg,
500			 DNS_PSEUDOSECTION_OPT,
501			 style, flags, buf);
502		if (result == ISC_R_NOSPACE) {
503buftoosmall:
504			len += OUTPUTBUF;
505			isc_buffer_free(&buf);
506			result = isc_buffer_allocate(&buf, len);
507			if (result == ISC_R_SUCCESS)
508				goto repopulate_buffer;
509			else
510				goto cleanup;
511		}
512		check_result(result,
513		     "dns_message_pseudosectiontotext");
514	}
515
516	if (query->lookup->section_question && headers) {
517		if (!short_form) {
518			result = dns_message_sectiontotext(msg,
519						       DNS_SECTION_QUESTION,
520						       style, flags, buf);
521			if (result == ISC_R_NOSPACE)
522				goto buftoosmall;
523			check_result(result, "dns_message_sectiontotext");
524		}
525	}
526	if (query->lookup->section_answer) {
527		if (!short_form) {
528			result = dns_message_sectiontotext(msg,
529						       DNS_SECTION_ANSWER,
530						       style, flags, buf);
531			if (result == ISC_R_NOSPACE)
532				goto buftoosmall;
533			check_result(result, "dns_message_sectiontotext");
534		} else {
535			result = short_answer(msg, flags, buf, query);
536			if (result == ISC_R_NOSPACE)
537				goto buftoosmall;
538			check_result(result, "short_answer");
539		}
540	}
541	if (query->lookup->section_authority) {
542		if (!short_form) {
543			result = dns_message_sectiontotext(msg,
544						       DNS_SECTION_AUTHORITY,
545						       style, flags, buf);
546			if (result == ISC_R_NOSPACE)
547				goto buftoosmall;
548			check_result(result, "dns_message_sectiontotext");
549		}
550	}
551	if (query->lookup->section_additional) {
552		if (!short_form) {
553			result = dns_message_sectiontotext(msg,
554						      DNS_SECTION_ADDITIONAL,
555						      style, flags, buf);
556			if (result == ISC_R_NOSPACE)
557				goto buftoosmall;
558			check_result(result, "dns_message_sectiontotext");
559			/*
560			 * Only print the signature on the first record.
561			 */
562			if (headers) {
563				result = dns_message_pseudosectiontotext(
564						   msg,
565						   DNS_PSEUDOSECTION_TSIG,
566						   style, flags, buf);
567				if (result == ISC_R_NOSPACE)
568					goto buftoosmall;
569				check_result(result,
570					  "dns_message_pseudosectiontotext");
571				result = dns_message_pseudosectiontotext(
572						   msg,
573						   DNS_PSEUDOSECTION_SIG0,
574						   style, flags, buf);
575				if (result == ISC_R_NOSPACE)
576					goto buftoosmall;
577				check_result(result,
578					   "dns_message_pseudosectiontotext");
579			}
580		}
581	}
582
583	if (headers && query->lookup->comments && !short_form)
584		printf("\n");
585
586	printf("%.*s", (int)isc_buffer_usedlength(buf),
587	       (char *)isc_buffer_base(buf));
588	isc_buffer_free(&buf);
589
590cleanup:
591	if (style != NULL)
592		dns_master_styledestroy(&style);
593	return (result);
594}
595
596/*%
597 * print the greeting message when the program first starts up.
598 */
599static void
600printgreeting(int argc, char **argv, dig_lookup_t *lookup) {
601	int i;
602	static int first = 1;
603	char append[MXNAME];
604
605	if (printcmd) {
606		snprintf(lookup->cmdline, sizeof(lookup->cmdline),
607			 "%s; <<>> dig " VERSION " <<>>",
608			 first?"\n":"");
609		i = 1;
610		while (i < argc) {
611			snprintf(append, sizeof(append), " %s", argv[i++]);
612			strlcat(lookup->cmdline, append,
613				sizeof(lookup->cmdline));
614		}
615		strlcat(lookup->cmdline, "\n", sizeof(lookup->cmdline));
616		if (first && addresscount != 0) {
617			snprintf(append, sizeof(append),
618				 "; (%d server%s found)\n",
619				 addresscount,
620				 addresscount > 1 ? "s" : "");
621			strlcat(lookup->cmdline, append,
622				sizeof(lookup->cmdline));
623		}
624		if (first) {
625			snprintf(append, sizeof(append),
626				 ";; global options:%s%s\n",
627				 short_form ? " +short" : "",
628				 printcmd ? " +cmd" : "");
629			first = 0;
630			strlcat(lookup->cmdline, append,
631				sizeof(lookup->cmdline));
632		}
633	}
634}
635
636static void
637plus_option(const char *option, int is_batchfile,
638	    dig_lookup_t *lookup)
639{
640	isc_result_t result;
641	char option_store[256];
642	char *cmd, *value, *ptr, *code, *ep;
643	const char *errstr;
644	long lval;
645	uint32_t num;
646	int state = 1;
647	size_t n;
648
649	strlcpy(option_store, option, sizeof(option_store));
650	ptr = option_store;
651	cmd = next_token(&ptr, "=");
652	if (cmd == NULL) {
653		printf(";; Invalid option %s\n", option_store);
654		return;
655	}
656	value = ptr;
657	if (strncasecmp(cmd, "no", 2)==0) {
658		cmd += 2;
659		state = 0;
660	}
661
662#define FULLCHECK(A) \
663	do { \
664		size_t _l = strlen(cmd); \
665		if (_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) \
666			goto invalid_option; \
667	} while (0)
668#define FULLCHECK2(A, B) \
669	do { \
670		size_t _l = strlen(cmd); \
671		if ((_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) && \
672		    (_l >= sizeof(B) || strncasecmp(cmd, B, _l) != 0)) \
673			goto invalid_option; \
674	} while (0)
675
676	switch (cmd[0]) {
677	case 'a':
678		switch (cmd[1]) {
679		case 'a': /* aaonly / aaflag */
680			FULLCHECK2("aaonly", "aaflag");
681			lookup->aaonly = state;
682			break;
683		case 'd':
684			switch (cmd[2]) {
685			case 'd': /* additional */
686				FULLCHECK("additional");
687				lookup->section_additional = state;
688				break;
689			case 'f': /* adflag */
690			case '\0': /* +ad is a synonym for +adflag */
691				FULLCHECK("adflag");
692				lookup->adflag = state;
693				break;
694			default:
695				goto invalid_option;
696			}
697			break;
698		case 'l': /* all */
699			FULLCHECK("all");
700			lookup->section_question = state;
701			lookup->section_authority = state;
702			lookup->section_answer = state;
703			lookup->section_additional = state;
704			lookup->comments = state;
705			lookup->stats = state;
706			printcmd = state;
707			break;
708		case 'n': /* answer */
709			FULLCHECK("answer");
710			lookup->section_answer = state;
711			break;
712		case 'u': /* authority */
713			FULLCHECK("authority");
714			lookup->section_authority = state;
715			break;
716		default:
717			goto invalid_option;
718		}
719		break;
720	case 'b':
721		switch (cmd[1]) {
722		case 'e':/* besteffort */
723			FULLCHECK("besteffort");
724			lookup->besteffort = state;
725			break;
726		case 'u':/* bufsize */
727			FULLCHECK("bufsize");
728			if (value == NULL)
729				goto need_value;
730			if (!state)
731				goto invalid_option;
732			num = strtonum(value, 0, COMMSIZE, &errstr);
733			if (errstr != NULL)
734				fatal("buffer size is %s: '%s'", errstr, value);
735			lookup->udpsize = num;
736			break;
737		default:
738			goto invalid_option;
739		}
740		break;
741	case 'c':
742		switch (cmd[1]) {
743		case 'd':/* cdflag */
744			switch (cmd[2]) {
745			case 'f': /* cdflag */
746			case '\0': /* +cd is a synonym for +cdflag */
747				FULLCHECK("cdflag");
748				lookup->cdflag = state;
749				break;
750			default:
751				goto invalid_option;
752			}
753			break;
754		case 'l': /* class */
755			/* keep +cl for backwards compatibility */
756			FULLCHECK2("cl", "class");
757			noclass = !state;
758			break;
759		case 'm': /* cmd */
760			FULLCHECK("cmd");
761			printcmd = state;
762			break;
763		case 'o': /* comments */
764			switch (cmd[2]) {
765			case 'o':
766				FULLCHECK("cookie");
767				goto sit;
768			case 'm':
769				FULLCHECK("comments");
770				lookup->comments = state;
771				if (lookup == default_lookup)
772					pluscomm = state;
773				break;
774			default:
775				goto invalid_option;
776			}
777			break;
778		case 'r':
779			FULLCHECK("crypto");
780			nocrypto = !state;
781			break;
782		default:
783			goto invalid_option;
784		}
785		break;
786	case 'd':
787		switch (cmd[1]) {
788		case 'e': /* defname */
789			FULLCHECK("defname");
790			if (!lookup->trace) {
791				usesearch = state;
792			}
793			break;
794		case 'n': /* dnssec */
795			FULLCHECK("dnssec");
796			if (state && lookup->edns == -1)
797				lookup->edns = 0;
798			lookup->dnssec = state;
799			break;
800		case 'o': /* domain */
801			FULLCHECK("domain");
802			if (value == NULL)
803				goto need_value;
804			if (!state)
805				goto invalid_option;
806			strlcpy(domainopt, value, sizeof(domainopt));
807			break;
808		default:
809			goto invalid_option;
810		}
811		break;
812	case 'e':
813		switch (cmd[1]) {
814		case 'd':
815			switch(cmd[2]) {
816			case 'n':
817				switch (cmd[3]) {
818				case 's':
819					switch (cmd[4]) {
820					case 0:
821						FULLCHECK("edns");
822						if (!state) {
823							lookup->edns = -1;
824							break;
825						}
826						if (value == NULL) {
827							lookup->edns = 0;
828							break;
829						}
830						num = strtonum(value, 0, 255,
831						    &errstr);
832						if (errstr != NULL)
833							fatal("edns is %s: "
834							    "'%s'", errstr,
835							    value);
836						lookup->edns = num;
837						break;
838					case 'f':
839						FULLCHECK("ednsflags");
840						if (!state) {
841							lookup->ednsflags = 0;
842							break;
843						}
844						if (value == NULL) {
845							lookup->ednsflags = 0;
846							break;
847						}
848						errno = 0;
849						lval = strtol(value, &ep, 0);
850						if (value[0] == '\0' || *ep !=
851						    '\0' || lval < 0 || lval >
852						    0xffff || errno != 0)
853							fatal("Couldn't parse "
854							      "ednsflags");
855						lookup->ednsflags = lval;
856						break;
857					case 'n':
858						FULLCHECK("ednsnegotiation");
859						lookup->ednsneg = state;
860						break;
861					case 'o':
862						FULLCHECK("ednsopt");
863						if (!state) {
864							lookup->ednsoptscnt = 0;
865							break;
866						}
867						if (value == NULL)
868							fatal("ednsopt no "
869							      "code point "
870							      "specified");
871						code = next_token(&value, ":");
872						save_opt(lookup, code, value);
873						break;
874					default:
875						goto invalid_option;
876					}
877					break;
878				default:
879					goto invalid_option;
880				}
881				break;
882			default:
883				goto invalid_option;
884			}
885			break;
886		case 'x':
887			FULLCHECK("expire");
888			lookup->expire = state;
889			break;
890		default:
891			goto invalid_option;
892		}
893		break;
894	case 'f': /* fail */
895		FULLCHECK("fail");
896		lookup->servfail_stops = state;
897		break;
898	case 'i':
899		switch (cmd[1]) {
900		case 'd': /* identify */
901			switch (cmd[2]) {
902			case 'e':
903				FULLCHECK("identify");
904				lookup->identify = state;
905				break;
906			case 'n':
907				FULLCHECK("idnout");
908				fprintf(stderr, ";; IDN support not enabled\n");
909				break;
910			default:
911				goto invalid_option;
912			}
913			break;
914		case 'g': /* ignore */
915		default: /*
916			  * Inherits default for compatibility (+[no]i*).
917			  */
918			FULLCHECK("ignore");
919			lookup->ignore = state;
920		}
921		break;
922	case 'k':
923		FULLCHECK("keepopen");
924		keep_open = state;
925		break;
926	case 'm': /* multiline */
927		FULLCHECK("multiline");
928		multiline = state;
929		break;
930	case 'n':
931		switch (cmd[1]) {
932		case 'd': /* ndots */
933			FULLCHECK("ndots");
934			if (value == NULL)
935				goto need_value;
936			if (!state)
937				goto invalid_option;
938			num = strtonum(value, 0, MAXNDOTS, &errstr);
939			if (errstr != NULL)
940				fatal("ndots is %s: '%s'", errstr, value);
941			ndots = num;
942			break;
943		case 's':
944			switch (cmd[2]) {
945			case 'i': /* nsid */
946				FULLCHECK("nsid");
947				if (state && lookup->edns == -1)
948					lookup->edns = 0;
949				lookup->nsid = state;
950				break;
951			case 's': /* nssearch */
952				FULLCHECK("nssearch");
953				lookup->ns_search_only = state;
954				if (state) {
955					lookup->trace_root = 1;
956					lookup->recurse = 1;
957					lookup->identify = 1;
958					lookup->stats = 0;
959					lookup->comments = 0;
960					lookup->section_additional = 0;
961					lookup->section_authority = 0;
962					lookup->section_question = 0;
963					lookup->rdtype = dns_rdatatype_ns;
964					lookup->rdtypeset = 1;
965					short_form = 1;
966					rrcomments = 0;
967				}
968				break;
969			default:
970				goto invalid_option;
971			}
972			break;
973		default:
974			goto invalid_option;
975		}
976		break;
977	case 'o':
978		switch (cmd[1]) {
979		case 'n':
980			FULLCHECK("onesoa");
981			onesoa = state;
982			break;
983		case 'p':
984			FULLCHECK("opcode");
985			if (!state) {
986				lookup->opcode = 0;	/* default - query */
987				break;
988			}
989			if (value == NULL)
990				goto need_value;
991			for (num = 0;
992			     num < sizeof(opcodetext)/sizeof(opcodetext[0]);
993			     num++) {
994				if (strcasecmp(opcodetext[num], value) == 0)
995					break;
996			}
997			if (num < 16) {
998				lookup->opcode = (dns_opcode_t)num;
999				break;
1000			}
1001			num = strtonum(value, 0, 15, &errstr);
1002			if (errstr != NULL)
1003				fatal("opcode is %s: '%s'", errstr, value);
1004			lookup->opcode = (dns_opcode_t)num;
1005			break;
1006		default:
1007			goto invalid_option;
1008		}
1009		break;
1010	case 'q':
1011		switch (cmd[1]) {
1012		case 'r': /* qr */
1013			FULLCHECK("qr");
1014			qr = state;
1015			break;
1016		case 'u': /* question */
1017			FULLCHECK("question");
1018			lookup->section_question = state;
1019			if (lookup == default_lookup)
1020				plusquest = state;
1021			break;
1022		default:
1023			goto invalid_option;
1024		}
1025		break;
1026	case 'r':
1027		switch (cmd[1]) {
1028		case 'd': /* rdflag */
1029			FULLCHECK("rdflag");
1030			lookup->recurse = state;
1031			break;
1032		case 'e':
1033			switch (cmd[2]) {
1034			case 'c': /* recurse */
1035				FULLCHECK("recurse");
1036				lookup->recurse = state;
1037				break;
1038			case 't': /* retry / retries */
1039				FULLCHECK2("retry", "retries");
1040				if (value == NULL)
1041					goto need_value;
1042				if (!state)
1043					goto invalid_option;
1044				lookup->retries = strtonum(value, 0,
1045				    MAXTRIES - 1, &errstr);
1046				if (errstr != NULL)
1047					fatal("retries is %s: '%s'", errstr,
1048					    value);
1049				lookup->retries++;
1050				break;
1051			default:
1052				goto invalid_option;
1053			}
1054			break;
1055		case 'r': /* rrcomments */
1056			FULLCHECK("rrcomments");
1057			rrcomments = state ? 1 : -1;
1058			break;
1059		default:
1060			goto invalid_option;
1061		}
1062		break;
1063	case 's':
1064		switch (cmd[1]) {
1065		case 'e': /* search */
1066			FULLCHECK("search");
1067			if (!lookup->trace) {
1068				usesearch = state;
1069			}
1070			break;
1071		case 'h':
1072			if (cmd[2] != 'o')
1073				goto invalid_option;
1074			switch (cmd[3]) {
1075			case 'r': /* short */
1076				FULLCHECK("short");
1077				short_form = state;
1078				if (state) {
1079					printcmd = 0;
1080					lookup->section_additional = 0;
1081					lookup->section_answer = 1;
1082					lookup->section_authority = 0;
1083					lookup->section_question = 0;
1084					lookup->comments = 0;
1085					lookup->stats = 0;
1086					rrcomments = -1;
1087				}
1088				break;
1089			case 'w': /* showsearch */
1090				FULLCHECK("showsearch");
1091				if (!lookup->trace) {
1092					showsearch = state;
1093					usesearch = state;
1094				}
1095				break;
1096			default:
1097				goto invalid_option;
1098			}
1099			break;
1100		case 'i':
1101			switch (cmd[2]) {
1102			case 't': /* sit */
1103				FULLCHECK("sit");
1104 sit:
1105				if (state && lookup->edns == -1)
1106					lookup->edns = 0;
1107				lookup->sit = state;
1108				if (value != NULL) {
1109					n = strlcpy(sitvalue, value,
1110						    sizeof(sitvalue));
1111					if (n >= sizeof(sitvalue))
1112						fatal("SIT data too large");
1113					lookup->sitvalue = sitvalue;
1114				} else
1115					lookup->sitvalue = NULL;
1116				break;
1117			default:
1118				goto invalid_option;
1119			}
1120			break;
1121		case 'p': /* split */
1122			FULLCHECK("split");
1123			if (value != NULL && !state)
1124				goto invalid_option;
1125			if (!state) {
1126				splitwidth = 0;
1127				break;
1128			} else if (value == NULL)
1129				break;
1130
1131			splitwidth = strtonum(value, 0, 1023, &errstr);
1132			if (errstr != NULL)
1133				fatal("split is %s: '%s'", errstr, value);
1134			if ((splitwidth % 4) != 0U) {
1135				splitwidth = ((splitwidth + 3) / 4) * 4;
1136				fprintf(stderr, ";; Warning, split must be "
1137						"a multiple of 4; adjusting "
1138						"to %u\n", splitwidth);
1139			}
1140			/*
1141			 * There is an adjustment done in the
1142			 * totext_<rrtype>() functions which causes
1143			 * splitwidth to shrink.  This is okay when we're
1144			 * using the default width but incorrect in this
1145			 * case, so we correct for it
1146			 */
1147			if (splitwidth)
1148				splitwidth += 3;
1149			break;
1150		case 't': /* stats */
1151			FULLCHECK("stats");
1152			lookup->stats = state;
1153			break;
1154		case 'u': /* subnet */
1155			FULLCHECK("subnet");
1156			if (state && value == NULL)
1157				goto need_value;
1158			if (!state) {
1159				if (lookup->ecs_addr != NULL) {
1160					free(lookup->ecs_addr);
1161					lookup->ecs_addr = NULL;
1162				}
1163				break;
1164			}
1165			if (lookup->edns == -1)
1166				lookup->edns = 0;
1167			if (lookup->ecs_addr != NULL) {
1168				free(lookup->ecs_addr);
1169				lookup->ecs_addr = NULL;
1170			}
1171			result = parse_netprefix(&lookup->ecs_addr,
1172			    &lookup->ecs_plen, value);
1173			if (result != ISC_R_SUCCESS)
1174				fatal("Couldn't parse client");
1175			break;
1176		default:
1177			goto invalid_option;
1178		}
1179		break;
1180	case 't':
1181		switch (cmd[1]) {
1182		case 'c': /* tcp */
1183			FULLCHECK("tcp");
1184			if (!is_batchfile) {
1185				lookup->tcp_mode = state;
1186				lookup->tcp_mode_set = 1;
1187			}
1188			break;
1189		case 'i': /* timeout */
1190			FULLCHECK("timeout");
1191			if (value == NULL)
1192				goto need_value;
1193			if (!state)
1194				goto invalid_option;
1195			timeout = strtonum(value, 0, MAXTIMEOUT, &errstr);
1196			if (errstr != NULL)
1197				fatal("timeout is %s: '%s'", errstr, value);
1198			if (timeout == 0)
1199				timeout = 1;
1200			break;
1201		case 'r':
1202			switch (cmd[2]) {
1203			case 'a': /* trace */
1204				FULLCHECK("trace");
1205				lookup->trace = state;
1206				lookup->trace_root = state;
1207				if (state) {
1208					lookup->recurse = 0;
1209					lookup->identify = 1;
1210					lookup->comments = 0;
1211					rrcomments = 0;
1212					lookup->stats = 0;
1213					lookup->section_additional = 0;
1214					lookup->section_authority = 1;
1215					lookup->section_question = 0;
1216					lookup->dnssec = 1;
1217					usesearch = 0;
1218				}
1219				break;
1220			case 'i': /* tries */
1221				FULLCHECK("tries");
1222				if (value == NULL)
1223					goto need_value;
1224				if (!state)
1225					goto invalid_option;
1226				lookup->retries = strtonum(value, 0, MAXTRIES,
1227				    &errstr);
1228				if (errstr != NULL)
1229					fatal("tries is %s: '%s'", errstr,
1230					    value);
1231				if (lookup->retries == 0)
1232					lookup->retries = 1;
1233				break;
1234			default:
1235				goto invalid_option;
1236			}
1237			break;
1238		case 't': /* ttlid */
1239			FULLCHECK("ttlid");
1240			nottl = !state;
1241			break;
1242		default:
1243			goto invalid_option;
1244		}
1245		break;
1246	case 'v':
1247		FULLCHECK("vc");
1248		if (!is_batchfile) {
1249			lookup->tcp_mode = state;
1250			lookup->tcp_mode_set = 1;
1251		}
1252		break;
1253	default:
1254	invalid_option:
1255	need_value:
1256		fprintf(stderr, "Invalid option: +%s\n",
1257			option);
1258		usage();
1259	}
1260	return;
1261}
1262
1263/*%
1264 * #1 returned if value was used
1265 */
1266static const char *single_dash_opts = "46dhinuv";
1267static const char *dash_opts = "46bcdfhikmnptvyx";
1268static int
1269dash_option(char *option, char *next, dig_lookup_t **lookup,
1270	    int *open_type_class, int *need_clone,
1271	    int config_only, int argc, char **argv,
1272	    int *firstarg)
1273{
1274	char opt, *value, *ptr, *ptr2, *ptr3;
1275	isc_result_t result;
1276	int value_from_next;
1277	isc_textregion_t tr;
1278	dns_rdatatype_t rdtype;
1279	dns_rdataclass_t rdclass;
1280	char textname[MXNAME];
1281	char *cmd;
1282	uint32_t num;
1283	const char *errstr;
1284
1285	while (strpbrk(option, single_dash_opts) == &option[0]) {
1286		/*
1287		 * Since the -[46dhinuv] options do not take an argument,
1288		 * account for them (in any number and/or combination)
1289		 * if they appear as the first character(s) of a q-opt.
1290		 */
1291		opt = option[0];
1292		switch (opt) {
1293		case '4':
1294			if (have_ipv4)
1295				have_ipv6 = 0;
1296			else
1297				fatal("can't find IPv4 networking");
1298			break;
1299		case '6':
1300			if (have_ipv6)
1301				have_ipv4 = 0;
1302			else
1303				fatal("can't find IPv6 networking");
1304			break;
1305		case 'd':
1306			ptr = strpbrk(&option[1], dash_opts);
1307			if (ptr != &option[1]) {
1308				cmd = option;
1309				FULLCHECK("debug");
1310				debugging = 1;
1311				return (0);
1312			} else
1313				debugging = 1;
1314			break;
1315		case 'h':
1316			help();
1317			exit(0);
1318			break;
1319		case 'i':
1320			ip6_int = 1;
1321			break;
1322		case 'n':
1323			/* deprecated */
1324			break;
1325		case 'u':
1326			use_usec = 1;
1327			break;
1328		case 'v':
1329			version();
1330			exit(0);
1331			break;
1332		}
1333		if (strlen(option) > 1U)
1334			option = &option[1];
1335		else
1336			return (0);
1337	}
1338	opt = option[0];
1339	if (strlen(option) > 1U) {
1340		value_from_next = 0;
1341		value = &option[1];
1342	} else {
1343		value_from_next = 1;
1344		value = next;
1345	}
1346	if (value == NULL)
1347		goto invalid_option;
1348	switch (opt) {
1349	case 'b': {
1350		struct addrinfo *ai = NULL, hints;
1351		int error;
1352		char *hash;
1353
1354		memset(&hints, 0, sizeof(hints));
1355		hints.ai_flags = AI_NUMERICHOST;
1356		hints.ai_socktype = SOCK_DGRAM;
1357
1358		hash = strchr(value, '#');
1359		if (hash != NULL) {
1360			*hash = '\0';
1361			error = getaddrinfo(value, hash + 1, &hints, &ai);
1362			*hash = '#';
1363		} else
1364			error = getaddrinfo(value, NULL, &hints, &ai);
1365
1366		if (error)
1367			fatal("invalid address %s: %s", value,
1368			    gai_strerror(error));
1369		if (ai == NULL || ai->ai_addrlen > sizeof(bind_address))
1370			fatal("invalid address %s", value);
1371		if (!have_ipv4 && ai->ai_family == AF_INET)
1372			fatal("%s: wrong address family", value);
1373		if (!have_ipv6 && ai->ai_family == AF_INET6)
1374			fatal("%s: wrong address family", value);
1375
1376		memset(&bind_address, 0, sizeof(bind_address));
1377		memcpy(&bind_address, ai->ai_addr, ai->ai_addrlen);
1378
1379		specified_source = 1;
1380		return (value_from_next);
1381	}
1382	case 'c':
1383		if ((*lookup)->rdclassset) {
1384			fprintf(stderr, ";; Warning, extra class option\n");
1385		}
1386		*open_type_class = 0;
1387		tr.base = value;
1388		tr.length = (unsigned int) strlen(value);
1389		result = dns_rdataclass_fromtext(&rdclass,
1390						 (isc_textregion_t *)&tr);
1391		if (result == ISC_R_SUCCESS) {
1392			(*lookup)->rdclass = rdclass;
1393			(*lookup)->rdclassset = 1;
1394		} else
1395			fprintf(stderr, ";; Warning, ignoring "
1396				"invalid class %s\n",
1397				value);
1398		return (value_from_next);
1399	case 'f':
1400		batchname = value;
1401		return (value_from_next);
1402	case 'k':
1403		strlcpy(keyfile, value, sizeof(keyfile));
1404		return (value_from_next);
1405	case 'p':
1406		num = strtonum(value, 0, MAXPORT, &errstr);
1407		if (errstr != NULL)
1408			fatal("port number is %s: '%s'", errstr, value);
1409		port = num;
1410		return (value_from_next);
1411	case 'q':
1412		if (!config_only) {
1413			if (*need_clone)
1414				(*lookup) = clone_lookup(default_lookup,
1415							 1);
1416			*need_clone = 1;
1417			strlcpy((*lookup)->textname, value,
1418				sizeof((*lookup)->textname));
1419			(*lookup)->trace_root = (*lookup)->trace  ||
1420						     (*lookup)->ns_search_only;
1421			(*lookup)->new_search = 1;
1422			if (*firstarg) {
1423				printgreeting(argc, argv, *lookup);
1424				*firstarg = 0;
1425			}
1426			ISC_LIST_APPEND(lookup_list, (*lookup), link);
1427			debug("looking up %s", (*lookup)->textname);
1428		}
1429		return (value_from_next);
1430	case 't':
1431		*open_type_class = 0;
1432		if (strncasecmp(value, "ixfr=", 5) == 0) {
1433			rdtype = dns_rdatatype_ixfr;
1434			result = ISC_R_SUCCESS;
1435		} else {
1436			tr.base = value;
1437			tr.length = (unsigned int) strlen(value);
1438			result = dns_rdatatype_fromtext(&rdtype,
1439						(isc_textregion_t *)&tr);
1440			if (result == ISC_R_SUCCESS &&
1441			    rdtype == dns_rdatatype_ixfr) {
1442				result = DNS_R_UNKNOWN;
1443			}
1444		}
1445		if (result == ISC_R_SUCCESS) {
1446			if ((*lookup)->rdtypeset) {
1447				fprintf(stderr, ";; Warning, "
1448						"extra type option\n");
1449			}
1450			if (rdtype == dns_rdatatype_ixfr) {
1451				uint32_t serial;
1452				(*lookup)->rdtype = dns_rdatatype_ixfr;
1453				(*lookup)->rdtypeset = 1;
1454				serial = strtonum(&value[5], 0, MAXSERIAL,
1455				    &errstr);
1456				if (errstr != NULL)
1457					fatal("serial number is %s: '%s'",
1458					    errstr, &value[5]);
1459				(*lookup)->ixfr_serial = serial;
1460				(*lookup)->section_question = plusquest;
1461				(*lookup)->comments = pluscomm;
1462				if (!(*lookup)->tcp_mode_set)
1463					(*lookup)->tcp_mode = 1;
1464			} else {
1465				(*lookup)->rdtype = rdtype;
1466				if (!config_only)
1467					(*lookup)->rdtypeset = 1;
1468				if (rdtype == dns_rdatatype_axfr) {
1469					(*lookup)->section_question = plusquest;
1470					(*lookup)->comments = pluscomm;
1471				}
1472				(*lookup)->ixfr_serial = 0;
1473			}
1474		} else
1475			fprintf(stderr, ";; Warning, ignoring "
1476				 "invalid type %s\n",
1477				 value);
1478		return (value_from_next);
1479	case 'y':
1480		ptr = next_token(&value, ":");	/* hmac type or name */
1481		if (ptr == NULL) {
1482			usage();
1483		}
1484		ptr2 = next_token(&value, ":");	/* name or secret */
1485		if (ptr2 == NULL)
1486			usage();
1487		ptr3 = next_token(&value, ":"); /* secret or NULL */
1488		if (ptr3 != NULL) {
1489			parse_hmac(ptr);
1490			ptr = ptr2;
1491			ptr2 = ptr3;
1492		} else  {
1493			hmacname = DNS_TSIG_HMACSHA256_NAME;
1494			digestbits = 0;
1495		}
1496		strlcpy(keynametext, ptr, sizeof(keynametext));
1497		strlcpy(keysecret, ptr2, sizeof(keysecret));
1498		return (value_from_next);
1499	case 'x':
1500		if (*need_clone)
1501			*lookup = clone_lookup(default_lookup, 1);
1502		*need_clone = 1;
1503		if (get_reverse(textname, sizeof(textname), value,
1504				ip6_int, 0) == ISC_R_SUCCESS) {
1505			strlcpy((*lookup)->textname, textname,
1506				sizeof((*lookup)->textname));
1507			debug("looking up %s", (*lookup)->textname);
1508			(*lookup)->trace_root = (*lookup)->trace  ||
1509						(*lookup)->ns_search_only;
1510			(*lookup)->ip6_int = ip6_int;
1511			if (!(*lookup)->rdtypeset)
1512				(*lookup)->rdtype = dns_rdatatype_ptr;
1513			if (!(*lookup)->rdclassset)
1514				(*lookup)->rdclass = dns_rdataclass_in;
1515			(*lookup)->new_search = 1;
1516			if (*firstarg) {
1517				printgreeting(argc, argv, *lookup);
1518				*firstarg = 0;
1519			}
1520			ISC_LIST_APPEND(lookup_list, *lookup, link);
1521		} else {
1522			fprintf(stderr, "Invalid IP address %s\n", value);
1523			exit(1);
1524		}
1525		return (value_from_next);
1526	invalid_option:
1527	default:
1528		fprintf(stderr, "Invalid option: -%s\n", option);
1529		usage();
1530	}
1531	/* NOTREACHED */
1532	return (0);
1533}
1534
1535/*%
1536 * Because we may be trying to do memory allocation recording, we're going
1537 * to need to parse the arguments for the -m *before* we start the main
1538 * argument parsing routine.
1539 *
1540 * I'd prefer not to have to do this, but I am not quite sure how else to
1541 * fix the problem.  Argument parsing in dig involves memory allocation
1542 * by its nature, so it can't be done in the main argument parser.
1543 */
1544static void
1545preparse_args(int argc, char **argv) {
1546	int rc;
1547	char **rv;
1548	char *option;
1549
1550	rc = argc;
1551	rv = argv;
1552	for (rc--, rv++; rc > 0; rc--, rv++) {
1553		if (rv[0][0] != '-')
1554			continue;
1555		option = &rv[0][1];
1556		while (strpbrk(option, single_dash_opts) == &option[0]) {
1557			switch (option[0]) {
1558			case '4':
1559				if (ipv6only)
1560					fatal("only one of -4 and -6 allowed");
1561				ipv4only = 1;
1562				break;
1563			case '6':
1564				if (ipv4only)
1565					fatal("only one of -4 and -6 allowed");
1566				ipv6only = 1;
1567				break;
1568			}
1569			option = &option[1];
1570		}
1571	}
1572}
1573
1574static void
1575parse_args(int is_batchfile, int config_only,
1576	   int argc, char **argv)
1577{
1578	isc_result_t result;
1579	isc_textregion_t tr;
1580	int firstarg = 1;
1581	dig_lookup_t *lookup = NULL;
1582	dns_rdatatype_t rdtype;
1583	dns_rdataclass_t rdclass;
1584	int open_type_class = 1;
1585	char batchline[MXNAME];
1586	int bargc;
1587	char *bargv[64];
1588	int rc;
1589	char **rv;
1590	char *input;
1591	int i;
1592	int need_clone = 1;
1593	const char *errstr;
1594
1595	/*
1596	 * The semantics for parsing the args is a bit complex; if
1597	 * we don't have a host yet, make the arg apply globally,
1598	 * otherwise make it apply to the latest host.  This is
1599	 * a bit different than the previous versions, but should
1600	 * form a consistent user interface.
1601	 *
1602	 * First, create a "default lookup" which won't actually be used
1603	 * anywhere, except for cloning into new lookups
1604	 */
1605
1606	debug("parse_args()");
1607	if (!is_batchfile) {
1608		debug("making new lookup");
1609		default_lookup = make_empty_lookup();
1610		default_lookup->adflag = 1;
1611		default_lookup->edns = 0;
1612	}
1613
1614	if (is_batchfile && !config_only) {
1615		/* Processing '-f batchfile'. */
1616		lookup = clone_lookup(default_lookup, 1);
1617		need_clone = 0;
1618	} else
1619		lookup = default_lookup;
1620
1621	rc = argc;
1622	rv = argv;
1623	for (rc--, rv++; rc > 0; rc--, rv++) {
1624		debug("main parsing %s", rv[0]);
1625		if (strncmp(rv[0], "%", 1) == 0)
1626			break;
1627		if (rv[0][0] == '@') {
1628
1629			if (is_batchfile && !config_only) {
1630				addresscount = getaddresses(lookup, &rv[0][1],
1631							     &result);
1632				if (result != ISC_R_SUCCESS) {
1633					fprintf(stderr, "couldn't get address "
1634						"for '%s': %s: skipping "
1635						"lookup\n", &rv[0][1],
1636						isc_result_totext(result));
1637					if (ISC_LINK_LINKED(lookup, link))
1638						ISC_LIST_DEQUEUE(lookup_list,
1639								 lookup, link);
1640					destroy_lookup(lookup);
1641					return;
1642				}
1643			} else
1644				addresscount = getaddresses(lookup, &rv[0][1],
1645							    NULL);
1646		} else if (rv[0][0] == '+') {
1647			plus_option(&rv[0][1], is_batchfile,
1648				    lookup);
1649		} else if (rv[0][0] == '-') {
1650			if (rc <= 1) {
1651				if (dash_option(&rv[0][1], NULL,
1652						&lookup, &open_type_class,
1653						&need_clone, config_only,
1654						argc, argv, &firstarg)) {
1655					rc--;
1656					rv++;
1657				}
1658			} else {
1659				if (dash_option(&rv[0][1], rv[1],
1660						&lookup, &open_type_class,
1661						&need_clone, config_only,
1662						argc, argv, &firstarg)) {
1663					rc--;
1664					rv++;
1665				}
1666			}
1667		} else {
1668			/*
1669			 * Anything which isn't an option
1670			 */
1671			if (open_type_class) {
1672				if (strncasecmp(rv[0], "ixfr=", 5) == 0) {
1673					rdtype = dns_rdatatype_ixfr;
1674					result = ISC_R_SUCCESS;
1675				} else {
1676					tr.base = rv[0];
1677					tr.length =
1678						(unsigned int) strlen(rv[0]);
1679					result = dns_rdatatype_fromtext(&rdtype,
1680						(isc_textregion_t *)&tr);
1681					if (result == ISC_R_SUCCESS &&
1682					    rdtype == dns_rdatatype_ixfr) {
1683						fprintf(stderr, ";; Warning, "
1684							"ixfr requires a "
1685							"serial number\n");
1686						continue;
1687					}
1688				}
1689				if (result == ISC_R_SUCCESS) {
1690					if (lookup->rdtypeset) {
1691						fprintf(stderr, ";; Warning, "
1692							"extra type option\n");
1693					}
1694					if (rdtype == dns_rdatatype_ixfr) {
1695						uint32_t serial;
1696						lookup->rdtype =
1697							dns_rdatatype_ixfr;
1698						lookup->rdtypeset = 1;
1699						serial = strtonum(&rv[0][5], 0,
1700						    MAXSERIAL, &errstr);
1701						if (errstr != NULL)
1702							fatal("serial number "
1703							    "is %s: '%s'",
1704							    errstr, &rv[0][5]);
1705						lookup->ixfr_serial = serial;
1706						lookup->section_question =
1707							plusquest;
1708						lookup->comments = pluscomm;
1709						if (!lookup->tcp_mode_set)
1710							lookup->tcp_mode = 1;
1711					} else {
1712						lookup->rdtype = rdtype;
1713						lookup->rdtypeset = 1;
1714						if (rdtype ==
1715						    dns_rdatatype_axfr) {
1716						    lookup->section_question =
1717								plusquest;
1718						    lookup->comments = pluscomm;
1719						}
1720						lookup->ixfr_serial = 0;
1721					}
1722					continue;
1723				}
1724				result = dns_rdataclass_fromtext(&rdclass,
1725						     (isc_textregion_t *)&tr);
1726				if (result == ISC_R_SUCCESS) {
1727					if (lookup->rdclassset) {
1728						fprintf(stderr, ";; Warning, "
1729							"extra class option\n");
1730					}
1731					lookup->rdclass = rdclass;
1732					lookup->rdclassset = 1;
1733					continue;
1734				}
1735			}
1736
1737			if (!config_only) {
1738				if (need_clone)
1739					lookup = clone_lookup(default_lookup,
1740								      1);
1741				need_clone = 1;
1742				strlcpy(lookup->textname, rv[0],
1743					sizeof(lookup->textname));
1744				lookup->trace_root = lookup->trace  ||
1745						     lookup->ns_search_only;
1746				lookup->new_search = 1;
1747				if (firstarg) {
1748					printgreeting(argc, argv, lookup);
1749					firstarg = 0;
1750				}
1751				ISC_LIST_APPEND(lookup_list, lookup, link);
1752				debug("looking up %s", lookup->textname);
1753			}
1754			/* XXX Error message */
1755		}
1756	}
1757
1758	/*
1759	 * If we have a batchfile, seed the lookup list with the
1760	 * first entry, then trust the callback in dighost_shutdown
1761	 * to get the rest
1762	 */
1763	if ((batchname != NULL) && !(is_batchfile)) {
1764		if (strcmp(batchname, "-") == 0)
1765			batchfp = stdin;
1766		else
1767			batchfp = fopen(batchname, "r");
1768		if (batchfp == NULL) {
1769			perror(batchname);
1770			if (exitcode < 8)
1771				exitcode = 8;
1772			fatal("couldn't open specified batch file");
1773		}
1774		/* XXX Remove code dup from shutdown code */
1775	next_line:
1776		if (fgets(batchline, sizeof(batchline), batchfp) != NULL) {
1777			bargc = 1;
1778			debug("batch line %s", batchline);
1779			if (batchline[0] == '\r' || batchline[0] == '\n'
1780			    || batchline[0] == '#' || batchline[0] == ';')
1781				goto next_line;
1782			input = batchline;
1783			bargv[bargc] = next_token(&input, " \t\r\n");
1784			while ((bargc < 14) && (bargv[bargc] != NULL)) {
1785				bargc++;
1786				bargv[bargc] = next_token(&input, " \t\r\n");
1787			}
1788
1789			bargv[0] = argv[0];
1790			argv0 = argv[0];
1791
1792			for(i = 0; i < bargc; i++)
1793				debug("batch argv %d: %s", i, bargv[i]);
1794			parse_args(1, 0, bargc, (char **)bargv);
1795			return;
1796		}
1797		return;
1798	}
1799	/*
1800	 * If no lookup specified, search for root
1801	 */
1802	if ((lookup_list.head == NULL) && !config_only) {
1803		if (need_clone)
1804			lookup = clone_lookup(default_lookup, 1);
1805		need_clone = 1;
1806		lookup->trace_root = lookup->trace || lookup->ns_search_only;
1807		lookup->new_search = 1;
1808		strlcpy(lookup->textname, ".", sizeof(lookup->textname));
1809		lookup->rdtype = dns_rdatatype_ns;
1810		lookup->rdtypeset = 1;
1811		if (firstarg) {
1812			printgreeting(argc, argv, lookup);
1813			firstarg = 0;
1814		}
1815		ISC_LIST_APPEND(lookup_list, lookup, link);
1816	}
1817	if (!need_clone)
1818		destroy_lookup(lookup);
1819}
1820
1821/*
1822 * Callback from dighost.c to allow program-specific shutdown code.
1823 * Here, we're possibly reading from a batch file, then shutting down
1824 * for real if there's nothing in the batch file to read.
1825 */
1826static void
1827query_finished(void) {
1828	char batchline[MXNAME];
1829	int bargc;
1830	char *bargv[16];
1831	char *input;
1832	int i;
1833
1834	if (batchname == NULL) {
1835		isc_app_shutdown();
1836		return;
1837	}
1838
1839	fflush(stdout);
1840	if (feof(batchfp)) {
1841		batchname = NULL;
1842		isc_app_shutdown();
1843		if (batchfp != stdin)
1844			fclose(batchfp);
1845		return;
1846	}
1847
1848	if (fgets(batchline, sizeof(batchline), batchfp) != NULL) {
1849		debug("batch line %s", batchline);
1850		bargc = 1;
1851		input = batchline;
1852		bargv[bargc] = next_token(&input, " \t\r\n");
1853		while ((bargc < 14) && (bargv[bargc] != NULL)) {
1854			bargc++;
1855			bargv[bargc] = next_token(&input, " \t\r\n");
1856		}
1857
1858		bargv[0] = argv0;
1859
1860		for(i = 0; i < bargc; i++)
1861			debug("batch argv %d: %s", i, bargv[i]);
1862		parse_args(1, 0, bargc, (char **)bargv);
1863		start_lookup();
1864	} else {
1865		batchname = NULL;
1866		if (batchfp != stdin)
1867			fclose(batchfp);
1868		isc_app_shutdown();
1869		return;
1870	}
1871}
1872
1873void dig_setup(int argc, char **argv)
1874{
1875	isc_result_t result;
1876
1877	ISC_LIST_INIT(lookup_list);
1878	ISC_LIST_INIT(server_list);
1879	ISC_LIST_INIT(root_hints_server_list);
1880	ISC_LIST_INIT(search_list);
1881
1882	if (pledge("stdio rpath inet dns", NULL) == -1) {
1883		perror("pledge");
1884		exit(1);
1885	}
1886
1887	debug("dig_setup()");
1888
1889	/* setup dighost callbacks */
1890	dighost_printmessage = printmessage;
1891	dighost_received = received;
1892	dighost_trying = trying;
1893	dighost_shutdown = query_finished;
1894
1895	progname = argv[0];
1896	preparse_args(argc, argv);
1897
1898	result = isc_app_start();
1899	check_result(result, "isc_app_start");
1900
1901	setup_libs();
1902	setup_system(ipv4only, ipv6only);
1903}
1904
1905void dig_query_setup(int is_batchfile, int config_only,
1906		int argc, char **argv)
1907{
1908	debug("dig_query_setup");
1909
1910	parse_args(is_batchfile, config_only, argc, argv);
1911	if (keyfile[0] != 0)
1912		setup_file_key();
1913	else if (keysecret[0] != 0)
1914		setup_text_key();
1915
1916	if (pledge("stdio inet dns", NULL) == -1) {
1917		perror("pledge");
1918		exit(1);
1919	}
1920
1921	if (domainopt[0] != '\0') {
1922		set_search_domain(domainopt);
1923		usesearch = 1;
1924	}
1925}
1926
1927void dig_startup(void) {
1928	isc_result_t result;
1929
1930	debug("dig_startup()");
1931
1932	result = isc_app_onrun(global_task, onrun_callback, NULL);
1933	check_result(result, "isc_app_onrun");
1934	isc_app_run();
1935}
1936
1937void
1938dig_shutdown(void) {
1939	destroy_lookup(default_lookup);
1940	if (batchname != NULL) {
1941		if (batchfp != stdin)
1942			fclose(batchfp);
1943		batchname = NULL;
1944	}
1945
1946	cancel_all();
1947	destroy_libs();
1948}
1949
1950/*% Main processing routine for dig */
1951int
1952main(int argc, char **argv) {
1953	extern char *__progname;
1954
1955	if (strcmp("host", __progname) == 0)
1956		return host_main(argc, argv);
1957	if (strcmp("nslookup", __progname) == 0)
1958		return nslookup_main(argc, argv);
1959
1960	dig_setup(argc, argv);
1961	dig_query_setup(0, 0, argc, argv);
1962	dig_startup();
1963	dig_shutdown();
1964
1965	return (exitcode);
1966}
1967