Add BIND 9.2.4rc7.
[dragonfly.git] / contrib / bind-9.2.4rc7 / bin / dig / dig.c
blobb401dc72abcef9de085dc94dbee9cd256efddd53
1 /*
2 * Copyright (C) 2004 Internet Systems Consortium, Inc. ("ISC")
3 * Copyright (C) 2000-2003 Internet Software Consortium.
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
10 * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
12 * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
14 * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 * PERFORMANCE OF THIS SOFTWARE.
18 /* $Id: dig.c,v 1.157.2.16 2004/06/07 03:59:08 marka Exp $ */
20 #include <config.h>
21 #include <stdlib.h>
22 #include <time.h>
23 #include <ctype.h>
25 #include <isc/app.h>
26 #include <isc/netaddr.h>
27 #include <isc/print.h>
28 #include <isc/string.h>
29 #include <isc/util.h>
30 #include <isc/task.h>
32 #include <dns/byaddr.h>
33 #include <dns/fixedname.h>
34 #include <dns/masterdump.h>
35 #include <dns/message.h>
36 #include <dns/name.h>
37 #include <dns/rdata.h>
38 #include <dns/rdataset.h>
39 #include <dns/rdatatype.h>
40 #include <dns/rdataclass.h>
41 #include <dns/result.h>
43 #include <dig/dig.h>
45 extern ISC_LIST(dig_lookup_t) lookup_list;
46 extern dig_serverlist_t server_list;
47 extern ISC_LIST(dig_searchlist_t) search_list;
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); \
57 extern isc_boolean_t have_ipv4, have_ipv6, specified_source,
58 usesearch, qr;
59 extern in_port_t port;
60 extern unsigned int timeout;
61 extern isc_mem_t *mctx;
62 extern dns_messageid_t id;
63 extern int sendcount;
64 extern int ndots;
65 extern int tries;
66 extern int lookup_counter;
67 extern int exitcode;
68 extern isc_sockaddr_t bind_address;
69 extern char keynametext[MXNAME];
70 extern char keyfile[MXNAME];
71 extern char keysecret[MXNAME];
72 extern dns_tsigkey_t *key;
73 extern isc_boolean_t validated;
74 extern isc_taskmgr_t *taskmgr;
75 extern isc_task_t *global_task;
76 extern isc_boolean_t free_now;
77 dig_lookup_t *default_lookup = NULL;
79 extern isc_boolean_t debugging, memdebugging;
80 static char *batchname = NULL;
81 static FILE *batchfp = NULL;
82 static char *argv0;
84 static char domainopt[DNS_NAME_MAXTEXT];
86 static isc_boolean_t short_form = ISC_FALSE, printcmd = ISC_TRUE,
87 ip6_int = ISC_FALSE, plusquest = ISC_FALSE, pluscomm = ISC_FALSE,
88 multiline = ISC_FALSE;
90 static const char *opcodetext[] = {
91 "QUERY",
92 "IQUERY",
93 "STATUS",
94 "RESERVED3",
95 "NOTIFY",
96 "UPDATE",
97 "RESERVED6",
98 "RESERVED7",
99 "RESERVED8",
100 "RESERVED9",
101 "RESERVED10",
102 "RESERVED11",
103 "RESERVED12",
104 "RESERVED13",
105 "RESERVED14",
106 "RESERVED15"
109 static const char *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"
129 extern char *progname;
131 static void
132 print_usage(FILE *fp) {
133 fputs(
134 "Usage: dig [@global-server] [domain] [q-type] [q-class] {q-opt}\n"
135 " {global-d-opt} host [@local-server] {local-d-opt}\n"
136 " [ host [@local-server] {local-d-opt} [...]]\n", fp);
139 static void
140 usage(void) {
141 print_usage(stderr);
142 fputs("\nUse \"dig -h\" (or \"dig -h | more\") "
143 "for complete list of options\n", stderr);
144 exit(1);
147 static void
148 help(void) {
149 print_usage(stdout);
150 fputs(
151 "Where: domain are in the Domain Name System\n"
152 " q-class is one of (in,hs,ch,...) [default: in]\n"
153 " q-type is one of (a,any,mx,ns,soa,hinfo,axfr,txt,...) [default:a]\n"
154 " (Use ixfr=version for type ixfr)\n"
155 " q-opt is one of:\n"
156 " -x dot-notation (shortcut for in-addr lookups)\n"
157 " -i (IP6.INT reverse IPv6 lookups)\n"
158 " -f filename (batch mode)\n"
159 " -b address (bind to source address)\n"
160 " -p port (specify port number)\n"
161 " -t type (specify query type)\n"
162 " -c class (specify query class)\n"
163 " -k keyfile (specify tsig key file)\n"
164 " -y name:key (specify named base64 tsig key)\n"
165 " d-opt is of the form +keyword[=value], where keyword is:\n"
166 " +[no]vc (TCP mode)\n"
167 " +[no]tcp (TCP mode, alternate syntax)\n"
168 " +time=### (Set query timeout) [5]\n"
169 " +tries=### (Set number of UDP attempts) [3]\n"
170 " +domain=### (Set default domainname)\n"
171 " +bufsize=### (Set EDNS0 Max UDP packet size)\n"
172 " +ndots=### (Set NDOTS value)\n"
173 " +[no]search (Set whether to use searchlist)\n"
174 " +[no]defname (Ditto)\n"
175 " +[no]recurse (Recursive mode)\n"
176 " +[no]ignore (Don't revert to TCP for TC responses.)"
177 "\n"
178 " +[no]fail (Don't try next server on SERVFAIL)\n"
179 " +[no]besteffort (Try to parse even illegal messages)\n"
180 " +[no]aaonly (Set AA flag in query)\n"
181 " +[no]adflag (Set AD flag in query)\n"
182 " +[no]cdflag (Set CD flag in query)\n"
183 " +[no]cmd (Control display of command line)\n"
184 " +[no]comments (Control display of comment lines)\n"
185 " +[no]question (Control display of question)\n"
186 " +[no]answer (Control display of answer)\n"
187 " +[no]authority (Control display of authority)\n"
188 " +[no]additional (Control display of additional)\n"
189 " +[no]stats (Control display of statistics)\n"
190 " +[no]short (Disable everything except short\n"
191 " form of answer)\n"
192 " +[no]all (Set or clear all display flags)\n"
193 " +[no]qr (Print question before sending)\n"
194 " +[no]nssearch (Search all authoritative nameservers)\n"
195 " +[no]identify (ID responders in short answers)\n"
196 " +[no]trace (Trace delegation down from root)\n"
197 " +[no]dnssec (Request DNSSEC records)\n"
198 " +[no]multiline (Print records in an expanded format)\n"
199 " global d-opts and servers (before host name) affect all queries.\n"
200 " local d-opts and servers (after host name) affect only that lookup.\n",
201 stdout);
205 * Callback from dighost.c to print the received message.
207 void
208 received(int bytes, isc_sockaddr_t *from, dig_query_t *query) {
209 isc_uint64_t diff;
210 isc_time_t now;
211 isc_result_t result;
212 time_t tnow;
213 char fromtext[ISC_SOCKADDR_FORMATSIZE];
215 isc_sockaddr_format(from, fromtext, sizeof(fromtext));
217 result = isc_time_now(&now);
218 check_result(result, "isc_time_now");
220 if (query->lookup->stats && !short_form) {
221 diff = isc_time_microdiff(&now, &query->time_sent);
222 printf(";; Query time: %ld msec\n", (long int)diff/1000);
223 printf(";; SERVER: %s(%s)\n", fromtext, query->servname);
224 time(&tnow);
225 printf(";; WHEN: %s", ctime(&tnow));
226 if (query->lookup->doing_xfr) {
227 printf(";; XFR size: %d records\n",
228 query->rr_count);
229 } else {
230 printf(";; MSG SIZE rcvd: %d\n", bytes);
233 if (key != NULL) {
234 if (!validated)
235 puts(";; WARNING -- Some TSIG could not "
236 "be validated");
238 if ((key == NULL) && (keysecret[0] != 0)) {
239 puts(";; WARNING -- TSIG key was not used.");
241 puts("");
242 } else if (query->lookup->identify && !short_form) {
243 diff = isc_time_microdiff(&now, &query->time_sent);
244 printf(";; Received %u bytes from %s(%s) in %d ms\n\n",
245 bytes, fromtext, query->servname,
246 (int)diff/1000);
251 * Callback from dighost.c to print that it is trying a server.
252 * Not used in dig.
253 * XXX print_trying
255 void
256 trying(char *frm, dig_lookup_t *lookup) {
257 UNUSED(frm);
258 UNUSED(lookup);
262 * Internal print routine used to print short form replies.
264 static isc_result_t
265 say_message(dns_rdata_t *rdata, dig_query_t *query, isc_buffer_t *buf) {
266 isc_result_t result;
267 isc_uint64_t diff;
268 isc_time_t now;
269 char store[sizeof("12345678901234567890")];
271 if (query->lookup->trace || query->lookup->ns_search_only) {
272 result = dns_rdatatype_totext(rdata->type, buf);
273 if (result != ISC_R_SUCCESS)
274 return (result);
275 ADD_STRING(buf, " ");
277 result = dns_rdata_totext(rdata, NULL, buf);
278 check_result(result, "dns_rdata_totext");
279 if (query->lookup->identify) {
280 result = isc_time_now(&now);
281 if (result != ISC_R_SUCCESS)
282 return (result);
283 diff = isc_time_microdiff(&now, &query->time_sent);
284 ADD_STRING(buf, " from server ");
285 ADD_STRING(buf, query->servname);
286 snprintf(store, 19, " in %d ms.", (int)diff/1000);
287 ADD_STRING(buf, store);
289 ADD_STRING(buf, "\n");
290 return (ISC_R_SUCCESS);
294 * short_form message print handler. Calls above say_message()
296 static isc_result_t
297 short_answer(dns_message_t *msg, dns_messagetextflag_t flags,
298 isc_buffer_t *buf, dig_query_t *query)
300 dns_name_t *name;
301 dns_rdataset_t *rdataset;
302 isc_buffer_t target;
303 isc_result_t result, loopresult;
304 dns_name_t empty_name;
305 char t[4096];
306 dns_rdata_t rdata = DNS_RDATA_INIT;
308 UNUSED(flags);
310 dns_name_init(&empty_name, NULL);
311 result = dns_message_firstname(msg, DNS_SECTION_ANSWER);
312 if (result == ISC_R_NOMORE)
313 return (ISC_R_SUCCESS);
314 else if (result != ISC_R_SUCCESS)
315 return (result);
317 for (;;) {
318 name = NULL;
319 dns_message_currentname(msg, DNS_SECTION_ANSWER, &name);
321 isc_buffer_init(&target, t, sizeof(t));
323 for (rdataset = ISC_LIST_HEAD(name->list);
324 rdataset != NULL;
325 rdataset = ISC_LIST_NEXT(rdataset, link)) {
326 loopresult = dns_rdataset_first(rdataset);
327 while (loopresult == ISC_R_SUCCESS) {
328 dns_rdataset_current(rdataset, &rdata);
329 result = say_message(&rdata, query,
330 buf);
331 check_result(result, "say_message");
332 loopresult = dns_rdataset_next(rdataset);
333 dns_rdata_reset(&rdata);
336 result = dns_message_nextname(msg, DNS_SECTION_ANSWER);
337 if (result == ISC_R_NOMORE)
338 break;
339 else if (result != ISC_R_SUCCESS)
340 return (result);
343 return (ISC_R_SUCCESS);
347 * Callback from dighost.c to print the reply from a server
349 isc_result_t
350 printmessage(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers) {
351 isc_result_t result;
352 dns_messagetextflag_t flags;
353 isc_buffer_t *buf = NULL;
354 unsigned int len = OUTPUTBUF;
355 const dns_master_style_t *style;
357 if (multiline)
358 style = &dns_master_style_default;
359 else
360 style = &dns_master_style_debug;
362 if (query->lookup->cmdline[0] != 0) {
363 if (!short_form)
364 fputs(query->lookup->cmdline, stdout);
365 query->lookup->cmdline[0]=0;
367 debug("printmessage(%s %s %s)", headers ? "headers" : "noheaders",
368 query->lookup->comments ? "comments" : "nocomments",
369 short_form ? "short_form" : "long_form");
371 flags = 0;
372 if (!headers) {
373 flags |= DNS_MESSAGETEXTFLAG_NOHEADERS;
374 flags |= DNS_MESSAGETEXTFLAG_NOCOMMENTS;
376 if (!query->lookup->comments)
377 flags |= DNS_MESSAGETEXTFLAG_NOCOMMENTS;
379 result = ISC_R_SUCCESS;
381 result = isc_buffer_allocate(mctx, &buf, len);
382 check_result(result, "isc_buffer_allocate");
384 if (query->lookup->comments && !short_form) {
385 if (query->lookup->cmdline[0] != 0)
386 printf("; %s\n", query->lookup->cmdline);
387 if (msg == query->lookup->sendmsg)
388 printf(";; Sending:\n");
389 else
390 printf(";; Got answer:\n");
392 if (headers) {
393 printf(";; ->>HEADER<<- opcode: %s, status: %s, "
394 "id: %u\n",
395 opcodetext[msg->opcode], rcodetext[msg->rcode],
396 msg->id);
397 printf(";; flags:");
398 if ((msg->flags & DNS_MESSAGEFLAG_QR) != 0)
399 printf(" qr");
400 if ((msg->flags & DNS_MESSAGEFLAG_AA) != 0)
401 printf(" aa");
402 if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0)
403 printf(" tc");
404 if ((msg->flags & DNS_MESSAGEFLAG_RD) != 0)
405 printf(" rd");
406 if ((msg->flags & DNS_MESSAGEFLAG_RA) != 0)
407 printf(" ra");
408 if ((msg->flags & DNS_MESSAGEFLAG_AD) != 0)
409 printf(" ad");
410 if ((msg->flags & DNS_MESSAGEFLAG_CD) != 0)
411 printf(" cd");
413 printf("; QUERY: %u, ANSWER: %u, "
414 "AUTHORITY: %u, ADDITIONAL: %u\n",
415 msg->counts[DNS_SECTION_QUESTION],
416 msg->counts[DNS_SECTION_ANSWER],
417 msg->counts[DNS_SECTION_AUTHORITY],
418 msg->counts[DNS_SECTION_ADDITIONAL]);
422 repopulate_buffer:
424 if (query->lookup->comments && headers && !short_form)
426 result = dns_message_pseudosectiontotext(msg,
427 DNS_PSEUDOSECTION_OPT,
428 style, flags, buf);
429 if (result == ISC_R_NOSPACE) {
430 buftoosmall:
431 len += OUTPUTBUF;
432 isc_buffer_free(&buf);
433 result = isc_buffer_allocate(mctx, &buf, len);
434 if (result == ISC_R_SUCCESS)
435 goto repopulate_buffer;
436 else
437 return (result);
439 check_result(result,
440 "dns_message_pseudosectiontotext");
443 if (query->lookup->section_question && headers) {
444 if (!short_form) {
445 result = dns_message_sectiontotext(msg,
446 DNS_SECTION_QUESTION,
447 style, flags, buf);
448 if (result == ISC_R_NOSPACE)
449 goto buftoosmall;
450 check_result(result, "dns_message_sectiontotext");
453 if (query->lookup->section_answer) {
454 if (!short_form) {
455 result = dns_message_sectiontotext(msg,
456 DNS_SECTION_ANSWER,
457 style, flags, buf);
458 if (result == ISC_R_NOSPACE)
459 goto buftoosmall;
460 check_result(result, "dns_message_sectiontotext");
461 } else {
462 result = short_answer(msg, flags, buf, query);
463 if (result == ISC_R_NOSPACE)
464 goto buftoosmall;
465 check_result(result, "short_answer");
468 if (query->lookup->section_authority) {
469 if (!short_form) {
470 result = dns_message_sectiontotext(msg,
471 DNS_SECTION_AUTHORITY,
472 style, flags, buf);
473 if (result == ISC_R_NOSPACE)
474 goto buftoosmall;
475 check_result(result, "dns_message_sectiontotext");
478 if (query->lookup->section_additional) {
479 if (!short_form) {
480 result = dns_message_sectiontotext(msg,
481 DNS_SECTION_ADDITIONAL,
482 style, flags, buf);
483 if (result == ISC_R_NOSPACE)
484 goto buftoosmall;
485 check_result(result, "dns_message_sectiontotext");
487 * Only print the signature on the first record.
489 if (headers) {
490 result = dns_message_pseudosectiontotext(
491 msg,
492 DNS_PSEUDOSECTION_TSIG,
493 style, flags, buf);
494 if (result == ISC_R_NOSPACE)
495 goto buftoosmall;
496 check_result(result,
497 "dns_message_pseudosectiontotext");
498 result = dns_message_pseudosectiontotext(
499 msg,
500 DNS_PSEUDOSECTION_SIG0,
501 style, flags, buf);
502 if (result == ISC_R_NOSPACE)
503 goto buftoosmall;
504 check_result(result,
505 "dns_message_pseudosectiontotext");
509 if (headers && query->lookup->comments && !short_form)
510 printf("\n");
512 printf("%.*s", (int)isc_buffer_usedlength(buf),
513 (char *)isc_buffer_base(buf));
514 isc_buffer_free(&buf);
515 return (result);
519 * print the greeting message when the program first starts up.
521 static void
522 printgreeting(int argc, char **argv, dig_lookup_t *lookup) {
523 int i;
524 int remaining;
525 static isc_boolean_t first = ISC_TRUE;
526 char append[MXNAME];
528 if (printcmd) {
529 lookup->cmdline[sizeof(lookup->cmdline) - 1] = 0;
530 snprintf(lookup->cmdline, sizeof(lookup->cmdline),
531 "%s; <<>> DiG " VERSION " <<>>",
532 first?"\n":"");
533 i = 1;
534 while (i < argc) {
535 snprintf(append, sizeof(append), " %s", argv[i++]);
536 remaining = sizeof(lookup->cmdline) -
537 strlen(lookup->cmdline) - 1;
538 strncat(lookup->cmdline, append, remaining);
540 remaining = sizeof(lookup->cmdline) -
541 strlen(lookup->cmdline) - 1;
542 strncat(lookup->cmdline, "\n", remaining);
543 if (first) {
544 snprintf(append, sizeof (append),
545 ";; global options: %s %s\n",
546 short_form ? "short_form" : "",
547 printcmd ? "printcmd" : "");
548 first = ISC_FALSE;
549 remaining = sizeof(lookup->cmdline) -
550 strlen(lookup->cmdline) - 1;
551 strncat(lookup->cmdline, append, remaining);
557 * Reorder an argument list so that server names all come at the end.
558 * This is a bit of a hack, to allow batch-mode processing to properly
559 * handle the server options.
561 static void
562 reorder_args(int argc, char *argv[]) {
563 int i, j;
564 char *ptr;
565 int end;
567 debug("reorder_args()");
568 end = argc - 1;
569 while (argv[end][0] == '@') {
570 end--;
571 if (end == 0)
572 return;
574 debug("arg[end]=%s", argv[end]);
575 for (i = 1; i < end - 1; i++) {
576 if (argv[i][0] == '@') {
577 debug("arg[%d]=%s", i, argv[i]);
578 ptr = argv[i];
579 for (j = i + 1; j < end; j++) {
580 debug("Moving %s to %d", argv[j], j - 1);
581 argv[j - 1] = argv[j];
583 debug("moving %s to end, %d", ptr, end - 1);
584 argv[end - 1] = ptr;
585 end--;
586 if (end < 1)
587 return;
592 static isc_uint32_t
593 parse_uint(char *arg, const char *desc, isc_uint32_t max) {
594 char *endp;
595 isc_uint32_t tmp;
597 tmp = strtoul(arg, &endp, 10);
598 if (*endp != '\0')
599 fatal("%s '%s' must be numeric", desc, arg);
600 if (tmp > max)
601 fatal("%s '%s' out of range", desc, arg);
602 return (tmp);
606 * We're not using isc_commandline_parse() here since the command line
607 * syntax of dig is quite a bit different from that which can be described
608 * by that routine.
609 * XXX doc options
612 static void
613 plus_option(char *option, isc_boolean_t is_batchfile,
614 dig_lookup_t *lookup)
616 char option_store[256];
617 char *cmd, *value, *ptr;
618 isc_boolean_t state = ISC_TRUE;
620 strncpy(option_store, option, sizeof(option_store));
621 option_store[sizeof(option_store)-1]=0;
622 ptr = option_store;
623 cmd=next_token(&ptr,"=");
624 if (cmd == NULL) {
625 printf(";; Invalid option %s\n",option_store);
626 return;
628 value=ptr;
629 if (strncasecmp(cmd,"no",2)==0) {
630 cmd += 2;
631 state = ISC_FALSE;
633 switch (cmd[0]) {
634 case 'a':
635 switch (cmd[1]) {
636 case 'a': /* aaflag */
637 lookup->aaonly = state;
638 break;
639 case 'd':
640 switch (cmd[2]) {
641 case 'd': /* additional */
642 lookup->section_additional = state;
643 break;
644 case 'f': /* adflag */
645 lookup->adflag = state;
646 break;
647 default:
648 goto invalid_option;
650 break;
651 case 'l': /* all */
652 lookup->section_question = state;
653 lookup->section_authority = state;
654 lookup->section_answer = state;
655 lookup->section_additional = state;
656 lookup->comments = state;
657 lookup->stats = state;
658 printcmd = state;
659 break;
660 case 'n': /* answer */
661 lookup->section_answer = state;
662 break;
663 case 'u': /* authority */
664 lookup->section_authority = state;
665 break;
666 default:
667 goto invalid_option;
669 break;
670 case 'b':
671 switch (cmd[1]) {
672 case 'e':/* besteffort */
673 lookup->besteffort = state;
674 break;
675 case 'u':/* bufsize */
676 if (value == NULL)
677 goto need_value;
678 if (!state)
679 goto invalid_option;
680 lookup->udpsize = (isc_uint16_t) parse_uint(value,
681 "buffer size", COMMSIZE);
682 break;
683 default:
684 goto invalid_option;
686 break;
687 case 'c':
688 switch (cmd[1]) {
689 case 'd':/* cdflag */
690 lookup->cdflag = state;
691 break;
692 case 'm': /* cmd */
693 printcmd = state;
694 break;
695 case 'o': /* comments */
696 lookup->comments = state;
697 if (lookup == default_lookup)
698 pluscomm = state;
699 break;
700 default:
701 goto invalid_option;
703 break;
704 case 'd':
705 switch (cmd[1]) {
706 case 'e': /* defname */
707 usesearch = state;
708 break;
709 case 'n': /* dnssec */
710 lookup->dnssec = state;
711 break;
712 case 'o': /* domain */
713 if (value == NULL)
714 goto need_value;
715 if (!state)
716 goto invalid_option;
717 strncpy(domainopt, value, sizeof(domainopt));
718 domainopt[sizeof(domainopt)-1] = '\0';
719 break;
720 default:
721 goto invalid_option;
723 break;
724 case 'f': /* fail */
725 lookup->servfail_stops = state;
726 break;
727 case 'i':
728 switch (cmd[1]) {
729 case 'd': /* identify */
730 lookup->identify = state;
731 break;
732 case 'g': /* ignore */
733 default: /* Inherets default for compatibility */
734 lookup->ignore = ISC_TRUE;
736 break;
737 case 'm': /* multiline */
738 multiline = state;
739 break;
740 case 'n':
741 switch (cmd[1]) {
742 case 'd': /* ndots */
743 if (value == NULL)
744 goto need_value;
745 if (!state)
746 goto invalid_option;
747 ndots = parse_uint(value, "ndots", MAXNDOTS);
748 break;
749 case 's': /* nssearch */
750 lookup->ns_search_only = state;
751 if (state) {
752 lookup->trace_root = ISC_TRUE;
753 lookup->recurse = ISC_FALSE;
754 lookup->identify = ISC_TRUE;
755 lookup->stats = ISC_FALSE;
756 lookup->comments = ISC_FALSE;
757 lookup->section_additional = ISC_FALSE;
758 lookup->section_authority = ISC_FALSE;
759 lookup->section_question = ISC_FALSE;
760 lookup->rdtype = dns_rdatatype_ns;
761 lookup->rdtypeset = ISC_TRUE;
762 short_form = ISC_TRUE;
764 break;
765 default:
766 goto invalid_option;
768 break;
769 case 'q':
770 switch (cmd[1]) {
771 case 'r': /* qr */
772 qr = state;
773 break;
774 case 'u': /* question */
775 lookup->section_question = state;
776 if (lookup == default_lookup)
777 plusquest = state;
778 break;
779 default:
780 goto invalid_option;
782 break;
783 case 'r': /* recurse */
784 lookup->recurse = state;
785 break;
786 case 's':
787 switch (cmd[1]) {
788 case 'e': /* search */
789 usesearch = state;
790 break;
791 case 'h': /* short */
792 short_form = state;
793 if (state) {
794 printcmd = ISC_FALSE;
795 lookup->section_additional = ISC_FALSE;
796 lookup->section_answer = ISC_TRUE;
797 lookup->section_authority = ISC_FALSE;
798 lookup->section_question = ISC_FALSE;
799 lookup->comments = ISC_FALSE;
800 lookup->stats = ISC_FALSE;
802 break;
803 case 't': /* stats */
804 lookup->stats = state;
805 break;
806 default:
807 goto invalid_option;
809 break;
810 case 't':
811 switch (cmd[1]) {
812 case 'c': /* tcp */
813 if (!is_batchfile)
814 lookup->tcp_mode = state;
815 break;
816 case 'i': /* timeout */
817 if (value == NULL)
818 goto need_value;
819 if (!state)
820 goto invalid_option;
821 timeout = parse_uint(value, "timeout", MAXTIMEOUT);
822 if (timeout == 0)
823 timeout = 1;
824 break;
825 case 'r':
826 switch (cmd[2]) {
827 case 'a': /* trace */
828 lookup->trace = state;
829 lookup->trace_root = state;
830 if (state) {
831 lookup->recurse = ISC_FALSE;
832 lookup->identify = ISC_TRUE;
833 lookup->comments = ISC_FALSE;
834 lookup->stats = ISC_FALSE;
835 lookup->section_additional = ISC_FALSE;
836 lookup->section_authority = ISC_TRUE;
837 lookup->section_question = ISC_FALSE;
839 break;
840 case 'i': /* tries */
841 if (value == NULL)
842 goto need_value;
843 if (!state)
844 goto invalid_option;
845 lookup->retries = parse_uint(value, "retries",
846 MAXTRIES);
847 if (lookup->retries == 0)
848 lookup->retries = 1;
849 break;
850 default:
851 goto invalid_option;
853 break;
854 default:
855 goto invalid_option;
857 break;
858 case 'v':
859 if (!is_batchfile)
860 lookup->tcp_mode = state;
861 break;
862 default:
863 invalid_option:
864 need_value:
865 fprintf(stderr, "Invalid option: +%s\n",
866 option);
867 usage();
869 return;
873 * ISC_TRUE returned if value was used
875 static isc_boolean_t
876 dash_option(char *option, char *next, dig_lookup_t **lookup,
877 isc_boolean_t *open_type_class,
878 isc_boolean_t *firstarg,
879 int argc, char **argv)
881 char cmd, *value, *ptr;
882 isc_result_t result;
883 isc_boolean_t value_from_next;
884 isc_textregion_t tr;
885 dns_rdatatype_t rdtype;
886 dns_rdataclass_t rdclass;
887 char textname[MXNAME];
888 struct in_addr in4;
889 struct in6_addr in6;
891 cmd = option[0];
892 if (strlen(option) > 1U) {
893 value_from_next = ISC_FALSE;
894 value = &option[1];
895 } else {
896 value_from_next = ISC_TRUE;
897 value = next;
899 switch (cmd) {
900 case 'd':
901 debugging = ISC_TRUE;
902 return (ISC_FALSE);
903 case 'h':
904 help();
905 exit(0);
906 break;
907 case 'i':
908 ip6_int = ISC_TRUE;
909 return (ISC_FALSE);
910 case 'm': /* memdebug */
911 /* memdebug is handled in preparse_args() */
912 return (ISC_FALSE);
913 case 'n':
914 /* deprecated */
915 return (ISC_FALSE);
917 if (value == NULL)
918 goto invalid_option;
919 switch (cmd) {
920 case 'b':
921 if (have_ipv6 && inet_pton(AF_INET6, value, &in6) == 1)
922 isc_sockaddr_fromin6(&bind_address, &in6, 0);
923 else if (have_ipv4 && inet_pton(AF_INET, value, &in4) == 1)
924 isc_sockaddr_fromin(&bind_address, &in4, 0);
925 else
926 fatal("invalid address %s", value);
927 specified_source = ISC_TRUE;
928 return (value_from_next);
929 case 'c':
930 if ((*lookup)->rdclassset) {
931 fprintf(stderr, ";; Warning, extra class option\n");
933 *open_type_class = ISC_FALSE;
934 tr.base = value;
935 tr.length = strlen(value);
936 result = dns_rdataclass_fromtext(&rdclass,
937 (isc_textregion_t *)&tr);
938 if (result == ISC_R_SUCCESS) {
939 (*lookup)->rdclass = rdclass;
940 (*lookup)->rdclassset = ISC_TRUE;
941 } else
942 fprintf(stderr, ";; Warning, ignoring "
943 "invalid class %s\n",
944 value);
945 return (value_from_next);
946 case 'f':
947 batchname = value;
948 return (value_from_next);
949 case 'k':
950 strncpy(keyfile, value, sizeof(keyfile));
951 keyfile[sizeof(keyfile)-1]=0;
952 return (value_from_next);
953 case 'p':
954 port = (in_port_t) parse_uint(value, "port number", MAXPORT);
955 return (value_from_next);
956 case 't':
957 *open_type_class = ISC_FALSE;
958 if (strncasecmp(value, "ixfr=", 5) == 0) {
959 rdtype = dns_rdatatype_ixfr;
960 result = ISC_R_SUCCESS;
961 } else {
962 tr.base = value;
963 tr.length = strlen(value);
964 result = dns_rdatatype_fromtext(&rdtype,
965 (isc_textregion_t *)&tr);
966 if (result == ISC_R_SUCCESS &&
967 rdtype == dns_rdatatype_ixfr)
969 result = DNS_R_UNKNOWN;
972 if (result == ISC_R_SUCCESS) {
973 if ((*lookup)->rdtypeset) {
974 fprintf(stderr, ";; Warning, "
975 "extra type option\n");
977 if (rdtype == dns_rdatatype_ixfr) {
978 (*lookup)->rdtype = dns_rdatatype_ixfr;
979 (*lookup)->rdtypeset = ISC_TRUE;
980 (*lookup)->ixfr_serial =
981 parse_uint(&value[5], "serial number",
982 MAXSERIAL);
983 (*lookup)->section_question = plusquest;
984 (*lookup)->comments = pluscomm;
985 } else {
986 (*lookup)->rdtype = rdtype;
987 (*lookup)->rdtypeset = ISC_TRUE;
988 if (rdtype == dns_rdatatype_axfr) {
989 (*lookup)->section_question = plusquest;
990 (*lookup)->comments = pluscomm;
992 (*lookup)->ixfr_serial = ISC_FALSE;
994 } else
995 fprintf(stderr, ";; Warning, ignoring "
996 "invalid type %s\n",
997 value);
998 return (value_from_next);
999 case 'y':
1000 ptr = next_token(&value,":");
1001 if (ptr == NULL) {
1002 usage();
1004 strncpy(keynametext, ptr, sizeof(keynametext));
1005 keynametext[sizeof(keynametext)-1]=0;
1006 ptr = next_token(&value, "");
1007 if (ptr == NULL)
1008 usage();
1009 strncpy(keysecret, ptr, sizeof(keysecret));
1010 keysecret[sizeof(keysecret)-1]=0;
1011 return (value_from_next);
1012 case 'x':
1013 *lookup = clone_lookup(default_lookup, ISC_TRUE);
1014 if (get_reverse(textname, value, ip6_int, ISC_FALSE)
1015 == ISC_R_SUCCESS)
1017 strncpy((*lookup)->textname, textname,
1018 sizeof((*lookup)->textname));
1019 debug("looking up %s", (*lookup)->textname);
1020 (*lookup)->trace_root = ISC_TF((*lookup)->trace ||
1021 (*lookup)->ns_search_only);
1022 (*lookup)->ip6_int = ip6_int;
1023 if (!(*lookup)->rdtypeset)
1024 (*lookup)->rdtype = dns_rdatatype_ptr;
1025 if (!(*lookup)->rdclassset)
1026 (*lookup)->rdclass = dns_rdataclass_in;
1027 (*lookup)->new_search = ISC_TRUE;
1028 if (*lookup && *firstarg)
1030 printgreeting(argc, argv, *lookup);
1031 *firstarg = ISC_FALSE;
1033 ISC_LIST_APPEND(lookup_list, *lookup, link);
1034 } else {
1035 fprintf(stderr, "Invalid IP address %s\n", value);
1036 exit(1);
1038 return (value_from_next);
1039 invalid_option:
1040 default:
1041 fprintf(stderr, "Invalid option: -%s\n", option);
1042 usage();
1044 return (ISC_FALSE);
1048 * Because we may be trying to do memory allocation recording, we're going
1049 * to need to parse the arguments for the -m *before* we start the main
1050 * argument parsing routine.
1051 * I'd prefer not to have to do this, but I am not quite sure how else to
1052 * fix the problem. Argument parsing in dig involves memory allocation
1053 * by its nature, so it can't be done in the main argument parser.
1055 static void
1056 preparse_args(int argc, char **argv) {
1057 int rc;
1058 char **rv;
1060 rc = argc;
1061 rv = argv;
1062 for (rc--, rv++; rc > 0; rc--, rv++) {
1063 if (strcmp(rv[0], "-m") == 0) {
1064 memdebugging = ISC_TRUE;
1065 isc_mem_debugging = ISC_MEM_DEBUGTRACE |
1066 ISC_MEM_DEBUGRECORD;
1067 return;
1073 static void
1074 parse_args(isc_boolean_t is_batchfile, isc_boolean_t config_only,
1075 int argc, char **argv) {
1076 isc_result_t result;
1077 isc_textregion_t tr;
1078 isc_boolean_t firstarg = ISC_TRUE;
1079 dig_server_t *srv = NULL;
1080 dig_lookup_t *lookup = NULL;
1081 dns_rdatatype_t rdtype;
1082 dns_rdataclass_t rdclass;
1083 isc_boolean_t open_type_class = ISC_TRUE;
1084 char batchline[MXNAME];
1085 int bargc;
1086 char *bargv[64];
1087 int rc;
1088 char **rv;
1089 #ifndef NOPOSIX
1090 char *homedir;
1091 char rcfile[256];
1092 #endif
1093 char *input;
1096 * The semantics for parsing the args is a bit complex; if
1097 * we don't have a host yet, make the arg apply globally,
1098 * otherwise make it apply to the latest host. This is
1099 * a bit different than the previous versions, but should
1100 * form a consistent user interface.
1102 * First, create a "default lookup" which won't actually be used
1103 * anywhere, except for cloning into new lookups
1106 debug("parse_args()");
1107 if (!is_batchfile) {
1108 debug("making new lookup");
1109 default_lookup = make_empty_lookup();
1111 #ifndef NOPOSIX
1113 * Treat .digrc as a special batchfile
1115 homedir = getenv("HOME");
1116 if (homedir != NULL)
1117 snprintf(rcfile, sizeof(rcfile), "%s/.digrc", homedir);
1118 else
1119 strcpy(rcfile, ".digrc");
1120 batchfp = fopen(rcfile, "r");
1121 if (batchfp != NULL) {
1122 while (fgets(batchline, sizeof(batchline),
1123 batchfp) != 0) {
1124 debug("config line %s", batchline);
1125 bargc = 1;
1126 input = batchline;
1127 bargv[bargc] = next_token(&input, " \t\r\n");
1128 while ((bargv[bargc] != NULL) &&
1129 (bargc < 62)) {
1130 bargc++;
1131 bargv[bargc] = next_token(&input, " \t\r\n");
1134 bargv[0] = argv[0];
1135 argv0 = argv[0];
1137 reorder_args(bargc, (char **)bargv);
1138 parse_args(ISC_TRUE, ISC_TRUE, bargc,
1139 (char **)bargv);
1141 fclose(batchfp);
1143 #endif
1146 lookup = default_lookup;
1148 rc = argc;
1149 rv = argv;
1150 for (rc--, rv++; rc > 0; rc--, rv++) {
1151 debug("main parsing %s", rv[0]);
1152 if (strncmp(rv[0], "%", 1) == 0)
1153 break;
1154 if (strncmp(rv[0], "@", 1) == 0) {
1155 srv = make_server(&rv[0][1]);
1156 ISC_LIST_APPEND(lookup->my_server_list,
1157 srv, link);
1158 } else if (rv[0][0] == '+') {
1159 plus_option(&rv[0][1], is_batchfile,
1160 lookup);
1161 } else if (rv[0][0] == '-') {
1162 if (rc <= 1) {
1163 if (dash_option(&rv[0][1], NULL,
1164 &lookup, &open_type_class,
1165 &firstarg, argc, argv)) {
1166 rc--;
1167 rv++;
1169 } else {
1170 if (dash_option(&rv[0][1], rv[1],
1171 &lookup, &open_type_class,
1172 &firstarg, argc, argv)) {
1173 rc--;
1174 rv++;
1177 } else {
1179 * Anything which isn't an option
1181 if (open_type_class) {
1182 if (strncmp(rv[0], "ixfr=", 5) == 0) {
1183 rdtype = dns_rdatatype_ixfr;
1184 result = ISC_R_SUCCESS;
1185 } else {
1186 tr.base = rv[0];
1187 tr.length = strlen(rv[0]);
1188 result = dns_rdatatype_fromtext(&rdtype,
1189 (isc_textregion_t *)&tr);
1190 if (result == ISC_R_SUCCESS &&
1191 rdtype == dns_rdatatype_ixfr)
1193 result = DNS_R_UNKNOWN;
1194 fprintf(stderr, ";; Warning, "
1195 "ixfr requires a "
1196 "serial number\n");
1197 continue;
1200 if (result == ISC_R_SUCCESS)
1202 if (lookup->rdtypeset) {
1203 fprintf(stderr, ";; Warning, "
1204 "extra type option\n");
1206 if (rdtype == dns_rdatatype_ixfr) {
1207 lookup->rdtype = dns_rdatatype_ixfr;
1208 lookup->rdtypeset = ISC_TRUE;
1209 lookup->ixfr_serial =
1210 parse_uint(&rv[0][5],
1211 "serial number",
1212 MAXSERIAL);
1213 lookup->section_question = plusquest;
1214 lookup->comments = pluscomm;
1215 } else {
1216 lookup->rdtype = rdtype;
1217 lookup->rdtypeset = ISC_TRUE;
1218 if (rdtype == dns_rdatatype_axfr) {
1219 lookup->section_question =
1220 plusquest;
1221 lookup->comments = pluscomm;
1223 lookup->ixfr_serial = ISC_FALSE;
1225 continue;
1227 result = dns_rdataclass_fromtext(&rdclass,
1228 (isc_textregion_t *)&tr);
1229 if (result == ISC_R_SUCCESS) {
1230 if (lookup->rdclassset) {
1231 fprintf(stderr, ";; Warning, "
1232 "extra class option\n");
1234 lookup->rdclass = rdclass;
1235 lookup->rdclassset = ISC_TRUE;
1236 continue;
1239 if (!config_only) {
1240 lookup = clone_lookup(default_lookup,
1241 ISC_TRUE);
1242 if (firstarg) {
1243 printgreeting(argc, argv, lookup);
1244 firstarg = ISC_FALSE;
1246 strncpy(lookup->textname, rv[0],
1247 sizeof(lookup->textname));
1248 lookup->textname[sizeof(lookup->textname)-1]=0;
1249 lookup->trace_root = ISC_TF(lookup->trace ||
1250 lookup->ns_search_only);
1251 lookup->new_search = ISC_TRUE;
1252 ISC_LIST_APPEND(lookup_list, lookup, link);
1253 debug("looking up %s", lookup->textname);
1255 /* XXX Error message */
1259 * If we have a batchfile, seed the lookup list with the
1260 * first entry, then trust the callback in dighost_shutdown
1261 * to get the rest
1263 if ((batchname != NULL) && !(is_batchfile)) {
1264 if (strcmp(batchname, "-") == 0)
1265 batchfp = stdin;
1266 else
1267 batchfp = fopen(batchname, "r");
1268 if (batchfp == NULL) {
1269 perror(batchname);
1270 if (exitcode < 8)
1271 exitcode = 8;
1272 fatal("Couldn't open specified batch file");
1274 /* XXX Remove code dup from shutdown code */
1275 next_line:
1276 if (fgets(batchline, sizeof(batchline), batchfp) != 0) {
1277 bargc = 1;
1278 debug("batch line %s", batchline);
1279 if (batchline[0] == '\r' || batchline[0] == '\n'
1280 || batchline[0] == '#' || batchline[0] == ';')
1281 goto next_line;
1282 input = batchline;
1283 bargv[bargc] = next_token(&input, " \t\r\n");
1284 while ((bargv[bargc] != NULL) && (bargc < 14)) {
1285 bargc++;
1286 bargv[bargc] = next_token(&input, " \t\r\n");
1289 bargv[0] = argv[0];
1290 argv0 = argv[0];
1292 reorder_args(bargc, (char **)bargv);
1293 parse_args(ISC_TRUE, ISC_FALSE, bargc, (char **)bargv);
1297 * If no lookup specified, search for root
1299 if ((lookup_list.head == NULL) && !config_only) {
1300 lookup = clone_lookup(default_lookup, ISC_TRUE);
1301 lookup->trace_root = ISC_TF(lookup->trace ||
1302 lookup->ns_search_only);
1303 lookup->new_search = ISC_TRUE;
1304 strcpy(lookup->textname, ".");
1305 lookup->rdtype = dns_rdatatype_ns;
1306 lookup->rdtypeset = ISC_TRUE;
1307 if (firstarg) {
1308 printgreeting(argc, argv, lookup);
1309 firstarg = ISC_FALSE;
1311 ISC_LIST_APPEND(lookup_list, lookup, link);
1316 * Callback from dighost.c to allow program-specific shutdown code. Here,
1317 * Here, we're possibly reading from a batch file, then shutting down for
1318 * real if there's nothing in the batch file to read.
1320 void
1321 dighost_shutdown(void) {
1322 char batchline[MXNAME];
1323 int bargc;
1324 char *bargv[16];
1325 char *input;
1328 if (batchname == NULL) {
1329 isc_app_shutdown();
1330 return;
1333 fflush(stdout);
1334 if (feof(batchfp)) {
1335 batchname = NULL;
1336 isc_app_shutdown();
1337 if (batchfp != stdin)
1338 fclose(batchfp);
1339 return;
1342 if (fgets(batchline, sizeof(batchline), batchfp) != 0) {
1343 debug("batch line %s", batchline);
1344 bargc = 1;
1345 input = batchline;
1346 bargv[bargc] = next_token(&input, " \t\r\n");
1347 while ((bargv[bargc] != NULL) && (bargc < 14)) {
1348 bargc++;
1349 bargv[bargc] = next_token(&input, " \t\r\n");
1352 bargv[0] = argv0;
1354 reorder_args(bargc, (char **)bargv);
1355 parse_args(ISC_TRUE, ISC_FALSE, bargc, (char **)bargv);
1356 start_lookup();
1357 } else {
1358 batchname = NULL;
1359 if (batchfp != stdin)
1360 fclose(batchfp);
1361 isc_app_shutdown();
1362 return;
1367 main(int argc, char **argv) {
1368 isc_result_t result;
1369 dig_server_t *s, *s2;
1371 ISC_LIST_INIT(lookup_list);
1372 ISC_LIST_INIT(server_list);
1373 ISC_LIST_INIT(search_list);
1375 debug("main()");
1376 preparse_args(argc, argv);
1377 progname = argv[0];
1378 result = isc_app_start();
1379 check_result(result, "isc_app_start");
1380 setup_libs();
1381 parse_args(ISC_FALSE, ISC_FALSE, argc, argv);
1382 setup_system();
1383 if (domainopt[0] != '\0') {
1384 set_search_domain(domainopt);
1385 usesearch = ISC_TRUE;
1387 result = isc_app_onrun(mctx, global_task, onrun_callback, NULL);
1388 check_result(result, "isc_app_onrun");
1389 isc_app_run();
1390 s = ISC_LIST_HEAD(default_lookup->my_server_list);
1391 while (s != NULL) {
1392 debug("freeing server %p belonging to %p",
1393 s, default_lookup);
1394 s2 = s;
1395 s = ISC_LIST_NEXT(s, link);
1396 ISC_LIST_DEQUEUE(default_lookup->my_server_list, s2, link);
1397 isc_mem_free(mctx, s2);
1399 isc_mem_free(mctx, default_lookup);
1400 if (batchname != NULL) {
1401 if (batchfp != stdin)
1402 fclose(batchfp);
1403 batchname = NULL;
1405 cancel_all();
1406 destroy_libs();
1407 isc_app_finish();
1408 return (exitcode);