Annotation of src/usr.bin/dig/dig.c, Revision 1.8
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.8 ! florian 17: /* $Id: dig.c,v 1.7 2020/02/13 16:55:20 florian 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: /*%
630: * We're not using isc_commandline_parse() here since the command line
631: * syntax of dig is quite a bit different from that which can be described
632: * by that routine.
633: * XXX doc options
634: */
635:
636: static void
637: plus_option(const char *option, isc_boolean_t is_batchfile,
638: dig_lookup_t *lookup)
639: {
640: isc_result_t result;
641: char option_store[256];
642: char *cmd, *value, *ptr, *code;
643: uint32_t num;
644: isc_boolean_t state = ISC_TRUE;
645: size_t n;
646:
647: strlcpy(option_store, option, sizeof(option_store));
648: ptr = option_store;
649: cmd = next_token(&ptr, "=");
650: if (cmd == NULL) {
651: printf(";; Invalid option %s\n", option_store);
652: return;
653: }
654: value = ptr;
655: if (strncasecmp(cmd, "no", 2)==0) {
656: cmd += 2;
657: state = ISC_FALSE;
658: }
659:
660: #define FULLCHECK(A) \
661: do { \
662: size_t _l = strlen(cmd); \
663: if (_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) \
664: goto invalid_option; \
665: } while (0)
666: #define FULLCHECK2(A, B) \
667: do { \
668: size_t _l = strlen(cmd); \
669: if ((_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) && \
670: (_l >= sizeof(B) || strncasecmp(cmd, B, _l) != 0)) \
671: goto invalid_option; \
672: } while (0)
673:
674: switch (cmd[0]) {
675: case 'a':
676: switch (cmd[1]) {
677: case 'a': /* aaonly / aaflag */
678: FULLCHECK2("aaonly", "aaflag");
679: lookup->aaonly = state;
680: break;
681: case 'd':
682: switch (cmd[2]) {
683: case 'd': /* additional */
684: FULLCHECK("additional");
685: lookup->section_additional = state;
686: break;
687: case 'f': /* adflag */
688: case '\0': /* +ad is a synonym for +adflag */
689: FULLCHECK("adflag");
690: lookup->adflag = state;
691: break;
692: default:
693: goto invalid_option;
694: }
695: break;
696: case 'l': /* all */
697: FULLCHECK("all");
698: lookup->section_question = state;
699: lookup->section_authority = state;
700: lookup->section_answer = state;
701: lookup->section_additional = state;
702: lookup->comments = state;
703: lookup->stats = state;
704: printcmd = state;
705: break;
706: case 'n': /* answer */
707: FULLCHECK("answer");
708: lookup->section_answer = state;
709: break;
710: case 'u': /* authority */
711: FULLCHECK("authority");
712: lookup->section_authority = state;
713: break;
714: default:
715: goto invalid_option;
716: }
717: break;
718: case 'b':
719: switch (cmd[1]) {
720: case 'e':/* besteffort */
721: FULLCHECK("besteffort");
722: lookup->besteffort = state;
723: break;
724: case 'u':/* bufsize */
725: FULLCHECK("bufsize");
726: if (value == NULL)
727: goto need_value;
728: if (!state)
729: goto invalid_option;
730: result = parse_uint(&num, value, COMMSIZE,
731: "buffer size");
732: if (result != ISC_R_SUCCESS)
733: fatal("Couldn't parse buffer size");
734: lookup->udpsize = num;
735: break;
736: default:
737: goto invalid_option;
738: }
739: break;
740: case 'c':
741: switch (cmd[1]) {
742: case 'd':/* cdflag */
743: switch (cmd[2]) {
744: case 'f': /* cdflag */
745: case '\0': /* +cd is a synonym for +cdflag */
746: FULLCHECK("cdflag");
747: lookup->cdflag = state;
748: break;
749: default:
750: goto invalid_option;
751: }
752: break;
753: case 'l': /* class */
754: /* keep +cl for backwards compatibility */
755: FULLCHECK2("cl", "class");
756: noclass = ISC_TF(!state);
757: break;
758: case 'm': /* cmd */
759: FULLCHECK("cmd");
760: printcmd = state;
761: break;
762: case 'o': /* comments */
763: switch (cmd[2]) {
764: case 'o':
765: FULLCHECK("cookie");
766: goto sit;
767: case 'm':
768: FULLCHECK("comments");
769: lookup->comments = state;
770: if (lookup == default_lookup)
771: pluscomm = state;
772: break;
773: default:
774: goto invalid_option;
775: }
776: break;
777: case 'r':
778: FULLCHECK("crypto");
779: nocrypto = ISC_TF(!state);
780: break;
781: default:
782: goto invalid_option;
783: }
784: break;
785: case 'd':
786: switch (cmd[1]) {
787: case 'e': /* defname */
788: FULLCHECK("defname");
789: if (!lookup->trace) {
790: usesearch = state;
791: }
792: break;
793: case 'n': /* dnssec */
794: FULLCHECK("dnssec");
795: if (state && lookup->edns == -1)
796: lookup->edns = 0;
797: lookup->dnssec = state;
798: break;
799: case 'o': /* domain */
800: FULLCHECK("domain");
801: if (value == NULL)
802: goto need_value;
803: if (!state)
804: goto invalid_option;
805: strlcpy(domainopt, value, sizeof(domainopt));
806: break;
807: default:
808: goto invalid_option;
809: }
810: break;
811: case 'e':
812: switch (cmd[1]) {
813: case 'd':
814: switch(cmd[2]) {
815: case 'n':
816: switch (cmd[3]) {
817: case 's':
818: switch (cmd[4]) {
819: case 0:
820: FULLCHECK("edns");
821: if (!state) {
822: lookup->edns = -1;
823: break;
824: }
825: if (value == NULL) {
826: lookup->edns = 0;
827: break;
828: }
829: result = parse_uint(&num,
830: value,
831: 255,
832: "edns");
833: if (result != ISC_R_SUCCESS)
834: fatal("Couldn't parse "
835: "edns");
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: result = parse_xint(&num,
849: value,
850: 0xffff,
851: "ednsflags");
852: if (result != ISC_R_SUCCESS)
853: fatal("Couldn't parse "
854: "ednsflags");
855: lookup->ednsflags = num;
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: result = parse_uint(&num, value, MAXNDOTS, "ndots");
939: if (result != ISC_R_SUCCESS)
940: fatal("Couldn't parse ndots");
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 = ISC_TRUE;
956: lookup->recurse = ISC_TRUE;
957: lookup->identify = ISC_TRUE;
958: lookup->stats = ISC_FALSE;
959: lookup->comments = ISC_FALSE;
960: lookup->section_additional = ISC_FALSE;
961: lookup->section_authority = ISC_FALSE;
962: lookup->section_question = ISC_FALSE;
963: lookup->rdtype = dns_rdatatype_ns;
964: lookup->rdtypeset = ISC_TRUE;
965: short_form = ISC_TRUE;
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: result = parse_uint(&num, value, 15, "opcode");
1002: if (result != ISC_R_SUCCESS)
1003: fatal("Couldn't parse opcode");
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: result = parse_uint(&lookup->retries, value,
1045: MAXTRIES - 1, "retries");
1046: if (result != ISC_R_SUCCESS)
1047: fatal("Couldn't parse retries");
1048: lookup->retries++;
1049: break;
1050: default:
1051: goto invalid_option;
1052: }
1053: break;
1054: case 'r': /* rrcomments */
1055: FULLCHECK("rrcomments");
1056: rrcomments = state ? 1 : -1;
1057: break;
1058: default:
1059: goto invalid_option;
1060: }
1061: break;
1062: case 's':
1063: switch (cmd[1]) {
1064: case 'e': /* search */
1065: FULLCHECK("search");
1066: if (!lookup->trace) {
1067: usesearch = state;
1068: }
1069: break;
1070: case 'h':
1071: if (cmd[2] != 'o')
1072: goto invalid_option;
1073: switch (cmd[3]) {
1074: case 'r': /* short */
1075: FULLCHECK("short");
1076: short_form = state;
1077: if (state) {
1078: printcmd = ISC_FALSE;
1079: lookup->section_additional = ISC_FALSE;
1080: lookup->section_answer = ISC_TRUE;
1081: lookup->section_authority = ISC_FALSE;
1082: lookup->section_question = ISC_FALSE;
1083: lookup->comments = ISC_FALSE;
1084: lookup->stats = ISC_FALSE;
1085: rrcomments = -1;
1086: }
1087: break;
1088: case 'w': /* showsearch */
1089: FULLCHECK("showsearch");
1090: if (!lookup->trace) {
1091: showsearch = state;
1092: usesearch = state;
1093: }
1094: break;
1095: default:
1096: goto invalid_option;
1097: }
1098: break;
1099: case 'i':
1100: switch (cmd[2]) {
1101: case 't': /* sit */
1102: FULLCHECK("sit");
1103: sit:
1104: if (state && lookup->edns == -1)
1105: lookup->edns = 0;
1106: lookup->sit = state;
1107: if (value != NULL) {
1108: n = strlcpy(sitvalue, value,
1109: sizeof(sitvalue));
1110: if (n >= sizeof(sitvalue))
1111: fatal("SIT data too large");
1112: lookup->sitvalue = sitvalue;
1113: } else
1114: lookup->sitvalue = NULL;
1115: break;
1116: default:
1117: goto invalid_option;
1118: }
1119: break;
1120: case 'p': /* split */
1121: FULLCHECK("split");
1122: if (value != NULL && !state)
1123: goto invalid_option;
1124: if (!state) {
1125: splitwidth = 0;
1126: break;
1127: } else if (value == NULL)
1128: break;
1129:
1130: result = parse_uint(&splitwidth, value,
1131: 1023, "split");
1132: if ((splitwidth % 4) != 0U) {
1133: splitwidth = ((splitwidth + 3) / 4) * 4;
1134: fprintf(stderr, ";; Warning, split must be "
1135: "a multiple of 4; adjusting "
1136: "to %u\n", splitwidth);
1137: }
1138: /*
1139: * There is an adjustment done in the
1140: * totext_<rrtype>() functions which causes
1141: * splitwidth to shrink. This is okay when we're
1142: * using the default width but incorrect in this
1143: * case, so we correct for it
1144: */
1145: if (splitwidth)
1146: splitwidth += 3;
1147: if (result != ISC_R_SUCCESS)
1148: fatal("Couldn't parse split");
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, value);
1172: if (result != ISC_R_SUCCESS)
1173: fatal("Couldn't parse client");
1174: break;
1175: default:
1176: goto invalid_option;
1177: }
1178: break;
1179: case 't':
1180: switch (cmd[1]) {
1181: case 'c': /* tcp */
1182: FULLCHECK("tcp");
1183: if (!is_batchfile) {
1184: lookup->tcp_mode = state;
1185: lookup->tcp_mode_set = ISC_TRUE;
1186: }
1187: break;
1188: case 'i': /* timeout */
1189: FULLCHECK("timeout");
1190: if (value == NULL)
1191: goto need_value;
1192: if (!state)
1193: goto invalid_option;
1194: result = parse_uint(&timeout, value, MAXTIMEOUT,
1195: "timeout");
1196: if (result != ISC_R_SUCCESS)
1197: fatal("Couldn't parse timeout");
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 = ISC_FALSE;
1209: lookup->identify = ISC_TRUE;
1210: lookup->comments = ISC_FALSE;
1211: rrcomments = 0;
1212: lookup->stats = ISC_FALSE;
1213: lookup->section_additional = ISC_FALSE;
1214: lookup->section_authority = ISC_TRUE;
1215: lookup->section_question = ISC_FALSE;
1216: lookup->dnssec = ISC_TRUE;
1217: usesearch = ISC_FALSE;
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: result = parse_uint(&lookup->retries, value,
1227: MAXTRIES, "tries");
1228: if (result != ISC_R_SUCCESS)
1229: fatal("Couldn't parse tries");
1230: if (lookup->retries == 0)
1231: lookup->retries = 1;
1232: break;
1233: default:
1234: goto invalid_option;
1235: }
1236: break;
1237: case 't': /* ttlid */
1238: FULLCHECK("ttlid");
1239: nottl = ISC_TF(!state);
1240: break;
1241: default:
1242: goto invalid_option;
1243: }
1244: break;
1245: case 'v':
1246: FULLCHECK("vc");
1247: if (!is_batchfile) {
1248: lookup->tcp_mode = state;
1249: lookup->tcp_mode_set = ISC_TRUE;
1250: }
1251: break;
1252: default:
1253: invalid_option:
1254: need_value:
1255: fprintf(stderr, "Invalid option: +%s\n",
1256: option);
1257: usage();
1258: }
1259: return;
1260: }
1261:
1262: /*%
1263: * #ISC_TRUE returned if value was used
1264: */
1265: static const char *single_dash_opts = "46dhinuv";
1266: static const char *dash_opts = "46bcdfhikmnptvyx";
1267: static isc_boolean_t
1268: dash_option(char *option, char *next, dig_lookup_t **lookup,
1269: isc_boolean_t *open_type_class, isc_boolean_t *need_clone,
1270: isc_boolean_t config_only, int argc, char **argv,
1271: isc_boolean_t *firstarg)
1272: {
1273: char opt, *value, *ptr, *ptr2, *ptr3;
1274: isc_result_t result;
1275: isc_boolean_t value_from_next;
1276: isc_textregion_t tr;
1277: dns_rdatatype_t rdtype;
1278: dns_rdataclass_t rdclass;
1279: char textname[MXNAME];
1280: struct in_addr in4;
1281: struct in6_addr in6;
1282: in_port_t srcport;
1283: char *hash, *cmd;
1284: uint32_t num;
1285:
1286: while (strpbrk(option, single_dash_opts) == &option[0]) {
1287: /*
1288: * Since the -[46dhinuv] options do not take an argument,
1289: * account for them (in any number and/or combination)
1290: * if they appear as the first character(s) of a q-opt.
1291: */
1292: opt = option[0];
1293: switch (opt) {
1294: case '4':
1295: if (have_ipv4) {
1296: isc_net_disableipv6();
1297: have_ipv6 = ISC_FALSE;
1298: } else {
1299: fatal("can't find IPv4 networking");
1300: /* NOTREACHED */
1301: return (ISC_FALSE);
1302: }
1303: break;
1304: case '6':
1305: if (have_ipv6) {
1306: isc_net_disableipv4();
1307: have_ipv4 = ISC_FALSE;
1308: } else {
1309: fatal("can't find IPv6 networking");
1310: /* NOTREACHED */
1311: return (ISC_FALSE);
1312: }
1313: break;
1314: case 'd':
1315: ptr = strpbrk(&option[1], dash_opts);
1316: if (ptr != &option[1]) {
1317: cmd = option;
1318: FULLCHECK("debug");
1319: debugging = ISC_TRUE;
1320: return (ISC_FALSE);
1321: } else
1322: debugging = ISC_TRUE;
1323: break;
1324: case 'h':
1325: help();
1326: exit(0);
1327: break;
1328: case 'i':
1329: ip6_int = ISC_TRUE;
1330: break;
1331: case 'n':
1332: /* deprecated */
1333: break;
1334: case 'u':
1335: use_usec = ISC_TRUE;
1336: break;
1337: case 'v':
1338: version();
1339: exit(0);
1340: break;
1341: }
1342: if (strlen(option) > 1U)
1343: option = &option[1];
1344: else
1345: return (ISC_FALSE);
1346: }
1347: opt = option[0];
1348: if (strlen(option) > 1U) {
1349: value_from_next = ISC_FALSE;
1350: value = &option[1];
1351: } else {
1352: value_from_next = ISC_TRUE;
1353: value = next;
1354: }
1355: if (value == NULL)
1356: goto invalid_option;
1357: switch (opt) {
1358: case 'b':
1359: hash = strchr(value, '#');
1360: if (hash != NULL) {
1361: result = parse_uint(&num, hash + 1, MAXPORT,
1362: "port number");
1363: if (result != ISC_R_SUCCESS)
1364: fatal("Couldn't parse port number");
1365: srcport = num;
1366: *hash = '\0';
1367: } else
1368: srcport = 0;
1369: if (have_ipv6 && inet_pton(AF_INET6, value, &in6) == 1) {
1370: isc_sockaddr_fromin6(&bind_address, &in6, srcport);
1371: isc_net_disableipv4();
1372: } else if (have_ipv4 && inet_pton(AF_INET, value, &in4) == 1) {
1373: isc_sockaddr_fromin(&bind_address, &in4, srcport);
1374: isc_net_disableipv6();
1375: } else {
1376: if (hash != NULL)
1377: *hash = '#';
1378: fatal("invalid address %s", value);
1379: }
1380: if (hash != NULL)
1381: *hash = '#';
1382: specified_source = ISC_TRUE;
1383: return (value_from_next);
1384: case 'c':
1385: if ((*lookup)->rdclassset) {
1386: fprintf(stderr, ";; Warning, extra class option\n");
1387: }
1388: *open_type_class = ISC_FALSE;
1389: tr.base = value;
1390: tr.length = (unsigned int) strlen(value);
1391: result = dns_rdataclass_fromtext(&rdclass,
1392: (isc_textregion_t *)&tr);
1393: if (result == ISC_R_SUCCESS) {
1394: (*lookup)->rdclass = rdclass;
1395: (*lookup)->rdclassset = ISC_TRUE;
1396: } else
1397: fprintf(stderr, ";; Warning, ignoring "
1398: "invalid class %s\n",
1399: value);
1400: return (value_from_next);
1401: case 'f':
1402: batchname = value;
1403: return (value_from_next);
1404: case 'k':
1405: strlcpy(keyfile, value, sizeof(keyfile));
1406: return (value_from_next);
1407: case 'p':
1408: result = parse_uint(&num, value, MAXPORT, "port number");
1409: if (result != ISC_R_SUCCESS)
1410: fatal("Couldn't parse port number");
1411: port = num;
1412: return (value_from_next);
1413: case 'q':
1414: if (!config_only) {
1415: if (*need_clone)
1416: (*lookup) = clone_lookup(default_lookup,
1417: ISC_TRUE);
1418: *need_clone = ISC_TRUE;
1419: strlcpy((*lookup)->textname, value,
1420: sizeof((*lookup)->textname));
1421: (*lookup)->trace_root = ISC_TF((*lookup)->trace ||
1422: (*lookup)->ns_search_only);
1423: (*lookup)->new_search = ISC_TRUE;
1424: if (*firstarg) {
1425: printgreeting(argc, argv, *lookup);
1426: *firstarg = ISC_FALSE;
1427: }
1428: ISC_LIST_APPEND(lookup_list, (*lookup), link);
1429: debug("looking up %s", (*lookup)->textname);
1430: }
1431: return (value_from_next);
1432: case 't':
1433: *open_type_class = ISC_FALSE;
1434: if (strncasecmp(value, "ixfr=", 5) == 0) {
1435: rdtype = dns_rdatatype_ixfr;
1436: result = ISC_R_SUCCESS;
1437: } else {
1438: tr.base = value;
1439: tr.length = (unsigned int) strlen(value);
1440: result = dns_rdatatype_fromtext(&rdtype,
1441: (isc_textregion_t *)&tr);
1442: if (result == ISC_R_SUCCESS &&
1443: rdtype == dns_rdatatype_ixfr) {
1444: result = DNS_R_UNKNOWN;
1445: }
1446: }
1447: if (result == ISC_R_SUCCESS) {
1448: if ((*lookup)->rdtypeset) {
1449: fprintf(stderr, ";; Warning, "
1450: "extra type option\n");
1451: }
1452: if (rdtype == dns_rdatatype_ixfr) {
1453: uint32_t serial;
1454: (*lookup)->rdtype = dns_rdatatype_ixfr;
1455: (*lookup)->rdtypeset = ISC_TRUE;
1456: result = parse_uint(&serial, &value[5],
1457: MAXSERIAL, "serial number");
1458: if (result != ISC_R_SUCCESS)
1459: fatal("Couldn't parse serial number");
1460: (*lookup)->ixfr_serial = serial;
1461: (*lookup)->section_question = plusquest;
1462: (*lookup)->comments = pluscomm;
1463: if (!(*lookup)->tcp_mode_set)
1464: (*lookup)->tcp_mode = ISC_TRUE;
1465: } else {
1466: (*lookup)->rdtype = rdtype;
1467: if (!config_only)
1468: (*lookup)->rdtypeset = ISC_TRUE;
1469: if (rdtype == dns_rdatatype_axfr) {
1470: (*lookup)->section_question = plusquest;
1471: (*lookup)->comments = pluscomm;
1472: }
1473: (*lookup)->ixfr_serial = ISC_FALSE;
1474: }
1475: } else
1476: fprintf(stderr, ";; Warning, ignoring "
1477: "invalid type %s\n",
1478: value);
1479: return (value_from_next);
1480: case 'y':
1481: ptr = next_token(&value, ":"); /* hmac type or name */
1482: if (ptr == NULL) {
1483: usage();
1484: }
1485: ptr2 = next_token(&value, ":"); /* name or secret */
1486: if (ptr2 == NULL)
1487: usage();
1488: ptr3 = next_token(&value, ":"); /* secret or NULL */
1489: if (ptr3 != NULL) {
1490: parse_hmac(ptr);
1491: ptr = ptr2;
1492: ptr2 = ptr3;
1493: } else {
1494: hmacname = DNS_TSIG_HMACSHA256_NAME;
1495: digestbits = 0;
1496: }
1497: strlcpy(keynametext, ptr, sizeof(keynametext));
1498: strlcpy(keysecret, ptr2, sizeof(keysecret));
1499: return (value_from_next);
1500: case 'x':
1501: if (*need_clone)
1502: *lookup = clone_lookup(default_lookup, ISC_TRUE);
1503: *need_clone = ISC_TRUE;
1504: if (get_reverse(textname, sizeof(textname), value,
1505: ip6_int, ISC_FALSE) == ISC_R_SUCCESS) {
1506: strlcpy((*lookup)->textname, textname,
1507: sizeof((*lookup)->textname));
1508: debug("looking up %s", (*lookup)->textname);
1509: (*lookup)->trace_root = ISC_TF((*lookup)->trace ||
1510: (*lookup)->ns_search_only);
1511: (*lookup)->ip6_int = ip6_int;
1512: if (!(*lookup)->rdtypeset)
1513: (*lookup)->rdtype = dns_rdatatype_ptr;
1514: if (!(*lookup)->rdclassset)
1515: (*lookup)->rdclass = dns_rdataclass_in;
1516: (*lookup)->new_search = ISC_TRUE;
1517: if (*firstarg) {
1518: printgreeting(argc, argv, *lookup);
1519: *firstarg = ISC_FALSE;
1520: }
1521: ISC_LIST_APPEND(lookup_list, *lookup, link);
1522: } else {
1523: fprintf(stderr, "Invalid IP address %s\n", value);
1524: exit(1);
1525: }
1526: return (value_from_next);
1527: invalid_option:
1528: default:
1529: fprintf(stderr, "Invalid option: -%s\n", option);
1530: usage();
1531: }
1532: /* NOTREACHED */
1533: return (ISC_FALSE);
1534: }
1535:
1536: /*%
1537: * Because we may be trying to do memory allocation recording, we're going
1538: * to need to parse the arguments for the -m *before* we start the main
1539: * argument parsing routine.
1540: *
1541: * I'd prefer not to have to do this, but I am not quite sure how else to
1542: * fix the problem. Argument parsing in dig involves memory allocation
1543: * by its nature, so it can't be done in the main argument parser.
1544: */
1545: static void
1546: preparse_args(int argc, char **argv) {
1547: int rc;
1548: char **rv;
1549: char *option;
1550:
1551: rc = argc;
1552: rv = argv;
1553: for (rc--, rv++; rc > 0; rc--, rv++) {
1554: if (rv[0][0] != '-')
1555: continue;
1556: option = &rv[0][1];
1557: while (strpbrk(option, single_dash_opts) == &option[0]) {
1558: switch (option[0]) {
1559: case '4':
1560: if (ipv6only)
1561: fatal("only one of -4 and -6 allowed");
1562: ipv4only = ISC_TRUE;
1563: break;
1564: case '6':
1565: if (ipv4only)
1566: fatal("only one of -4 and -6 allowed");
1567: ipv6only = ISC_TRUE;
1568: break;
1569: }
1570: option = &option[1];
1571: }
1572: }
1573: }
1574:
1575: static void
1576: parse_args(isc_boolean_t is_batchfile, isc_boolean_t config_only,
1577: int argc, char **argv)
1578: {
1579: isc_result_t result;
1580: isc_textregion_t tr;
1581: isc_boolean_t firstarg = ISC_TRUE;
1582: dig_lookup_t *lookup = NULL;
1583: dns_rdatatype_t rdtype;
1584: dns_rdataclass_t rdclass;
1585: isc_boolean_t open_type_class = ISC_TRUE;
1586: char batchline[MXNAME];
1587: int bargc;
1588: char *bargv[64];
1589: int rc;
1590: char **rv;
1591: char *input;
1592: int i;
1593: isc_boolean_t need_clone = ISC_TRUE;
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 = ISC_TRUE;
1611: default_lookup->edns = 0;
1612: }
1613:
1614: if (is_batchfile && !config_only) {
1615: /* Processing '-f batchfile'. */
1616: lookup = clone_lookup(default_lookup, ISC_TRUE);
1617: need_clone = ISC_FALSE;
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 = ISC_TRUE;
1699: result = parse_uint(&serial,
1700: &rv[0][5],
1701: MAXSERIAL,
1702: "serial number");
1703: if (result != ISC_R_SUCCESS)
1704: fatal("Couldn't parse "
1705: "serial number");
1706: lookup->ixfr_serial = serial;
1707: lookup->section_question =
1708: plusquest;
1709: lookup->comments = pluscomm;
1710: if (!lookup->tcp_mode_set)
1711: lookup->tcp_mode = ISC_TRUE;
1712: } else {
1713: lookup->rdtype = rdtype;
1714: lookup->rdtypeset = ISC_TRUE;
1715: if (rdtype ==
1716: dns_rdatatype_axfr) {
1717: lookup->section_question =
1718: plusquest;
1719: lookup->comments = pluscomm;
1720: }
1721: lookup->ixfr_serial = ISC_FALSE;
1722: }
1723: continue;
1724: }
1725: result = dns_rdataclass_fromtext(&rdclass,
1726: (isc_textregion_t *)&tr);
1727: if (result == ISC_R_SUCCESS) {
1728: if (lookup->rdclassset) {
1729: fprintf(stderr, ";; Warning, "
1730: "extra class option\n");
1731: }
1732: lookup->rdclass = rdclass;
1733: lookup->rdclassset = ISC_TRUE;
1734: continue;
1735: }
1736: }
1737:
1738: if (!config_only) {
1739: if (need_clone)
1740: lookup = clone_lookup(default_lookup,
1741: ISC_TRUE);
1742: need_clone = ISC_TRUE;
1743: strlcpy(lookup->textname, rv[0],
1744: sizeof(lookup->textname));
1745: lookup->trace_root = ISC_TF(lookup->trace ||
1746: lookup->ns_search_only);
1747: lookup->new_search = ISC_TRUE;
1748: if (firstarg) {
1749: printgreeting(argc, argv, lookup);
1750: firstarg = ISC_FALSE;
1751: }
1752: ISC_LIST_APPEND(lookup_list, lookup, link);
1753: debug("looking up %s", lookup->textname);
1754: }
1755: /* XXX Error message */
1756: }
1757: }
1758:
1759: /*
1760: * If we have a batchfile, seed the lookup list with the
1761: * first entry, then trust the callback in dighost_shutdown
1762: * to get the rest
1763: */
1764: if ((batchname != NULL) && !(is_batchfile)) {
1765: if (strcmp(batchname, "-") == 0)
1766: batchfp = stdin;
1767: else
1768: batchfp = fopen(batchname, "r");
1769: if (batchfp == NULL) {
1770: perror(batchname);
1771: if (exitcode < 8)
1772: exitcode = 8;
1773: fatal("couldn't open specified batch file");
1774: }
1775: /* XXX Remove code dup from shutdown code */
1776: next_line:
1777: if (fgets(batchline, sizeof(batchline), batchfp) != 0) {
1778: bargc = 1;
1779: debug("batch line %s", batchline);
1780: if (batchline[0] == '\r' || batchline[0] == '\n'
1781: || batchline[0] == '#' || batchline[0] == ';')
1782: goto next_line;
1783: input = batchline;
1784: bargv[bargc] = next_token(&input, " \t\r\n");
1785: while ((bargc < 14) && (bargv[bargc] != NULL)) {
1786: bargc++;
1787: bargv[bargc] = next_token(&input, " \t\r\n");
1788: }
1789:
1790: bargv[0] = argv[0];
1791: argv0 = argv[0];
1792:
1793: for(i = 0; i < bargc; i++)
1794: debug("batch argv %d: %s", i, bargv[i]);
1795: parse_args(ISC_TRUE, ISC_FALSE, bargc, (char **)bargv);
1796: return;
1797: }
1798: return;
1799: }
1800: /*
1801: * If no lookup specified, search for root
1802: */
1803: if ((lookup_list.head == NULL) && !config_only) {
1804: if (need_clone)
1805: lookup = clone_lookup(default_lookup, ISC_TRUE);
1806: need_clone = ISC_TRUE;
1807: lookup->trace_root = ISC_TF(lookup->trace ||
1808: lookup->ns_search_only);
1809: lookup->new_search = ISC_TRUE;
1810: strlcpy(lookup->textname, ".", sizeof(lookup->textname));
1811: lookup->rdtype = dns_rdatatype_ns;
1812: lookup->rdtypeset = ISC_TRUE;
1813: if (firstarg) {
1814: printgreeting(argc, argv, lookup);
1815: firstarg = ISC_FALSE;
1816: }
1817: ISC_LIST_APPEND(lookup_list, lookup, link);
1818: }
1819: if (!need_clone)
1820: destroy_lookup(lookup);
1821: }
1822:
1823: /*
1824: * Callback from dighost.c to allow program-specific shutdown code.
1825: * Here, we're possibly reading from a batch file, then shutting down
1826: * for real if there's nothing in the batch file to read.
1827: */
1828: static void
1829: query_finished(void) {
1830: char batchline[MXNAME];
1831: int bargc;
1832: char *bargv[16];
1833: char *input;
1834: int i;
1835:
1836: if (batchname == NULL) {
1837: isc_app_shutdown();
1838: return;
1839: }
1840:
1841: fflush(stdout);
1842: if (feof(batchfp)) {
1843: batchname = NULL;
1844: isc_app_shutdown();
1845: if (batchfp != stdin)
1846: fclose(batchfp);
1847: return;
1848: }
1849:
1850: if (fgets(batchline, sizeof(batchline), batchfp) != 0) {
1851: debug("batch line %s", batchline);
1852: bargc = 1;
1853: input = batchline;
1854: bargv[bargc] = next_token(&input, " \t\r\n");
1855: while ((bargc < 14) && (bargv[bargc] != NULL)) {
1856: bargc++;
1857: bargv[bargc] = next_token(&input, " \t\r\n");
1858: }
1859:
1860: bargv[0] = argv0;
1861:
1862: for(i = 0; i < bargc; i++)
1863: debug("batch argv %d: %s", i, bargv[i]);
1864: parse_args(ISC_TRUE, ISC_FALSE, bargc, (char **)bargv);
1865: start_lookup();
1866: } else {
1867: batchname = NULL;
1868: if (batchfp != stdin)
1869: fclose(batchfp);
1870: isc_app_shutdown();
1871: return;
1872: }
1873: }
1874:
1875: void dig_setup(int argc, char **argv)
1876: {
1877: isc_result_t result;
1878:
1879: ISC_LIST_INIT(lookup_list);
1880: ISC_LIST_INIT(server_list);
1.8 ! florian 1881: ISC_LIST_INIT(root_hints_server_list);
1.1 florian 1882: ISC_LIST_INIT(search_list);
1883:
1884: if (pledge("stdio rpath inet dns", NULL) == -1) {
1885: perror("pledge");
1886: exit(1);
1887: }
1888:
1889: debug("dig_setup()");
1890:
1891: /* setup dighost callbacks */
1892: dighost_printmessage = printmessage;
1893: dighost_received = received;
1894: dighost_trying = trying;
1895: dighost_shutdown = query_finished;
1896:
1897: progname = argv[0];
1898: preparse_args(argc, argv);
1899:
1900: result = isc_app_start();
1901: check_result(result, "isc_app_start");
1902:
1903: setup_libs();
1904: setup_system(ipv4only, ipv6only);
1905: }
1906:
1907: void dig_query_setup(isc_boolean_t is_batchfile, isc_boolean_t config_only,
1908: int argc, char **argv)
1909: {
1910: debug("dig_query_setup");
1911:
1912: parse_args(is_batchfile, config_only, argc, argv);
1913: if (keyfile[0] != 0)
1914: setup_file_key();
1915: else if (keysecret[0] != 0)
1916: setup_text_key();
1917:
1918: if (pledge("stdio inet dns", NULL) == -1) {
1919: perror("pledge");
1920: exit(1);
1921: }
1922:
1923: if (domainopt[0] != '\0') {
1924: set_search_domain(domainopt);
1925: usesearch = ISC_TRUE;
1926: }
1927: }
1928:
1929: void dig_startup() {
1930: isc_result_t result;
1931:
1932: debug("dig_startup()");
1933:
1934: result = isc_app_onrun(global_task, onrun_callback, NULL);
1935: check_result(result, "isc_app_onrun");
1936: isc_app_run();
1937: }
1938:
1939: void
1940: dig_shutdown() {
1941: destroy_lookup(default_lookup);
1942: if (batchname != NULL) {
1943: if (batchfp != stdin)
1944: fclose(batchfp);
1945: batchname = NULL;
1946: }
1947:
1948: cancel_all();
1949: destroy_libs();
1950: isc_app_finish();
1951: }
1952:
1953: /*% Main processing routine for dig */
1954: int
1955: main(int argc, char **argv) {
1956: extern char *__progname;
1957:
1958: if (strcmp("host", __progname) == 0)
1959: return host_main(argc, argv);
1960: if (strcmp("nslookup", __progname) == 0)
1961: return nslookup_main(argc, argv);
1962:
1963: dig_setup(argc, argv);
1964: dig_query_setup(ISC_FALSE, ISC_FALSE, argc, argv);
1965: dig_startup();
1966: dig_shutdown();
1967:
1968: return (exitcode);
1969: }