Merge from vendor branch LIBARCHIVE:
[dragonfly.git] / contrib / bind-9.3 / bin / dig / dig.c
1 /*
2  * Copyright (C) 2004-2006  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.13.2.31 2006/07/22 23:52:57 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/parseint.h>
28 #include <isc/print.h>
29 #include <isc/string.h>
30 #include <isc/util.h>
31 #include <isc/task.h>
32
33 #include <dns/byaddr.h>
34 #include <dns/fixedname.h>
35 #include <dns/masterdump.h>
36 #include <dns/message.h>
37 #include <dns/name.h>
38 #include <dns/rdata.h>
39 #include <dns/rdataset.h>
40 #include <dns/rdatatype.h>
41 #include <dns/rdataclass.h>
42 #include <dns/result.h>
43
44 #include <bind9/getaddresses.h>
45
46 #include <dig/dig.h>
47
48 #define ADD_STRING(b, s) {                              \
49         if (strlen(s) >= isc_buffer_availablelength(b)) \
50                 return (ISC_R_NOSPACE);                 \
51         else                                            \
52                 isc_buffer_putstr(b, s);                \
53 }
54
55 #define DIG_MAX_ADDRESSES 20
56
57 dig_lookup_t *default_lookup = NULL;
58
59 static char *batchname = NULL;
60 static FILE *batchfp = NULL;
61 static char *argv0;
62 static int addresscount = 0;
63
64 static char domainopt[DNS_NAME_MAXTEXT];
65
66 static isc_boolean_t short_form = ISC_FALSE, printcmd = ISC_TRUE,
67         ip6_int = ISC_FALSE, plusquest = ISC_FALSE, pluscomm = ISC_FALSE,
68         multiline = ISC_FALSE, nottl = ISC_FALSE, noclass = ISC_FALSE;
69
70 static const char *opcodetext[] = {
71         "QUERY",
72         "IQUERY",
73         "STATUS",
74         "RESERVED3",
75         "NOTIFY",
76         "UPDATE",
77         "RESERVED6",
78         "RESERVED7",
79         "RESERVED8",
80         "RESERVED9",
81         "RESERVED10",
82         "RESERVED11",
83         "RESERVED12",
84         "RESERVED13",
85         "RESERVED14",
86         "RESERVED15"
87 };
88
89 static const char *rcodetext[] = {
90         "NOERROR",
91         "FORMERR",
92         "SERVFAIL",
93         "NXDOMAIN",
94         "NOTIMP",
95         "REFUSED",
96         "YXDOMAIN",
97         "YXRRSET",
98         "NXRRSET",
99         "NOTAUTH",
100         "NOTZONE",
101         "RESERVED11",
102         "RESERVED12",
103         "RESERVED13",
104         "RESERVED14",
105         "RESERVED15",
106         "BADVERS"
107 };
108
109 static void
110 print_usage(FILE *fp) {
111         fputs(
112 "Usage:  dig [@global-server] [domain] [q-type] [q-class] {q-opt}\n"
113 "            {global-d-opt} host [@local-server] {local-d-opt}\n"
114 "            [ host [@local-server] {local-d-opt} [...]]\n", fp);
115 }
116
117 static void
118 usage(void) {
119         print_usage(stderr);
120         fputs("\nUse \"dig -h\" (or \"dig -h | more\") "
121               "for complete list of options\n", stderr);
122         exit(1);
123 }
124
125 static void
126 version(void) {
127         fputs("DiG " VERSION "\n", stderr);
128 }
129
130 static void
131 help(void) {
132         print_usage(stdout);
133         fputs(
134 "Where:  domain   is in the Domain Name System\n"
135 "        q-class  is one of (in,hs,ch,...) [default: in]\n"
136 "        q-type   is one of (a,any,mx,ns,soa,hinfo,axfr,txt,...) [default:a]\n"
137 "                 (Use ixfr=version for type ixfr)\n"
138 "        q-opt    is one of:\n"
139 "                 -x dot-notation     (shortcut for in-addr lookups)\n"
140 "                 -i                  (IP6.INT reverse IPv6 lookups)\n"
141 "                 -f filename         (batch mode)\n"
142 "                 -b address[#port]   (bind to source address/port)\n"
143 "                 -p port             (specify port number)\n"
144 "                 -t type             (specify query type)\n"
145 "                 -c class            (specify query class)\n"
146 "                 -k keyfile          (specify tsig key file)\n"
147 "                 -y name:key         (specify named base64 tsig key)\n"
148 "                 -4                  (use IPv4 query transport only)\n"
149 "                 -6                  (use IPv6 query transport only)\n"
150 "        d-opt    is of the form +keyword[=value], where keyword is:\n"
151 "                 +[no]vc             (TCP mode)\n"
152 "                 +[no]tcp            (TCP mode, alternate syntax)\n"
153 "                 +time=###           (Set query timeout) [5]\n"
154 "                 +tries=###          (Set number of UDP attempts) [3]\n"
155 "                 +retry=###          (Set number of UDP retries) [2]\n"
156 "                 +domain=###         (Set default domainname)\n"
157 "                 +bufsize=###        (Set EDNS0 Max UDP packet size)\n"
158 "                 +ndots=###          (Set NDOTS value)\n"
159 "                 +[no]search         (Set whether to use searchlist)\n"
160 "                 +[no]defname        (Ditto)\n"
161 "                 +[no]recurse        (Recursive mode)\n"
162 "                 +[no]ignore         (Don't revert to TCP for TC responses.)"
163 "\n"
164 "                 +[no]fail           (Don't try next server on SERVFAIL)\n"
165 "                 +[no]besteffort     (Try to parse even illegal messages)\n"
166 "                 +[no]aaonly         (Set AA flag in query (+[no]aaflag))\n"
167 "                 +[no]adflag         (Set AD flag in query)\n"
168 "                 +[no]cdflag         (Set CD flag in query)\n"
169 "                 +[no]cl             (Control display of class in records)\n"
170 "                 +[no]cmd            (Control display of command line)\n"
171 "                 +[no]comments       (Control display of comment lines)\n"
172 "                 +[no]question       (Control display of question)\n"
173 "                 +[no]answer         (Control display of answer)\n"
174 "                 +[no]authority      (Control display of authority)\n"
175 "                 +[no]additional     (Control display of additional)\n"
176 "                 +[no]stats          (Control display of statistics)\n"
177 "                 +[no]short          (Disable everything except short\n"
178 "                                      form of answer)\n"
179 "                 +[no]ttlid          (Control display of ttls in records)\n"
180 "                 +[no]all            (Set or clear all display flags)\n"
181 "                 +[no]qr             (Print question before sending)\n"
182 "                 +[no]nssearch       (Search all authoritative nameservers)\n"
183 "                 +[no]identify       (ID responders in short answers)\n"
184 "                 +[no]trace          (Trace delegation down from root)\n"
185 "                 +[no]dnssec         (Request DNSSEC records)\n"
186 #ifdef DIG_SIGCHASE
187 "                 +[no]sigchase       (Chase DNSSEC signatures)\n"
188 "                 +trusted-key=####   (Trusted Key when chasing DNSSEC sigs)\n"
189 #if DIG_SIGCHASE_TD
190 "                 +[no]topdown        (Do DNSSEC validation top down mode)\n"
191 #endif
192 #endif
193 "                 +[no]multiline      (Print records in an expanded format)\n"
194 "        global d-opts and servers (before host name) affect all queries.\n"
195 "        local d-opts and servers (after host name) affect only that lookup.\n"
196 "        -h                           (print help and exit)\n"
197 "        -v                           (print version and exit)\n",
198         stdout);
199 }
200
201 /*
202  * Callback from dighost.c to print the received message.
203  */
204 void
205 received(int bytes, isc_sockaddr_t *from, dig_query_t *query) {
206         isc_uint64_t diff;
207         isc_time_t now;
208         time_t tnow;
209         char fromtext[ISC_SOCKADDR_FORMATSIZE];
210
211         isc_sockaddr_format(from, fromtext, sizeof(fromtext));
212
213         TIME_NOW(&now);
214
215         if (query->lookup->stats && !short_form) {
216                 diff = isc_time_microdiff(&now, &query->time_sent);
217                 printf(";; Query time: %ld msec\n", (long int)diff/1000);
218                 printf(";; SERVER: %s(%s)\n", fromtext, query->servname);
219                 time(&tnow);
220                 printf(";; WHEN: %s", ctime(&tnow));
221                 if (query->lookup->doing_xfr) {
222                         printf(";; XFR size: %u records (messages %u)\n",
223                                query->rr_count, query->msg_count);
224                 } else {
225                         printf(";; MSG SIZE  rcvd: %d\n", bytes);
226
227                 }
228                 if (key != NULL) {
229                         if (!validated)
230                                 puts(";; WARNING -- Some TSIG could not "
231                                      "be validated");
232                 }
233                 if ((key == NULL) && (keysecret[0] != 0)) {
234                         puts(";; WARNING -- TSIG key was not used.");
235                 }
236                 puts("");
237         } else if (query->lookup->identify && !short_form) {
238                 diff = isc_time_microdiff(&now, &query->time_sent);
239                 printf(";; Received %u bytes from %s(%s) in %d ms\n\n",
240                        bytes, fromtext, query->servname,
241                        (int)diff/1000);
242         }
243 }
244
245 /*
246  * Callback from dighost.c to print that it is trying a server.
247  * Not used in dig.
248  * XXX print_trying
249  */
250 void
251 trying(char *frm, dig_lookup_t *lookup) {
252         UNUSED(frm);
253         UNUSED(lookup);
254 }
255
256 /*
257  * Internal print routine used to print short form replies.
258  */
259 static isc_result_t
260 say_message(dns_rdata_t *rdata, dig_query_t *query, isc_buffer_t *buf) {
261         isc_result_t result;
262         isc_uint64_t diff;
263         isc_time_t now;
264         char store[sizeof("12345678901234567890")];
265
266         if (query->lookup->trace || query->lookup->ns_search_only) {
267                 result = dns_rdatatype_totext(rdata->type, buf);
268                 if (result != ISC_R_SUCCESS)
269                         return (result);
270                 ADD_STRING(buf, " ");
271         }
272         result = dns_rdata_totext(rdata, NULL, buf);
273         check_result(result, "dns_rdata_totext");
274         if (query->lookup->identify) {
275                 TIME_NOW(&now);
276                 diff = isc_time_microdiff(&now, &query->time_sent);
277                 ADD_STRING(buf, " from server ");
278                 ADD_STRING(buf, query->servname);
279                 snprintf(store, 19, " in %d ms.", (int)diff/1000);
280                 ADD_STRING(buf, store);
281         }
282         ADD_STRING(buf, "\n");
283         return (ISC_R_SUCCESS);
284 }
285
286 /*
287  * short_form message print handler.  Calls above say_message()
288  */
289 static isc_result_t
290 short_answer(dns_message_t *msg, dns_messagetextflag_t flags,
291              isc_buffer_t *buf, dig_query_t *query)
292 {
293         dns_name_t *name;
294         dns_rdataset_t *rdataset;
295         isc_buffer_t target;
296         isc_result_t result, loopresult;
297         dns_name_t empty_name;
298         char t[4096];
299         dns_rdata_t rdata = DNS_RDATA_INIT;
300
301         UNUSED(flags);
302
303         dns_name_init(&empty_name, NULL);
304         result = dns_message_firstname(msg, DNS_SECTION_ANSWER);
305         if (result == ISC_R_NOMORE)
306                 return (ISC_R_SUCCESS);
307         else if (result != ISC_R_SUCCESS)
308                 return (result);
309
310         for (;;) {
311                 name = NULL;
312                 dns_message_currentname(msg, DNS_SECTION_ANSWER, &name);
313
314                 isc_buffer_init(&target, t, sizeof(t));
315
316                 for (rdataset = ISC_LIST_HEAD(name->list);
317                      rdataset != NULL;
318                      rdataset = ISC_LIST_NEXT(rdataset, link)) {
319                         loopresult = dns_rdataset_first(rdataset);
320                         while (loopresult == ISC_R_SUCCESS) {
321                                 dns_rdataset_current(rdataset, &rdata);
322                                 result = say_message(&rdata, query,
323                                                      buf);
324                                 check_result(result, "say_message");
325                                 loopresult = dns_rdataset_next(rdataset);
326                                 dns_rdata_reset(&rdata);
327                         }
328                 }
329                 result = dns_message_nextname(msg, DNS_SECTION_ANSWER);
330                 if (result == ISC_R_NOMORE)
331                         break;
332                 else if (result != ISC_R_SUCCESS)
333                         return (result);
334         }
335
336         return (ISC_R_SUCCESS);
337 }
338 #ifdef DIG_SIGCHASE
339 isc_result_t
340 printrdataset(dns_name_t *owner_name, dns_rdataset_t *rdataset,
341               isc_buffer_t *target)
342 {
343         isc_result_t result;
344         dns_master_style_t *style = NULL;
345         unsigned int styleflags = 0;
346
347         if (rdataset == NULL || owner_name == NULL || target == NULL)
348                 return(ISC_FALSE);
349
350         styleflags |= DNS_STYLEFLAG_REL_OWNER;
351         if (nottl)
352                 styleflags |= DNS_STYLEFLAG_NO_TTL;
353         if (noclass)
354                 styleflags |= DNS_STYLEFLAG_NO_CLASS;
355         if (multiline) {
356                 styleflags |= DNS_STYLEFLAG_OMIT_OWNER;
357                 styleflags |= DNS_STYLEFLAG_OMIT_CLASS;
358                 styleflags |= DNS_STYLEFLAG_REL_DATA;
359                 styleflags |= DNS_STYLEFLAG_OMIT_TTL;
360                 styleflags |= DNS_STYLEFLAG_TTL;
361                 styleflags |= DNS_STYLEFLAG_MULTILINE;
362                 styleflags |= DNS_STYLEFLAG_COMMENT;
363         }
364         if (multiline || (nottl && noclass))
365                 result = dns_master_stylecreate(&style, styleflags,
366                                                 24, 24, 24, 32, 80, 8, mctx);
367         else if (nottl || noclass)
368                 result = dns_master_stylecreate(&style, styleflags,
369                                                 24, 24, 32, 40, 80, 8, mctx);
370         else 
371                 result = dns_master_stylecreate(&style, styleflags,
372                                                 24, 32, 40, 48, 80, 8, mctx);
373         check_result(result, "dns_master_stylecreate");
374
375         result = dns_master_rdatasettotext(owner_name, rdataset, style, target);
376
377         if (style != NULL)
378                 dns_master_styledestroy(&style, mctx);
379   
380         return(result);
381 }
382 #endif
383
384 /*
385  * Callback from dighost.c to print the reply from a server
386  */
387 isc_result_t
388 printmessage(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers) {
389         isc_result_t result;
390         dns_messagetextflag_t flags;
391         isc_buffer_t *buf = NULL;
392         unsigned int len = OUTPUTBUF;
393         dns_master_style_t *style = NULL;
394         unsigned int styleflags = 0;
395
396         styleflags |= DNS_STYLEFLAG_REL_OWNER;
397         if (nottl)
398                 styleflags |= DNS_STYLEFLAG_NO_TTL;
399         if (noclass)
400                 styleflags |= DNS_STYLEFLAG_NO_CLASS;
401         if (multiline) {
402                 styleflags |= DNS_STYLEFLAG_OMIT_OWNER;
403                 styleflags |= DNS_STYLEFLAG_OMIT_CLASS;
404                 styleflags |= DNS_STYLEFLAG_REL_DATA;
405                 styleflags |= DNS_STYLEFLAG_OMIT_TTL;
406                 styleflags |= DNS_STYLEFLAG_TTL;
407                 styleflags |= DNS_STYLEFLAG_MULTILINE;
408                 styleflags |= DNS_STYLEFLAG_COMMENT;
409         }
410         if (multiline || (nottl && noclass))
411                 result = dns_master_stylecreate(&style, styleflags,
412                                                 24, 24, 24, 32, 80, 8, mctx);
413         else if (nottl || noclass)
414                 result = dns_master_stylecreate(&style, styleflags,
415                                                 24, 24, 32, 40, 80, 8, mctx);
416         else 
417                 result = dns_master_stylecreate(&style, styleflags,
418                                                 24, 32, 40, 48, 80, 8, mctx);
419         check_result(result, "dns_master_stylecreate");
420
421         if (query->lookup->cmdline[0] != 0) {
422                 if (!short_form)
423                         fputs(query->lookup->cmdline, stdout);
424                 query->lookup->cmdline[0]=0;
425         }
426         debug("printmessage(%s %s %s)", headers ? "headers" : "noheaders",
427               query->lookup->comments ? "comments" : "nocomments",
428               short_form ? "short_form" : "long_form");
429
430         flags = 0;
431         if (!headers) {
432                 flags |= DNS_MESSAGETEXTFLAG_NOHEADERS;
433                 flags |= DNS_MESSAGETEXTFLAG_NOCOMMENTS;
434         }
435         if (!query->lookup->comments)
436                 flags |= DNS_MESSAGETEXTFLAG_NOCOMMENTS;
437
438         result = ISC_R_SUCCESS;
439
440         result = isc_buffer_allocate(mctx, &buf, len);
441         check_result(result, "isc_buffer_allocate");
442
443         if (query->lookup->comments && !short_form) {
444                 if (query->lookup->cmdline[0] != 0)
445                         printf("; %s\n", query->lookup->cmdline);
446                 if (msg == query->lookup->sendmsg)
447                         printf(";; Sending:\n");
448                 else
449                         printf(";; Got answer:\n");
450
451                 if (headers) {
452                         printf(";; ->>HEADER<<- opcode: %s, status: %s, "
453                                "id: %u\n",
454                                opcodetext[msg->opcode], rcodetext[msg->rcode],
455                                msg->id);
456                         printf(";; flags:");
457                         if ((msg->flags & DNS_MESSAGEFLAG_QR) != 0)
458                                 printf(" qr");
459                         if ((msg->flags & DNS_MESSAGEFLAG_AA) != 0)
460                                 printf(" aa");
461                         if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0)
462                                 printf(" tc");
463                         if ((msg->flags & DNS_MESSAGEFLAG_RD) != 0)
464                                 printf(" rd");
465                         if ((msg->flags & DNS_MESSAGEFLAG_RA) != 0)
466                                 printf(" ra");
467                         if ((msg->flags & DNS_MESSAGEFLAG_AD) != 0)
468                                 printf(" ad");
469                         if ((msg->flags & DNS_MESSAGEFLAG_CD) != 0)
470                                 printf(" cd");
471
472                         printf("; QUERY: %u, ANSWER: %u, "
473                                "AUTHORITY: %u, ADDITIONAL: %u\n",
474                                msg->counts[DNS_SECTION_QUESTION],
475                                msg->counts[DNS_SECTION_ANSWER],
476                                msg->counts[DNS_SECTION_AUTHORITY],
477                                msg->counts[DNS_SECTION_ADDITIONAL]);
478                 }
479         }
480
481 repopulate_buffer:
482
483         if (query->lookup->comments && headers && !short_form) {
484                 result = dns_message_pseudosectiontotext(msg,
485                          DNS_PSEUDOSECTION_OPT,
486                          style, flags, buf);
487                 if (result == ISC_R_NOSPACE) {
488 buftoosmall:
489                         len += OUTPUTBUF;
490                         isc_buffer_free(&buf);
491                         result = isc_buffer_allocate(mctx, &buf, len);
492                         if (result == ISC_R_SUCCESS)
493                                 goto repopulate_buffer;
494                         else
495                                 goto cleanup;
496                 }
497                 check_result(result,
498                      "dns_message_pseudosectiontotext");
499         }
500
501         if (query->lookup->section_question && headers) {
502                 if (!short_form) {
503                         result = dns_message_sectiontotext(msg,
504                                                        DNS_SECTION_QUESTION,
505                                                        style, flags, buf);
506                         if (result == ISC_R_NOSPACE)
507                                 goto buftoosmall;
508                         check_result(result, "dns_message_sectiontotext");
509                 }
510         }
511         if (query->lookup->section_answer) {
512                 if (!short_form) {
513                         result = dns_message_sectiontotext(msg,
514                                                        DNS_SECTION_ANSWER,
515                                                        style, flags, buf);
516                         if (result == ISC_R_NOSPACE)
517                                 goto buftoosmall;
518                         check_result(result, "dns_message_sectiontotext");
519                 } else {
520                         result = short_answer(msg, flags, buf, query);
521                         if (result == ISC_R_NOSPACE)
522                                 goto buftoosmall;
523                         check_result(result, "short_answer");
524                 }
525         }
526         if (query->lookup->section_authority) {
527                 if (!short_form) {
528                         result = dns_message_sectiontotext(msg,
529                                                        DNS_SECTION_AUTHORITY,
530                                                        style, flags, buf);
531                         if (result == ISC_R_NOSPACE)
532                                 goto buftoosmall;
533                         check_result(result, "dns_message_sectiontotext");
534                 }
535         }
536         if (query->lookup->section_additional) {
537                 if (!short_form) {
538                         result = dns_message_sectiontotext(msg,
539                                                       DNS_SECTION_ADDITIONAL,
540                                                       style, flags, buf);
541                         if (result == ISC_R_NOSPACE)
542                                 goto buftoosmall;
543                         check_result(result, "dns_message_sectiontotext");
544                         /*
545                          * Only print the signature on the first record.
546                          */
547                         if (headers) {
548                                 result = dns_message_pseudosectiontotext(
549                                                    msg,
550                                                    DNS_PSEUDOSECTION_TSIG,
551                                                    style, flags, buf);
552                                 if (result == ISC_R_NOSPACE)
553                                         goto buftoosmall;
554                                 check_result(result,
555                                           "dns_message_pseudosectiontotext");
556                                 result = dns_message_pseudosectiontotext(
557                                                    msg,
558                                                    DNS_PSEUDOSECTION_SIG0,
559                                                    style, flags, buf);
560                                 if (result == ISC_R_NOSPACE)
561                                         goto buftoosmall;
562                                 check_result(result,
563                                            "dns_message_pseudosectiontotext");
564                         }
565                 }
566         }
567
568         if (headers && query->lookup->comments && !short_form)
569                 printf("\n");
570
571         printf("%.*s", (int)isc_buffer_usedlength(buf),
572                (char *)isc_buffer_base(buf));
573         isc_buffer_free(&buf);
574
575 cleanup:
576         if (style != NULL)
577                 dns_master_styledestroy(&style, mctx);
578         return (result);
579 }
580
581 /*
582  * print the greeting message when the program first starts up.
583  */
584 static void
585 printgreeting(int argc, char **argv, dig_lookup_t *lookup) {
586         int i;
587         int remaining;
588         static isc_boolean_t first = ISC_TRUE;
589         char append[MXNAME];
590
591         if (printcmd) {
592                 lookup->cmdline[sizeof(lookup->cmdline) - 1] = 0;
593                 snprintf(lookup->cmdline, sizeof(lookup->cmdline),
594                          "%s; <<>> DiG " VERSION " <<>>",
595                          first?"\n":"");
596                 i = 1;
597                 while (i < argc) {
598                         snprintf(append, sizeof(append), " %s", argv[i++]);
599                         remaining = sizeof(lookup->cmdline) -
600                                     strlen(lookup->cmdline) - 1;
601                         strncat(lookup->cmdline, append, remaining);
602                 }
603                 remaining = sizeof(lookup->cmdline) -
604                             strlen(lookup->cmdline) - 1;
605                 strncat(lookup->cmdline, "\n", remaining);
606                 if (first && addresscount != 0) {
607                         snprintf(append, sizeof(append),
608                                  "; (%d server%s found)\n",
609                                  addresscount,
610                                  addresscount > 1 ? "s" : "");
611                         remaining = sizeof(lookup->cmdline) -
612                                     strlen(lookup->cmdline) - 1;
613                         strncat(lookup->cmdline, append, remaining);
614                 }
615                 if (first) {
616                         snprintf(append, sizeof(append), 
617                                  ";; global options: %s %s\n",
618                                short_form ? "short_form" : "",
619                                printcmd ? "printcmd" : "");
620                         first = ISC_FALSE;
621                         remaining = sizeof(lookup->cmdline) -
622                                     strlen(lookup->cmdline) - 1;
623                         strncat(lookup->cmdline, append, remaining);
624                 }
625         }
626 }
627
628 /*
629  * Reorder an argument list so that server names all come at the end.
630  * This is a bit of a hack, to allow batch-mode processing to properly
631  * handle the server options.
632  */
633 static void
634 reorder_args(int argc, char *argv[]) {
635         int i, j;
636         char *ptr;
637         int end;
638
639         debug("reorder_args()");
640         end = argc - 1;
641         while (argv[end][0] == '@') {
642                 end--;
643                 if (end == 0)
644                         return;
645         }
646         debug("arg[end]=%s", argv[end]);
647         for (i = 1; i < end - 1; i++) {
648                 if (argv[i][0] == '@') {
649                         debug("arg[%d]=%s", i, argv[i]);
650                         ptr = argv[i];
651                         for (j = i + 1; j < end; j++) {
652                                 debug("Moving %s to %d", argv[j], j - 1);
653                                 argv[j - 1] = argv[j];
654                         }
655                         debug("moving %s to end, %d", ptr, end - 1);
656                         argv[end - 1] = ptr;
657                         end--;
658                         if (end < 1)
659                                 return;
660                 }
661         }
662 }
663
664 static isc_uint32_t
665 parse_uint(char *arg, const char *desc, isc_uint32_t max) {
666         isc_result_t result;
667         isc_uint32_t tmp;
668
669         result = isc_parse_uint32(&tmp, arg, 10);
670         if (result == ISC_R_SUCCESS && tmp > max)
671                 result = ISC_R_RANGE;
672         if (result != ISC_R_SUCCESS)
673                 fatal("%s '%s': %s", desc, arg, isc_result_totext(result));
674         return (tmp);
675 }
676
677 /*
678  * We're not using isc_commandline_parse() here since the command line
679  * syntax of dig is quite a bit different from that which can be described
680  * by that routine.
681  * XXX doc options
682  */
683
684 static void
685 plus_option(char *option, isc_boolean_t is_batchfile,
686             dig_lookup_t *lookup)
687 {
688         char option_store[256];
689         char *cmd, *value, *ptr;
690         isc_boolean_t state = ISC_TRUE;
691 #ifdef DIG_SIGCHASE
692         size_t n;
693 #endif
694
695         strncpy(option_store, option, sizeof(option_store));
696         option_store[sizeof(option_store)-1]=0;
697         ptr = option_store;
698         cmd = next_token(&ptr,"=");
699         if (cmd == NULL) {
700                 printf(";; Invalid option %s\n", option_store);
701                 return;
702         }
703         value = ptr;
704         if (strncasecmp(cmd, "no", 2)==0) {
705                 cmd += 2;
706                 state = ISC_FALSE;
707         }
708
709 #define FULLCHECK(A) \
710         do { \
711                 size_t _l = strlen(cmd); \
712                 if (_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) \
713                         goto invalid_option; \
714         } while (0)
715 #define FULLCHECK2(A, B) \
716         do { \
717                 size_t _l = strlen(cmd); \
718                 if ((_l >= sizeof(A) || strncasecmp(cmd, A, _l) != 0) && \
719                     (_l >= sizeof(B) || strncasecmp(cmd, B, _l) != 0)) \
720                         goto invalid_option; \
721         } while (0)
722
723         switch (cmd[0]) {
724         case 'a':
725                 switch (cmd[1]) {
726                 case 'a': /* aaonly / aaflag */
727                         FULLCHECK2("aaonly", "aaflag");
728                         lookup->aaonly = state;
729                         break;
730                 case 'd': 
731                         switch (cmd[2]) {
732                         case 'd': /* additional */
733                                 FULLCHECK("additional");
734                                 lookup->section_additional = state;
735                                 break;
736                         case 'f': /* adflag */
737                                 FULLCHECK("adflag");
738                                 lookup->adflag = state;
739                                 break;
740                         default:
741                                 goto invalid_option;
742                         }
743                         break;
744                 case 'l': /* all */
745                         FULLCHECK("all");
746                         lookup->section_question = state;
747                         lookup->section_authority = state;
748                         lookup->section_answer = state;
749                         lookup->section_additional = state;
750                         lookup->comments = state;
751                         lookup->stats = state;
752                         printcmd = state;
753                         break;
754                 case 'n': /* answer */
755                         FULLCHECK("answer");
756                         lookup->section_answer = state;
757                         break;
758                 case 'u': /* authority */
759                         FULLCHECK("authority");
760                         lookup->section_authority = state;
761                         break;
762                 default:
763                         goto invalid_option;
764                 }
765                 break;
766         case 'b':
767                 switch (cmd[1]) {
768                 case 'e':/* besteffort */
769                         FULLCHECK("besteffort");
770                         lookup->besteffort = state;
771                         break;
772                 case 'u':/* bufsize */
773                         FULLCHECK("bufsize");
774                         if (value == NULL)
775                                 goto need_value;
776                         if (!state)
777                                 goto invalid_option;
778                         lookup->udpsize = (isc_uint16_t) parse_uint(value,
779                                                     "buffer size", COMMSIZE);
780                         break;
781                 default:
782                         goto invalid_option;
783                 }
784                 break;
785         case 'c':
786                 switch (cmd[1]) {
787                 case 'd':/* cdflag */
788                         FULLCHECK("cdflag");
789                         lookup->cdflag = state;
790                         break;
791                 case 'l': /* cl */
792                         FULLCHECK("cl");
793                         noclass = ISC_TF(!state);
794                         break;
795                 case 'm': /* cmd */
796                         FULLCHECK("cmd");
797                         printcmd = state;
798                         break;
799                 case 'o': /* comments */
800                         FULLCHECK("comments");
801                         lookup->comments = state;
802                         if (lookup == default_lookup)
803                                 pluscomm = state;
804                         break;
805                 default:
806                         goto invalid_option;
807                 }
808                 break;
809         case 'd':
810                 switch (cmd[1]) {
811                 case 'e': /* defname */
812                         FULLCHECK("defname");
813                         usesearch = state;
814                         break;
815                 case 'n': /* dnssec */  
816                         FULLCHECK("dnssec");
817                         lookup->dnssec = state;
818                         break;
819                 case 'o': /* domain */  
820                         FULLCHECK("domain");
821                         if (value == NULL)
822                                 goto need_value;
823                         if (!state)
824                                 goto invalid_option;
825                         strncpy(domainopt, value, sizeof(domainopt));
826                         domainopt[sizeof(domainopt)-1] = '\0';
827                         break;
828                 default:
829                         goto invalid_option;
830                 }
831                 break;
832         case 'f': /* fail */
833                 FULLCHECK("fail");
834                 lookup->servfail_stops = state;
835                 break;
836         case 'i':
837                 switch (cmd[1]) {
838                 case 'd': /* identify */
839                         FULLCHECK("identify");
840                         lookup->identify = state;
841                         break;
842                 case 'g': /* ignore */
843                 default: /* Inherets default for compatibility */
844                         FULLCHECK("ignore");
845                         lookup->ignore = ISC_TRUE;
846                 }
847                 break;
848         case 'm': /* multiline */
849                 FULLCHECK("multiline");
850                 multiline = state;
851                 break;
852         case 'n':
853                 switch (cmd[1]) {
854                 case 'd': /* ndots */
855                         FULLCHECK("ndots");
856                         if (value == NULL)
857                                 goto need_value;
858                         if (!state)
859                                 goto invalid_option;
860                         ndots = parse_uint(value, "ndots", MAXNDOTS);
861                         break;
862                 case 's': /* nssearch */
863                         FULLCHECK("nssearch");
864                         lookup->ns_search_only = state;
865                         if (state) {
866                                 lookup->trace_root = ISC_TRUE;
867                                 lookup->recurse = ISC_TRUE;
868                                 lookup->identify = ISC_TRUE;
869                                 lookup->stats = ISC_FALSE;
870                                 lookup->comments = ISC_FALSE;
871                                 lookup->section_additional = ISC_FALSE;
872                                 lookup->section_authority = ISC_FALSE;
873                                 lookup->section_question = ISC_FALSE;
874                                 lookup->rdtype = dns_rdatatype_ns;
875                                 lookup->rdtypeset = ISC_TRUE;
876                                 short_form = ISC_TRUE;
877                         }
878                         break;
879                 default:
880                         goto invalid_option;
881                 }
882                 break;
883         case 'q': 
884                 switch (cmd[1]) {
885                 case 'r': /* qr */
886                         FULLCHECK("qr");
887                         qr = state;
888                         break;
889                 case 'u': /* question */
890                         FULLCHECK("question");
891                         lookup->section_question = state;
892                         if (lookup == default_lookup)
893                                 plusquest = state;
894                         break;
895                 default:
896                         goto invalid_option;
897                 }
898                 break;
899         case 'r':
900                 switch (cmd[1]) {
901                 case 'e':
902                         switch (cmd[2]) {
903                         case 'c': /* recurse */
904                                 FULLCHECK("recurse");
905                                 lookup->recurse = state;
906                                 break;
907                         case 't': /* retry / retries */
908                                 FULLCHECK2("retry", "retries");
909                                 if (value == NULL)
910                                         goto need_value;
911                                 if (!state)
912                                         goto invalid_option;
913                                 lookup->retries = parse_uint(value, "retries",
914                                                        MAXTRIES - 1);
915                                 lookup->retries++;
916                                 break;
917                         default:
918                                 goto invalid_option;
919                         }
920                         break;
921                 default:
922                         goto invalid_option;
923                 }
924                 break;
925         case 's':
926                 switch (cmd[1]) {
927                 case 'e': /* search */
928                         FULLCHECK("search");
929                         usesearch = state;
930                         break;
931                 case 'h': /* short */
932                         FULLCHECK("short");
933                         short_form = state;
934                         if (state) {
935                                 printcmd = ISC_FALSE;
936                                 lookup->section_additional = ISC_FALSE;
937                                 lookup->section_answer = ISC_TRUE;
938                                 lookup->section_authority = ISC_FALSE;
939                                 lookup->section_question = ISC_FALSE;
940                                 lookup->comments = ISC_FALSE;
941                                 lookup->stats = ISC_FALSE;
942                         }
943                         break;
944 #ifdef DIG_SIGCHASE
945                 case 'i': /* sigchase */
946                         FULLCHECK("sigchase");
947                         lookup->sigchase = state;
948                         if (lookup->sigchase)
949                                 lookup->dnssec = ISC_TRUE;
950                         break;  
951 #endif
952                 case 't': /* stats */
953                         FULLCHECK("stats");
954                         lookup->stats = state;
955                         break;
956                 default:
957                         goto invalid_option;
958                 }
959                 break;
960         case 't':
961                 switch (cmd[1]) {
962                 case 'c': /* tcp */
963                         FULLCHECK("tcp");
964                         if (!is_batchfile)
965                                 lookup->tcp_mode = state;
966                         break;
967                 case 'i': /* timeout */
968                         FULLCHECK("timeout");
969                         if (value == NULL)
970                                 goto need_value;
971                         if (!state)
972                                 goto invalid_option;
973                         timeout = parse_uint(value, "timeout", MAXTIMEOUT);
974                         if (timeout == 0)
975                                 timeout = 1;
976                         break;
977 #if DIG_SIGCHASE_TD
978                 case 'o': /* topdown */ 
979                         FULLCHECK("topdown");
980                         lookup->do_topdown = state;
981                         break;
982 #endif
983                 case 'r':
984                         switch (cmd[2]) {
985                         case 'a': /* trace */
986                                 FULLCHECK("trace");
987                                 lookup->trace = state;
988                                 lookup->trace_root = state;
989                                 if (state) {
990                                         lookup->recurse = ISC_FALSE;
991                                         lookup->identify = ISC_TRUE;
992                                         lookup->comments = ISC_FALSE;
993                                         lookup->stats = ISC_FALSE;
994                                         lookup->section_additional = ISC_FALSE;
995                                         lookup->section_authority = ISC_TRUE;
996                                         lookup->section_question = ISC_FALSE;
997                                 }
998                                 break;
999                         case 'i': /* tries */
1000                                 FULLCHECK("tries");
1001                                 if (value == NULL)
1002                                         goto need_value;
1003                                 if (!state)
1004                                         goto invalid_option;
1005                                 lookup->retries = parse_uint(value, "tries",
1006                                                              MAXTRIES);
1007                                 if (lookup->retries == 0)
1008                                         lookup->retries = 1;
1009                                 break;
1010 #ifdef DIG_SIGCHASE
1011                         case 'u': /* trusted-key */
1012                                 FULLCHECK("trusted-key");
1013                                 if (value == NULL) 
1014                                         goto need_value;
1015                                 if (!state)
1016                                         goto invalid_option;
1017                                 n = strlcpy(trustedkey, ptr,
1018                                             sizeof(trustedkey));
1019                                 if (n >= sizeof(trustedkey))
1020                                         fatal("trusted key too large");
1021                                 break;
1022 #endif
1023                         default:
1024                                 goto invalid_option;
1025                         }
1026                         break;
1027                 case 't': /* ttlid */
1028                         FULLCHECK("ttlid");
1029                         nottl = ISC_TF(!state);
1030                         break;
1031                 default:
1032                         goto invalid_option;
1033                 }
1034                 break;
1035         case 'v':
1036                 FULLCHECK("vc");
1037                 if (!is_batchfile)
1038                         lookup->tcp_mode = state;
1039                 break;
1040         default:
1041         invalid_option:
1042         need_value:
1043                 fprintf(stderr, "Invalid option: +%s\n",
1044                          option);
1045                 usage();
1046         }
1047         return;
1048 }
1049
1050 /*
1051  * ISC_TRUE returned if value was used
1052  */
1053 static const char *single_dash_opts = "46dhimnv";
1054 static const char *dash_opts = "46bcdfhikmnptvyx";
1055 static isc_boolean_t
1056 dash_option(char *option, char *next, dig_lookup_t **lookup,
1057             isc_boolean_t *open_type_class)
1058 {
1059         char opt, *value, *ptr;
1060         isc_result_t result;
1061         isc_boolean_t value_from_next;
1062         isc_textregion_t tr;
1063         dns_rdatatype_t rdtype;
1064         dns_rdataclass_t rdclass;
1065         char textname[MXNAME];
1066         struct in_addr in4;
1067         struct in6_addr in6;
1068         in_port_t srcport;
1069         char *hash, *cmd;
1070
1071         while (strpbrk(option, single_dash_opts) == &option[0]) {
1072                 /*
1073                  * Since the -[46dhimnv] options do not take an argument,
1074                  * account for them (in any number and/or combination)
1075                  * if they appear as the first character(s) of a q-opt.
1076                  */
1077                 opt = option[0];
1078                 switch (opt) {
1079                 case '4':
1080                         if (have_ipv4) {
1081                                 isc_net_disableipv6();
1082                                 have_ipv6 = ISC_FALSE;
1083                         } else {
1084                                 fatal("can't find IPv4 networking");
1085                                 return (ISC_FALSE);
1086                         }
1087                         break;
1088                 case '6':
1089                         if (have_ipv6) {
1090                                 isc_net_disableipv4();
1091                                 have_ipv4 = ISC_FALSE;
1092                         } else {
1093                                 fatal("can't find IPv6 networking");
1094                                 return (ISC_FALSE);
1095                         }
1096                         break;
1097                 case 'd':
1098                         ptr = strpbrk(&option[1], dash_opts);
1099                         if (ptr != &option[1]) {
1100                                 cmd = option;
1101                                 FULLCHECK("debug");
1102                                 debugging = ISC_TRUE;
1103                                 return (ISC_FALSE);
1104                         } else
1105                                 debugging = ISC_TRUE;
1106                         break;
1107                 case 'h':
1108                         help();
1109                         exit(0);
1110                         break;
1111                 case 'i':
1112                         ip6_int = ISC_TRUE;
1113                         break;
1114                 case 'm': /* memdebug */
1115                         /* memdebug is handled in preparse_args() */
1116                         break;
1117                 case 'n':
1118                         /* deprecated */
1119                         break;
1120                 case 'v':
1121                         version();
1122                         exit(0);
1123                         break;
1124                 }
1125                 if (strlen(option) > 1U)
1126                         option = &option[1];
1127                 else
1128                         return (ISC_FALSE);
1129         }
1130         opt = option[0];
1131         if (strlen(option) > 1U) {
1132                 value_from_next = ISC_FALSE;
1133                 value = &option[1];
1134         } else {
1135                 value_from_next = ISC_TRUE;
1136                 value = next;
1137         }
1138         if (value == NULL)
1139                 goto invalid_option;
1140         switch (opt) {
1141         case 'b':
1142                 hash = strchr(value, '#');
1143                 if (hash != NULL) {
1144                         srcport = (in_port_t)
1145                                 parse_uint(hash + 1,
1146                                            "port number", MAXPORT);
1147                         *hash = '\0';
1148                 } else
1149                         srcport = 0;
1150                 if (have_ipv6 && inet_pton(AF_INET6, value, &in6) == 1) {
1151                         isc_sockaddr_fromin6(&bind_address, &in6, srcport);
1152                         isc_net_disableipv4();
1153                 } else if (have_ipv4 && inet_pton(AF_INET, value, &in4) == 1) {
1154                         isc_sockaddr_fromin(&bind_address, &in4, srcport);
1155                         isc_net_disableipv6();
1156                 } else {
1157                         if (hash != NULL)
1158                                 *hash = '#';
1159                         fatal("invalid address %s", value);
1160                 }
1161                 if (hash != NULL)
1162                         *hash = '#';
1163                 specified_source = ISC_TRUE;
1164                 return (value_from_next);
1165         case 'c':
1166                 if ((*lookup)->rdclassset) {
1167                         fprintf(stderr, ";; Warning, extra class option\n");
1168                 }
1169                 *open_type_class = ISC_FALSE;
1170                 tr.base = value;
1171                 tr.length = strlen(value);
1172                 result = dns_rdataclass_fromtext(&rdclass,
1173                                                  (isc_textregion_t *)&tr);
1174                 if (result == ISC_R_SUCCESS) {
1175                         (*lookup)->rdclass = rdclass;
1176                         (*lookup)->rdclassset = ISC_TRUE;
1177                 } else
1178                         fprintf(stderr, ";; Warning, ignoring "
1179                                 "invalid class %s\n",
1180                                 value);
1181                 return (value_from_next);
1182         case 'f':
1183                 batchname = value;
1184                 return (value_from_next);
1185         case 'k':
1186                 strncpy(keyfile, value, sizeof(keyfile));
1187                 keyfile[sizeof(keyfile)-1]=0;
1188                 return (value_from_next);
1189         case 'p':
1190                 port = (in_port_t) parse_uint(value, "port number", MAXPORT);
1191                 return (value_from_next);
1192         case 't':
1193                 *open_type_class = ISC_FALSE;
1194                 if (strncasecmp(value, "ixfr=", 5) == 0) {
1195                         rdtype = dns_rdatatype_ixfr;
1196                         result = ISC_R_SUCCESS;
1197                 } else {
1198                         tr.base = value;
1199                         tr.length = strlen(value);
1200                         result = dns_rdatatype_fromtext(&rdtype,
1201                                                 (isc_textregion_t *)&tr);
1202                         if (result == ISC_R_SUCCESS &&
1203                             rdtype == dns_rdatatype_ixfr) {
1204                                 result = DNS_R_UNKNOWN;
1205                         }
1206                 }
1207                 if (result == ISC_R_SUCCESS) {
1208                         if ((*lookup)->rdtypeset) {
1209                                 fprintf(stderr, ";; Warning, "
1210                                                 "extra type option\n");
1211                         }
1212                         if (rdtype == dns_rdatatype_ixfr) {
1213                                 (*lookup)->rdtype = dns_rdatatype_ixfr;
1214                                 (*lookup)->rdtypeset = ISC_TRUE;
1215                                 (*lookup)->ixfr_serial =
1216                                         parse_uint(&value[5], "serial number",
1217                                                 MAXSERIAL);
1218                                 (*lookup)->section_question = plusquest;
1219                                 (*lookup)->comments = pluscomm;
1220                         } else {
1221                                 (*lookup)->rdtype = rdtype;
1222                                 (*lookup)->rdtypeset = ISC_TRUE;
1223                                 if (rdtype == dns_rdatatype_axfr) {
1224                                         (*lookup)->section_question = plusquest;
1225                                         (*lookup)->comments = pluscomm;
1226                                 }
1227                                 (*lookup)->ixfr_serial = ISC_FALSE;
1228                         }
1229                 } else
1230                         fprintf(stderr, ";; Warning, ignoring "
1231                                  "invalid type %s\n",
1232                                  value);
1233                 return (value_from_next);
1234         case 'y':
1235                 ptr = next_token(&value,":");
1236                 if (ptr == NULL) {
1237                         usage();
1238                 }
1239                 strncpy(keynametext, ptr, sizeof(keynametext));
1240                 keynametext[sizeof(keynametext)-1]=0;
1241                 ptr = next_token(&value, "");
1242                 if (ptr == NULL)
1243                         usage();
1244                 strncpy(keysecret, ptr, sizeof(keysecret));
1245                 keysecret[sizeof(keysecret)-1]=0;
1246                 return (value_from_next);
1247         case 'x':
1248                 *lookup = clone_lookup(default_lookup, ISC_TRUE);
1249                 if (get_reverse(textname, sizeof(textname), value,
1250                                 ip6_int, ISC_FALSE) == ISC_R_SUCCESS) {
1251                         strncpy((*lookup)->textname, textname,
1252                                 sizeof((*lookup)->textname));
1253                         debug("looking up %s", (*lookup)->textname);
1254                         (*lookup)->trace_root = ISC_TF((*lookup)->trace  ||
1255                                                 (*lookup)->ns_search_only);
1256                         (*lookup)->ip6_int = ip6_int;
1257                         if (!(*lookup)->rdtypeset)
1258                                 (*lookup)->rdtype = dns_rdatatype_ptr;
1259                         if (!(*lookup)->rdclassset)
1260                                 (*lookup)->rdclass = dns_rdataclass_in;
1261                         (*lookup)->new_search = ISC_TRUE;
1262                         ISC_LIST_APPEND(lookup_list, *lookup, link);
1263                 } else {
1264                         fprintf(stderr, "Invalid IP address %s\n", value);
1265                         exit(1);
1266                 }
1267                 return (value_from_next);
1268         invalid_option:
1269         default:
1270                 fprintf(stderr, "Invalid option: -%s\n", option);
1271                 usage();
1272         }
1273         return (ISC_FALSE);
1274 }
1275
1276 /*
1277  * Because we may be trying to do memory allocation recording, we're going
1278  * to need to parse the arguments for the -m *before* we start the main
1279  * argument parsing routine.
1280  * I'd prefer not to have to do this, but I am not quite sure how else to
1281  * fix the problem.  Argument parsing in dig involves memory allocation
1282  * by its nature, so it can't be done in the main argument parser.
1283  */
1284 static void
1285 preparse_args(int argc, char **argv) {
1286         int rc;
1287         char **rv;
1288         char *option;
1289
1290         rc = argc;
1291         rv = argv;
1292         for (rc--, rv++; rc > 0; rc--, rv++) {
1293                 if (rv[0][0] != '-')
1294                         continue;
1295                 option = &rv[0][1];
1296                 while (strpbrk(option, single_dash_opts) == &option[0]) {
1297                         if (option[0] == 'm') {
1298                                 memdebugging = ISC_TRUE;
1299                                 isc_mem_debugging = ISC_MEM_DEBUGTRACE |
1300                                         ISC_MEM_DEBUGRECORD;
1301                                 return;
1302                         }
1303                         option = &option[1];
1304                 }
1305         }
1306 }
1307
1308 static void
1309 getaddresses(dig_lookup_t *lookup, const char *host) {
1310         isc_result_t result;
1311         isc_sockaddr_t sockaddrs[DIG_MAX_ADDRESSES];
1312         isc_netaddr_t netaddr;
1313         int count, i;
1314         dig_server_t *srv;
1315         char tmp[ISC_NETADDR_FORMATSIZE];
1316
1317         result = bind9_getaddresses(host, 0, sockaddrs,
1318                                     DIG_MAX_ADDRESSES, &count);   
1319         if (result != ISC_R_SUCCESS)
1320         fatal("couldn't get address for '%s': %s",
1321               host, isc_result_totext(result));
1322
1323         for (i = 0; i < count; i++) {
1324                 isc_netaddr_fromsockaddr(&netaddr, &sockaddrs[i]);
1325                 isc_netaddr_format(&netaddr, tmp, sizeof(tmp));
1326                 srv = make_server(tmp, host);
1327                 ISC_LIST_APPEND(lookup->my_server_list, srv, link);
1328         }
1329         addresscount = count;
1330 }
1331
1332 static void
1333 parse_args(isc_boolean_t is_batchfile, isc_boolean_t config_only,
1334            int argc, char **argv) {
1335         isc_result_t result;
1336         isc_textregion_t tr;
1337         isc_boolean_t firstarg = ISC_TRUE;
1338         dig_lookup_t *lookup = NULL;
1339         dns_rdatatype_t rdtype;
1340         dns_rdataclass_t rdclass;
1341         isc_boolean_t open_type_class = ISC_TRUE;
1342         char batchline[MXNAME];
1343         int bargc;
1344         char *bargv[64];
1345         int rc;
1346         char **rv;
1347 #ifndef NOPOSIX
1348         char *homedir;
1349         char rcfile[256];
1350 #endif
1351         char *input;
1352
1353         /*
1354          * The semantics for parsing the args is a bit complex; if
1355          * we don't have a host yet, make the arg apply globally,
1356          * otherwise make it apply to the latest host.  This is
1357          * a bit different than the previous versions, but should
1358          * form a consistent user interface.
1359          *
1360          * First, create a "default lookup" which won't actually be used
1361          * anywhere, except for cloning into new lookups
1362          */
1363
1364         debug("parse_args()");
1365         if (!is_batchfile) {
1366                 debug("making new lookup");
1367                 default_lookup = make_empty_lookup();
1368
1369 #ifndef NOPOSIX
1370                 /*
1371                  * Treat ${HOME}/.digrc as a special batchfile
1372                  */
1373                 INSIST(batchfp == NULL);
1374                 homedir = getenv("HOME");
1375                 if (homedir != NULL) {
1376                         unsigned int n;
1377                         n = snprintf(rcfile, sizeof(rcfile), "%s/.digrc",
1378                                      homedir);
1379                         if (n < sizeof(rcfile))
1380                                 batchfp = fopen(rcfile, "r");
1381                 }
1382                 if (batchfp != NULL) {
1383                         while (fgets(batchline, sizeof(batchline),
1384                                      batchfp) != 0) {
1385                                 debug("config line %s", batchline);
1386                                 bargc = 1;
1387                                 input = batchline;
1388                                 bargv[bargc] = next_token(&input, " \t\r\n");
1389                                 while ((bargv[bargc] != NULL) &&
1390                                        (bargc < 62)) {
1391                                         bargc++;
1392                                         bargv[bargc] =
1393                                                 next_token(&input, " \t\r\n");
1394                                 }
1395
1396                                 bargv[0] = argv[0];
1397                                 argv0 = argv[0];
1398
1399                                 reorder_args(bargc, (char **)bargv);
1400                                 parse_args(ISC_TRUE, ISC_TRUE, bargc,
1401                                            (char **)bargv);
1402                         }
1403                         fclose(batchfp);
1404                 }
1405 #endif
1406         }
1407
1408         lookup = default_lookup;
1409
1410         rc = argc;
1411         rv = argv;
1412         for (rc--, rv++; rc > 0; rc--, rv++) {
1413                 debug("main parsing %s", rv[0]);
1414                 if (strncmp(rv[0], "%", 1) == 0)
1415                         break;
1416                 if (strncmp(rv[0], "@", 1) == 0) {
1417                         getaddresses(lookup, &rv[0][1]);
1418                 } else if (rv[0][0] == '+') {
1419                         plus_option(&rv[0][1], is_batchfile,
1420                                     lookup);
1421                 } else if (rv[0][0] == '-') {
1422                         if (rc <= 1) {
1423                                 if (dash_option(&rv[0][1], NULL,
1424                                                 &lookup, &open_type_class)) {
1425                                         rc--;
1426                                         rv++;
1427                                 }
1428                         } else {
1429                                 if (dash_option(&rv[0][1], rv[1],
1430                                                 &lookup, &open_type_class)) {
1431                                         rc--;
1432                                         rv++;
1433                                 }
1434                         }
1435                 } else {
1436                         /*
1437                          * Anything which isn't an option
1438                          */
1439                         if (open_type_class) {
1440                                 if (strncasecmp(rv[0], "ixfr=", 5) == 0) {
1441                                         rdtype = dns_rdatatype_ixfr;
1442                                         result = ISC_R_SUCCESS;
1443                                 } else {
1444                                         tr.base = rv[0];
1445                                         tr.length = strlen(rv[0]);
1446                                         result = dns_rdatatype_fromtext(&rdtype,
1447                                                 (isc_textregion_t *)&tr);
1448                                         if (result == ISC_R_SUCCESS &&
1449                                             rdtype == dns_rdatatype_ixfr) {
1450                                                 result = DNS_R_UNKNOWN;
1451                                                 fprintf(stderr, ";; Warning, "
1452                                                         "ixfr requires a "
1453                                                         "serial number\n");
1454                                                 continue;
1455                                         }
1456                                 }
1457                                 if (result == ISC_R_SUCCESS) {
1458                                         if (lookup->rdtypeset) {
1459                                                 fprintf(stderr, ";; Warning, "
1460                                                         "extra type option\n");
1461                                         }
1462                                         if (rdtype == dns_rdatatype_ixfr) {
1463                                                 lookup->rdtype =
1464                                                         dns_rdatatype_ixfr;
1465                                                 lookup->rdtypeset = ISC_TRUE;
1466                                                 lookup->ixfr_serial =
1467                                                         parse_uint(&rv[0][5],
1468                                                                 "serial number",
1469                                                                 MAXSERIAL);
1470                                                 lookup->section_question =
1471                                                         plusquest;
1472                                                 lookup->comments = pluscomm;
1473                                         } else {
1474                                                 lookup->rdtype = rdtype;
1475                                                 lookup->rdtypeset = ISC_TRUE;
1476                                                 if (rdtype ==
1477                                                     dns_rdatatype_axfr) {
1478                                                     lookup->section_question =
1479                                                                 plusquest;
1480                                                     lookup->comments = pluscomm;
1481                                                 }
1482                                                 lookup->ixfr_serial = ISC_FALSE;
1483                                         }
1484                                         continue;
1485                                 }
1486                                 result = dns_rdataclass_fromtext(&rdclass,
1487                                                      (isc_textregion_t *)&tr);
1488                                 if (result == ISC_R_SUCCESS) {
1489                                         if (lookup->rdclassset) {
1490                                                 fprintf(stderr, ";; Warning, "
1491                                                         "extra class option\n");
1492                                         }
1493                                         lookup->rdclass = rdclass;
1494                                         lookup->rdclassset = ISC_TRUE;
1495                                         continue;
1496                                 }
1497                         }
1498                         if (!config_only) {
1499                                 lookup = clone_lookup(default_lookup,
1500                                                       ISC_TRUE);
1501                                 strncpy(lookup->textname, rv[0], 
1502                                         sizeof(lookup->textname));
1503                                 lookup->textname[sizeof(lookup->textname)-1]=0;
1504                                 lookup->trace_root = ISC_TF(lookup->trace  ||
1505                                                      lookup->ns_search_only);
1506                                 lookup->new_search = ISC_TRUE;
1507                                 ISC_LIST_APPEND(lookup_list, lookup, link);
1508                                 debug("looking up %s", lookup->textname);
1509                         }
1510                         /* XXX Error message */
1511                 }
1512         }
1513         /*
1514          * If we have a batchfile, seed the lookup list with the
1515          * first entry, then trust the callback in dighost_shutdown
1516          * to get the rest
1517          */
1518         if ((batchname != NULL) && !(is_batchfile)) {
1519                 if (strcmp(batchname, "-") == 0)
1520                         batchfp = stdin;
1521                 else
1522                         batchfp = fopen(batchname, "r");
1523                 if (batchfp == NULL) {
1524                         perror(batchname);
1525                         if (exitcode < 8)
1526                                 exitcode = 8;
1527                         fatal("couldn't open specified batch file");
1528                 }
1529                 /* XXX Remove code dup from shutdown code */
1530         next_line:
1531                 if (fgets(batchline, sizeof(batchline), batchfp) != 0) {
1532                         bargc = 1;
1533                         debug("batch line %s", batchline);
1534                         if (batchline[0] == '\r' || batchline[0] == '\n'
1535                             || batchline[0] == '#' || batchline[0] == ';')
1536                                 goto next_line;
1537                         input = batchline;
1538                         bargv[bargc] = next_token(&input, " \t\r\n");
1539                         while ((bargv[bargc] != NULL) && (bargc < 14)) {
1540                                 bargc++;
1541                                 bargv[bargc] = next_token(&input, " \t\r\n");
1542                         }
1543
1544                         bargv[0] = argv[0];
1545                         argv0 = argv[0];
1546
1547                         reorder_args(bargc, (char **)bargv);
1548                         parse_args(ISC_TRUE, ISC_FALSE, bargc, (char **)bargv);
1549                 }
1550         }
1551         /*
1552          * If no lookup specified, search for root
1553          */
1554         if ((lookup_list.head == NULL) && !config_only) {
1555                 lookup = clone_lookup(default_lookup, ISC_TRUE);
1556                 lookup->trace_root = ISC_TF(lookup->trace ||
1557                                             lookup->ns_search_only);
1558                 lookup->new_search = ISC_TRUE;
1559                 strcpy(lookup->textname, ".");
1560                 lookup->rdtype = dns_rdatatype_ns;
1561                 lookup->rdtypeset = ISC_TRUE;
1562                 if (firstarg) {
1563                         printgreeting(argc, argv, lookup);
1564                         firstarg = ISC_FALSE;
1565                 }
1566                 ISC_LIST_APPEND(lookup_list, lookup, link);
1567         } else if (!config_only && firstarg) {
1568                         printgreeting(argc, argv, lookup);
1569                         firstarg = ISC_FALSE;
1570         }
1571 }
1572
1573 /*
1574  * Callback from dighost.c to allow program-specific shutdown code.
1575  * Here, we're possibly reading from a batch file, then shutting down
1576  * for real if there's nothing in the batch file to read.
1577  */
1578 void
1579 dighost_shutdown(void) {
1580         char batchline[MXNAME];
1581         int bargc;
1582         char *bargv[16];
1583         char *input;
1584
1585
1586         if (batchname == NULL) {
1587                 isc_app_shutdown();
1588                 return;
1589         }
1590
1591         fflush(stdout);
1592         if (feof(batchfp)) {
1593                 batchname = NULL;
1594                 isc_app_shutdown();
1595                 if (batchfp != stdin)
1596                         fclose(batchfp);
1597                 return;
1598         }
1599
1600         if (fgets(batchline, sizeof(batchline), batchfp) != 0) {
1601                 debug("batch line %s", batchline);
1602                 bargc = 1;
1603                 input = batchline;
1604                 bargv[bargc] = next_token(&input, " \t\r\n");
1605                 while ((bargv[bargc] != NULL) && (bargc < 14)) {
1606                         bargc++;
1607                         bargv[bargc] = next_token(&input, " \t\r\n");
1608                 }
1609
1610                 bargv[0] = argv0;
1611
1612                 reorder_args(bargc, (char **)bargv);
1613                 parse_args(ISC_TRUE, ISC_FALSE, bargc, (char **)bargv);
1614                 start_lookup();
1615         } else {
1616                 batchname = NULL;
1617                 if (batchfp != stdin)
1618                         fclose(batchfp);
1619                 isc_app_shutdown();
1620                 return;
1621         }
1622 }
1623
1624 int
1625 main(int argc, char **argv) {
1626         isc_result_t result;
1627         dig_server_t *s, *s2;
1628
1629         ISC_LIST_INIT(lookup_list);
1630         ISC_LIST_INIT(server_list);
1631         ISC_LIST_INIT(search_list);
1632
1633         debug("main()");
1634         preparse_args(argc, argv);
1635         progname = argv[0];
1636         result = isc_app_start();
1637         check_result(result, "isc_app_start");
1638         setup_libs();
1639         parse_args(ISC_FALSE, ISC_FALSE, argc, argv);
1640         setup_system();
1641         if (domainopt[0] != '\0') {
1642                 set_search_domain(domainopt);
1643                 usesearch = ISC_TRUE;
1644         }
1645         result = isc_app_onrun(mctx, global_task, onrun_callback, NULL);
1646         check_result(result, "isc_app_onrun");
1647         isc_app_run();
1648         s = ISC_LIST_HEAD(default_lookup->my_server_list);
1649         while (s != NULL) {
1650                 debug("freeing server %p belonging to %p",
1651                       s, default_lookup);
1652                 s2 = s;
1653                 s = ISC_LIST_NEXT(s, link);
1654                 ISC_LIST_DEQUEUE(default_lookup->my_server_list, s2, link);
1655                 isc_mem_free(mctx, s2);
1656         }
1657         isc_mem_free(mctx, default_lookup);
1658         if (batchname != NULL) {
1659                 if (batchfp != stdin)
1660                         fclose(batchfp);
1661                 batchname = NULL;
1662         }
1663 #ifdef DIG_SIGCHASE
1664         clean_trustedkey();
1665 #endif
1666         cancel_all();
1667         destroy_libs();
1668         isc_app_finish();
1669         return (exitcode);
1670 }