Merge from vendor branch GDB:
[dragonfly.git] / contrib / bind-9.2.4rc7 / bin / dig / dig.c
1 /*
2  * Copyright (C) 2004  Internet Systems Consortium, Inc. ("ISC")
3  * Copyright (C) 2000-2003  Internet Software Consortium.
4  *
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.
8  *
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.
16  */
17
18 /* $Id: dig.c,v 1.157.2.16 2004/06/07 03:59:08 marka Exp $ */
19
20 #include <config.h>
21 #include <stdlib.h>
22 #include <time.h>
23 #include <ctype.h>
24
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>
31
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>
42
43 #include <dig/dig.h>
44
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;
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
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;
78
79 extern isc_boolean_t debugging, memdebugging;
80 static char *batchname = NULL;
81 static FILE *batchfp = NULL;
82 static char *argv0;
83
84 static char domainopt[DNS_NAME_MAXTEXT];
85
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;
89
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"
107 };
108
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"
127 };
128
129 extern char *progname;
130
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);
137 }
138
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);
145 }
146
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);
202 }
203
204 /*
205  * Callback from dighost.c to print the received message.
206  */
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];
214
215         isc_sockaddr_format(from, fromtext, sizeof(fromtext));
216
217         result = isc_time_now(&now);
218         check_result(result, "isc_time_now");
219
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);
231
232                 }
233                 if (key != NULL) {
234                         if (!validated)
235                                 puts(";; WARNING -- Some TSIG could not "
236                                      "be validated");
237                 }
238                 if ((key == NULL) && (keysecret[0] != 0)) {
239                         puts(";; WARNING -- TSIG key was not used.");
240                 }
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);
247         }
248 }
249
250 /*
251  * Callback from dighost.c to print that it is trying a server.
252  * Not used in dig.
253  * XXX print_trying
254  */
255 void
256 trying(char *frm, dig_lookup_t *lookup) {
257         UNUSED(frm);
258         UNUSED(lookup);
259 }
260
261 /*
262  * Internal print routine used to print short form replies.
263  */
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")];
270
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, " ");
276         }
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);
288         }
289         ADD_STRING(buf, "\n");
290         return (ISC_R_SUCCESS);
291 }
292
293 /*
294  * short_form message print handler.  Calls above say_message()
295  */
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)
299 {
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;
307
308         UNUSED(flags);
309
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);
316
317         for (;;) {
318                 name = NULL;
319                 dns_message_currentname(msg, DNS_SECTION_ANSWER, &name);
320
321                 isc_buffer_init(&target, t, sizeof(t));
322
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);
334                         }
335                 }
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);
341         }
342
343         return (ISC_R_SUCCESS);
344 }
345
346 /*
347  * Callback from dighost.c to print the reply from a server
348  */
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;
356
357         if (multiline)
358                 style = &dns_master_style_default;
359         else
360                 style = &dns_master_style_debug;
361
362         if (query->lookup->cmdline[0] != 0) {
363                 if (!short_form)
364                         fputs(query->lookup->cmdline, stdout);
365                 query->lookup->cmdline[0]=0;
366         }
367         debug("printmessage(%s %s %s)", headers ? "headers" : "noheaders",
368               query->lookup->comments ? "comments" : "nocomments",
369               short_form ? "short_form" : "long_form");
370
371         flags = 0;
372         if (!headers) {
373                 flags |= DNS_MESSAGETEXTFLAG_NOHEADERS;
374                 flags |= DNS_MESSAGETEXTFLAG_NOCOMMENTS;
375         }
376         if (!query->lookup->comments)
377                 flags |= DNS_MESSAGETEXTFLAG_NOCOMMENTS;
378
379         result = ISC_R_SUCCESS;
380
381         result = isc_buffer_allocate(mctx, &buf, len);
382         check_result(result, "isc_buffer_allocate");
383
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");
391
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");
412
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]);
419                 }
420         }
421
422 repopulate_buffer:
423
424         if (query->lookup->comments && headers && !short_form)
425         {
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);
438                 }
439                 check_result(result,
440                      "dns_message_pseudosectiontotext");
441         }
442
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");
451                 }
452         }
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");
466                 }
467         }
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");
476                 }
477         }
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");
486                         /*
487                          * Only print the signature on the first record.
488                          */
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");
506                         }
507                 }
508         }
509         if (headers && query->lookup->comments && !short_form)
510                 printf("\n");
511
512         printf("%.*s", (int)isc_buffer_usedlength(buf),
513                (char *)isc_buffer_base(buf));
514         isc_buffer_free(&buf);
515         return (result);
516 }
517
518 /*
519  * print the greeting message when the program first starts up.
520  */
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];
527
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);
539                 }
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);
552                 }
553         }
554 }
555
556 /*
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.
560  */
561 static void
562 reorder_args(int argc, char *argv[]) {
563         int i, j;
564         char *ptr;
565         int end;
566
567         debug("reorder_args()");
568         end = argc - 1;
569         while (argv[end][0] == '@') {
570                 end--;
571                 if (end == 0)
572                         return;
573         }
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];
582                         }
583                         debug("moving %s to end, %d", ptr, end - 1);
584                         argv[end - 1] = ptr;
585                         end--;
586                         if (end < 1)
587                                 return;
588                 }
589         }
590 }
591
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;
596
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);
603 }
604
605 /*
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
610  */
611
612 static void
613 plus_option(char *option, isc_boolean_t is_batchfile,
614             dig_lookup_t *lookup)
615 {
616         char option_store[256];
617         char *cmd, *value, *ptr;
618         isc_boolean_t state = ISC_TRUE;
619
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;
627         }
628         value=ptr;
629         if (strncasecmp(cmd,"no",2)==0) {
630                 cmd += 2;
631                 state = ISC_FALSE;
632         }
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;
649                         }
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;
668                 }
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;
685                 }
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;
702                 }
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;
722                 }
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;
735                 }
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;
763                         }
764                         break;
765                 default:
766                         goto invalid_option;
767                 }
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;
781                 }
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;
801                         }
802                         break;
803                 case 't': /* stats */
804                         lookup->stats = state;
805                         break;
806                 default:
807                         goto invalid_option;
808                 }
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;
838                                 }
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;
852                         }
853                         break;
854                 default:
855                         goto invalid_option;
856                 }
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();
868         }
869         return;
870 }
871
872 /*
873  * ISC_TRUE returned if value was used
874  */
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)
880 {
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;
890
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;
898         }
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);
916         }
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");
932                 }
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)
968                         {
969                                 result = DNS_R_UNKNOWN;
970                         }
971                 }
972                 if (result == ISC_R_SUCCESS) {
973                         if ((*lookup)->rdtypeset) {
974                                 fprintf(stderr, ";; Warning, "
975                                                 "extra type option\n");
976                         }
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;
991                                 }
992                                 (*lookup)->ixfr_serial = ISC_FALSE;
993                         }
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();
1003                 }
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)
1016                 {
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)
1029                         {
1030                                 printgreeting(argc, argv, *lookup);
1031                                 *firstarg = ISC_FALSE;
1032                         }
1033                         ISC_LIST_APPEND(lookup_list, *lookup, link);
1034                 } else {
1035                         fprintf(stderr, "Invalid IP address %s\n", value);
1036                         exit(1);
1037                 }
1038                 return (value_from_next);
1039         invalid_option:
1040         default:
1041                 fprintf(stderr, "Invalid option: -%s\n", option);
1042                 usage();
1043         }
1044         return (ISC_FALSE);
1045 }
1046
1047 /*
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.
1054  */
1055 static void
1056 preparse_args(int argc, char **argv) {
1057         int rc;
1058         char **rv;
1059
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;
1068                 }
1069         }
1070 }
1071
1072
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;
1094
1095         /*
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.
1101          *
1102          * First, create a "default lookup" which won't actually be used
1103          * anywhere, except for cloning into new lookups
1104          */
1105
1106         debug("parse_args()");
1107         if (!is_batchfile) {
1108                 debug("making new lookup");
1109                 default_lookup = make_empty_lookup();
1110
1111 #ifndef NOPOSIX
1112                 /*
1113                  * Treat .digrc as a special batchfile
1114                  */
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");
1132                                 }
1133
1134                                 bargv[0] = argv[0];
1135                                 argv0 = argv[0];
1136
1137                                 reorder_args(bargc, (char **)bargv);
1138                                 parse_args(ISC_TRUE, ISC_TRUE, bargc,
1139                                            (char **)bargv);
1140                         }
1141                         fclose(batchfp);
1142                 }
1143 #endif
1144         }
1145
1146         lookup = default_lookup;
1147
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++;
1168                                 }
1169                         } else {
1170                                 if (dash_option(&rv[0][1], rv[1],
1171                                                 &lookup, &open_type_class,
1172                                                 &firstarg, argc, argv)) {
1173                                         rc--;
1174                                         rv++;
1175                                 }
1176                         }
1177                 } else {
1178                         /*
1179                          * Anything which isn't an option
1180                          */
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)
1192                                         {
1193                                                 result = DNS_R_UNKNOWN;
1194                                                 fprintf(stderr, ";; Warning, "
1195                                                         "ixfr requires a "
1196                                                         "serial number\n");
1197                                                 continue;
1198                                         }
1199                                 }
1200                                 if (result == ISC_R_SUCCESS)
1201                                 {
1202                                         if (lookup->rdtypeset) {
1203                                                 fprintf(stderr, ";; Warning, "
1204                                                         "extra type option\n");
1205                                         }
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;
1222                                                 }
1223                                                 lookup->ixfr_serial = ISC_FALSE;
1224                                         }
1225                                         continue;
1226                                 }
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");
1233                                         }
1234                                         lookup->rdclass = rdclass;
1235                                         lookup->rdclassset = ISC_TRUE;
1236                                         continue;
1237                                 }
1238                         }
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;
1245                                 }
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);
1254                         }
1255                         /* XXX Error message */
1256                 }
1257         }
1258         /*
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
1262          */
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");
1273                 }
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");
1287                         }
1288
1289                         bargv[0] = argv[0];
1290                         argv0 = argv[0];
1291
1292                         reorder_args(bargc, (char **)bargv);
1293                         parse_args(ISC_TRUE, ISC_FALSE, bargc, (char **)bargv);
1294                 }
1295         }
1296         /*
1297          * If no lookup specified, search for root
1298          */
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;
1310                 }
1311                 ISC_LIST_APPEND(lookup_list, lookup, link);
1312         }
1313 }
1314
1315 /*
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.
1319  */
1320 void
1321 dighost_shutdown(void) {
1322         char batchline[MXNAME];
1323         int bargc;
1324         char *bargv[16];
1325         char *input;
1326
1327
1328         if (batchname == NULL) {
1329                 isc_app_shutdown();
1330                 return;
1331         }
1332
1333         fflush(stdout);
1334         if (feof(batchfp)) {
1335                 batchname = NULL;
1336                 isc_app_shutdown();
1337                 if (batchfp != stdin)
1338                         fclose(batchfp);
1339                 return;
1340         }
1341
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");
1350                 }
1351
1352                 bargv[0] = argv0;
1353
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;
1363         }
1364 }
1365
1366 int
1367 main(int argc, char **argv) {
1368         isc_result_t result;
1369         dig_server_t *s, *s2;
1370
1371         ISC_LIST_INIT(lookup_list);
1372         ISC_LIST_INIT(server_list);
1373         ISC_LIST_INIT(search_list);
1374
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;
1386         }
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);
1398         }
1399         isc_mem_free(mctx, default_lookup);
1400         if (batchname != NULL) {
1401                 if (batchfp != stdin)
1402                         fclose(batchfp);
1403                 batchname = NULL;
1404         }
1405         cancel_all();
1406         destroy_libs();
1407         isc_app_finish();
1408         return (exitcode);
1409 }