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