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