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