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