]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
The big dig changes. Virtually everything is changed.
authorMichael Sawyer <source@isc.org>
Fri, 6 Oct 2000 19:08:08 +0000 (19:08 +0000)
committerMichael Sawyer <source@isc.org>
Fri, 6 Oct 2000 19:08:08 +0000 (19:08 +0000)
Reviewed by myself and Brian.

bin/dig/Makefile.in
bin/dig/dig.c
bin/dig/dighost.c
bin/dig/host.c
bin/dig/include/dig/dig.h
bin/dig/nslookup.c

index 9b8b32b55cb7df11dec551aff76dd9a18c618250..0ac933f3eb50131167725a860b47ffed26130c46 100644 (file)
@@ -1,19 +1,19 @@
 # Copyright (C) 2000  Internet Software Consortium.
-# 
+#
 # Permission to use, copy, modify, and distribute this software for any
 # purpose with or without fee is hereby granted, provided that the above
 # copyright notice and this permission notice appear in all copies.
-# 
-# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
-# ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
-# OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
-# CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
-# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
-# PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
-# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
-# SOFTWARE.
-
-# $Id: Makefile.in,v 1.10.2.2 2000/08/08 00:17:59 gson Exp $
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+# $Id: Makefile.in,v 1.10.2.3 2000/10/06 19:08:00 mws Exp $
 
 srcdir =       @srcdir@
 VPATH =                @srcdir@
@@ -25,7 +25,7 @@ top_srcdir =  @top_srcdir@
 
 CINCLUDES =    -I${srcdir}/include ${DNS_INCLUDES} ${ISC_INCLUDES}
 
-CDEFINES =     
+CDEFINES =     -DVERSION=\"${VERSION}\"
 CWARNINGS =
 
 DNSLIBS =      ../../lib/dns/libdns.@A@ @DNS_OPENSSL_LIBS@
@@ -44,7 +44,7 @@ TARGETS =     dig host nslookup
 
 OBJS =         dig.@O@ dighost.@O@ host.@O@ nslookup.@O@
 
-UOBJS =                
+UOBJS =
 
 SRCS =         dig.c dighost.c host.c nslookup.c
 
@@ -63,9 +63,7 @@ clean distclean::
        rm -f ${TARGETS}
 
 installdirs:
-       if [ ! -d ${DESTDIR}${bindir} ]; then \
-               mkdir ${DESTDIR}${bindir}; \
-       fi
+       $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${bindir}
 
 install:: dig host nslookup installdirs
        ${LIBTOOL} ${INSTALL_PROGRAM} dig ${DESTDIR}${bindir}
index e29ee14b8ad53e953e585fbf53efbcf10d866d09..656bc08cb35a9c9d69ee9053f2a367948311245f 100644 (file)
@@ -1,40 +1,46 @@
 /*
  * Copyright (C) 2000  Internet Software Consortium.
- * 
+ *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
- * 
- * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
- * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
- * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
- * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
- * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
- * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
- * SOFTWARE.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+ * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: dig.c,v 1.51.2.8 2000/09/15 22:56:11 gson Exp $ */
+/* $Id: dig.c,v 1.51.2.9 2000/10/06 19:08:01 mws Exp $ */
 
 #include <config.h>
 #include <stdlib.h>
+#include <time.h>
+#include <ctype.h>
 
 #include <isc/app.h>
+#include <isc/netaddr.h>
 #include <isc/string.h>
 #include <isc/util.h>
 #include <isc/task.h>
 
+#include <dns/byaddr.h>
+#include <dns/fixedname.h>
 #include <dns/message.h>
 #include <dns/name.h>
 #include <dns/rdata.h>
 #include <dns/rdataset.h>
 #include <dns/rdatatype.h>
+#include <dns/rdataclass.h>
 
 #include <dig/dig.h>
 
 extern ISC_LIST(dig_lookup_t) lookup_list;
-extern ISC_LIST(dig_server_t) server_list;
+extern dig_serverlist_t server_list;
 extern ISC_LIST(dig_searchlist_t) search_list;
 
 #define ADD_STRING(b, s) {                             \
@@ -45,8 +51,8 @@ extern ISC_LIST(dig_searchlist_t) search_list;
 }
 
 
-extern isc_boolean_t have_ipv6, show_details, specified_source,
-       usesearch, qr, ignore;
+extern isc_boolean_t have_ipv6, specified_source,
+       usesearch, qr;
 extern in_port_t port;
 extern unsigned int timeout;
 extern isc_mem_t *mctx;
@@ -61,35 +67,27 @@ extern char fixeddomain[MXNAME];
 extern int exitcode;
 extern isc_sockaddr_t bind_address;
 extern char keynametext[MXNAME];
+extern char keyfile[MXNAME];
 extern char keysecret[MXNAME];
 extern dns_tsigkey_t *key;
 extern isc_boolean_t validated;
 extern isc_taskmgr_t *taskmgr;
 extern isc_task_t *global_task;
+extern isc_boolean_t free_now;
+dig_lookup_t *default_lookup = NULL;
+extern isc_uint32_t name_limit;
+extern isc_uint32_t rr_limit;
 
-extern isc_boolean_t debugging;
-extern isc_boolean_t isc_mem_debugging;
+extern isc_boolean_t debugging, show_packets;
+char *batchname = NULL;
+FILE *batchfp = NULL;
+char *argv0;
 
-isc_boolean_t short_form = ISC_FALSE, printcmd = ISC_TRUE;
+isc_boolean_t short_form = ISC_FALSE, printcmd = ISC_TRUE,
+       nibble = ISC_FALSE;
 
 isc_uint16_t bufsize = 0;
-isc_boolean_t
-       identify = ISC_FALSE,
-       trace = ISC_FALSE,
-       ns_search_only = ISC_FALSE,
-       forcecomment = ISC_FALSE,
-       stats = ISC_TRUE,
-       comments = ISC_TRUE,
-       section_question = ISC_TRUE,
-       section_answer = ISC_TRUE,
-       section_authority = ISC_TRUE,
-       section_additional = ISC_TRUE,
-       recurse = ISC_TRUE,
-       defname = ISC_TRUE,
-       aaonly = ISC_FALSE,
-       tcpmode = ISC_FALSE,
-       adflag = ISC_FALSE,
-       cdflag = ISC_FALSE;
+isc_boolean_t forcecomment = ISC_FALSE;
 
 static const char *opcodetext[] = {
        "QUERY",
@@ -139,11 +137,12 @@ show_usage(void) {
 "        {global-d-opt} host [@local-server] {local-d-opt}\n"
 "        [ host [@local-server] {local-d-opt} [...]]\n"
 "Where:  domain          are in the Domain Name System\n"
-"        q-class  is one of (in,chaos,...) [default: in]\n"
+"        q-class  is one of (in,hs,ch,...) [default: in]\n"
 "        q-type   is one of (a,any,mx,ns,soa,hinfo,axfr,txt,...) [default:a]\n"
 "                 (Use ixfr=version for type ixfr)\n"
 "        q-opt    is one of:\n"
 "                 -x dot-notation     (shortcut for in-addr lookups)\n"
+"                 -n                  (nibble form for reverse IPv6 lookups)\n"
 "                 -f filename         (batch mode)\n"
 "                 -p port             (specify port number)\n"
 "                 -t type             (specify query type)\n"
@@ -157,15 +156,16 @@ show_usage(void) {
 "                 +domain=###         (Set default domainname)\n"
 "                 +bufsize=###        (Set EDNS0 Max UDP packet size)\n"
 "                 +[no]search         (Set whether to use searchlist)\n"
-"                 +[no]defname        (Set whether to use default domaon)\n"
+"                 +[no]defname        (Set whether to use default domain)\n"
 "                 +[no]recursive      (Recursive mode)\n"
 "                 +[no]ignore         (Don't revert to TCP for TC responses.)"
 "\n"
+"                 +[no]fail           (Don't try next server on SERVFAIL)\n"
 "                 +[no]aaonly         (Set AA flag in query)\n"
 "                 +[no]adflag         (Set AD flag in query)\n"
 "                 +[no]cdflag         (Set CD flag in query)\n"
-"                 +[no]details        (Show details of all requests)\n"
 "                 +ndots=###          (Set NDOTS value)\n"
+"                 +[no]cmd            (Control display of command line)\n"
 "                 +[no]comments       (Control display of comment lines)\n"
 "                 +[no]question       (Control display of question)\n"
 "                 +[no]answer         (Control display of answer)\n"
@@ -178,17 +178,16 @@ show_usage(void) {
 "                 +[no]nssearch       (Search all authorative nameservers)\n"
 "                 +[no]identify       (ID responders in short answers)\n"
 "                 +[no]trace          (Trace delegation down from root)\n"
+"                 +rrlimit=###        (Limit number of rr's in xfr)\n"
+"                 +namelimit=###      (Limit number of names in xfr)\n"
 "        global d-opts and servers (before host name) affect all queries.\n"
 "        local d-opts and servers (after host name) affect only that lookup.\n"
 , stderr);
-}                              
-
-void
-dighost_shutdown(void) {
-       free_lists();
-       isc_app_shutdown();
 }
 
+/*
+ * Callback from dighost.c to print the received message.
+ */
 void
 received(int bytes, int frmsize, char *frm, dig_query_t *query) {
        isc_uint64_t diff;
@@ -198,14 +197,21 @@ received(int bytes, int frmsize, char *frm, dig_query_t *query) {
 
        result = isc_time_now(&now);
        check_result(result, "isc_time_now");
-       
+
        if (query->lookup->stats) {
                diff = isc_time_microdiff(&now, &query->time_sent);
                printf(";; Query time: %ld msec\n", (long int)diff/1000);
-               printf(";; SERVER: %.*s\n", frmsize, frm);
+               printf(";; SERVER: %.*s(%s)\n", frmsize, frm,
+                      query->servname);
                time(&tnow);
                printf(";; WHEN: %s", ctime(&tnow));
-               printf(";; MSG SIZE  rcvd: %d\n", bytes);
+               if (query->lookup->doing_xfr) {
+                       printf(";; XFR size: %d names, %d rrs\n",
+                              query->name_count, query->rr_count);
+               } else {
+                       printf(";; MSG SIZE  rcvd: %d\n", bytes);
+
+               }
                if (key != NULL) {
                        if (!validated)
                                puts(";; WARNING -- Some TSIG could not "
@@ -217,11 +223,17 @@ received(int bytes, int frmsize, char *frm, dig_query_t *query) {
                puts("");
        } else if (query->lookup->identify && !short_form) {
                diff = isc_time_microdiff(&now, &query->time_sent);
-               printf(";; Received %u bytes from %.*s in %d ms\n",
-                      bytes, frmsize, frm, (int)diff/1000);
+               printf(";; Received %u bytes from %.*s(%s) in %d ms\n\n",
+                      bytes, frmsize, frm, query->servname,
+                      (int)diff/1000);
        }
 }
 
+/*
+ * Callback from dighost.c to print that it is trying a server.
+ * Not used in dig.
+ * XXX print_trying
+ */
 void
 trying(int frmsize, char *frm, dig_lookup_t *lookup) {
        UNUSED(frmsize);
@@ -229,6 +241,9 @@ trying(int frmsize, char *frm, dig_lookup_t *lookup) {
        UNUSED(lookup);
 }
 
+/*
+ * Internal print routine used to print short form replies.
+ */
 static isc_result_t
 say_message(dns_rdata_t *rdata, dig_query_t *query, isc_buffer_t *buf) {
        isc_result_t result;
@@ -258,6 +273,9 @@ say_message(dns_rdata_t *rdata, dig_query_t *query, isc_buffer_t *buf) {
        return (ISC_R_SUCCESS);
 }
 
+/*
+ * short_form message print handler.  Calls above say_message()
+ */
 static isc_result_t
 short_answer(dns_message_t *msg, dns_messagetextflag_t flags,
             isc_buffer_t *buf, dig_query_t *query)
@@ -269,7 +287,7 @@ short_answer(dns_message_t *msg, dns_messagetextflag_t flags,
        dns_name_t empty_name;
        char t[4096];
        dns_rdata_t rdata;
-       
+
        UNUSED(flags);
 
        dns_name_init(&empty_name, NULL);
@@ -303,11 +321,13 @@ short_answer(dns_message_t *msg, dns_messagetextflag_t flags,
                else if (result != ISC_R_SUCCESS)
                        return (result);
        }
-       
+
        return (ISC_R_SUCCESS);
 }
 
-
+/*
+ * Callback from dighost.c to print the reply from a server
+ */
 isc_result_t
 printmessage(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers) {
        isc_boolean_t did_flag = ISC_FALSE;
@@ -316,18 +336,12 @@ printmessage(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers) {
        isc_buffer_t *buf = NULL;
        unsigned int len = OUTPUTBUF;
 
-       UNUSED(query);
-
+       if (query->lookup->cmdline[0] != 0) {
+               fputs(query->lookup->cmdline, stdout);
+               query->lookup->cmdline[0]=0;
+       }
        debug("printmessage(%s)", headers ? "headers" : "noheaders");
 
-       /*
-        * Exitcode 9 means we timed out, but if we're printing a message,
-        * we must have recovered.  Go ahead and reset it to code 0, and
-        * call this a success.
-        */
-       if (exitcode == 9)
-               exitcode = 0;
-
        flags = 0;
        if (!headers) {
                flags |= DNS_MESSAGETEXTFLAG_NOHEADERS;
@@ -343,6 +357,8 @@ printmessage(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers) {
 
        if (query->lookup->comments && !short_form) {
                if (!query->lookup->doing_xfr) {
+                       if (query->lookup->cmdline[0] != 0)
+                               printf ("; %s\n",query->lookup->cmdline);
                        if (msg == query->lookup->sendmsg)
                                printf(";; Sending:\n");
                        else
@@ -365,7 +381,7 @@ printmessage(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers) {
                        if ((msg->flags & DNS_MESSAGEFLAG_TC) != 0) {
                                printf("%stc", did_flag ? " " : "");
                                did_flag = ISC_TRUE;
-                       }       
+                       }
                        if ((msg->flags & DNS_MESSAGEFLAG_RD) != 0) {
                                printf("%srd", did_flag ? " " : "");
                                did_flag = ISC_TRUE;
@@ -382,7 +398,7 @@ printmessage(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers) {
                                printf("%scd", did_flag ? " " : "");
                                did_flag = ISC_TRUE;
                        }
-                       
+
                        printf("; QUERY: %u, ANSWER: %u, "
                               "AUTHORITY: %u, ADDITIONAL: %u\n",
                               msg->counts[DNS_SECTION_QUESTION],
@@ -393,7 +409,7 @@ printmessage(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers) {
                        result = dns_message_pseudosectiontotext(msg,
                                                 DNS_PSEUDOSECTION_OPT,
                                                 flags, buf);
-                       check_result(result, 
+                       check_result(result,
                                     "dns_message_pseudosectiontotext");
                }
        }
@@ -413,7 +429,7 @@ printmessage(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers) {
                        }
                        check_result(result, "dns_message_sectiontotext");
                }
-       }                       
+       }
        if (query->lookup->section_answer) {
                if (!short_form) {
                answer_again:
@@ -432,7 +448,7 @@ printmessage(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers) {
                        result = short_answer(msg, flags, buf, query);
                        check_result(result, "short_answer");
                }
-       }                       
+       }
        if (query->lookup->section_authority) {
                if (!short_form) {
                authority_again:
@@ -448,7 +464,7 @@ printmessage(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers) {
                        }
                        check_result(result, "dns_message_sectiontotext");
                }
-       }                       
+       }
        if (query->lookup->section_additional) {
                if (!short_form) {
                additional_again:
@@ -477,12 +493,12 @@ printmessage(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers) {
                                                   msg,
                                                   DNS_PSEUDOSECTION_SIG0,
                                                   flags, buf);
-                               
+
                                check_result(result,
                                           "dns_message_pseudosectiontotext");
                        }
                }
-       }                       
+       }
        if (headers && query->lookup->comments && !short_form)
                printf("\n");
 
@@ -492,20 +508,35 @@ printmessage(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers) {
        return (result);
 }
 
+/*
+ * print the greeting message when the program first starts up.
+ */
 static void
-printgreeting(int argc, char **argv) {
-       int i = 1;
+printgreeting(int argc, char **argv, dig_lookup_t *lookup) {
+       int i;
+       static isc_boolean_t first = ISC_TRUE;
+       char append[MXNAME];
 
        if (printcmd) {
-               puts("");
-               printf("; <<>> DiG 9.0 <<>>");
+               snprintf(lookup->cmdline, sizeof(lookup->cmdline),
+                        "%s; <<>> DiG " VERSION " <<>>",
+                        first?"\n":"");
+               i = 1;
                while (i < argc) {
-                       printf(" %s", argv[i++]);
+                       snprintf(append, sizeof(append), " %s", argv[i++]);
+                       strncat(lookup->cmdline, append,
+                               sizeof (lookup->cmdline));
+               }
+               strncat(lookup->cmdline, "\n", sizeof (lookup->cmdline));
+               if (first) {
+                       snprintf(append, sizeof (append), 
+                                ";; global options: %s %s\n",
+                              short_form ? "short_form" : "",
+                              printcmd ? "printcmd" : "");
+                       first = ISC_FALSE;
+                       strncat(lookup->cmdline, append,
+                               sizeof (lookup->cmdline));
                }
-               puts("");
-               printf(";; global options: %s %s\n",
-                      short_form ? "short_form" : "",
-                      printcmd ? "printcmd" : "");
        }
 }
 
@@ -548,695 +579,696 @@ reorder_args(int argc, char *argv[]) {
 /*
  * We're not using isc_commandline_parse() here since the command line
  * syntax of dig is quite a bit different from that which can be described
- * that routine.  There is a portability issue here.
+ * that routine.
+ * XXX doc options
  */
-static void
-parse_args(isc_boolean_t is_batchfile, int argc, char **argv) {
-       isc_boolean_t have_host = ISC_FALSE;
-       dig_server_t *srv = NULL;
-       dig_lookup_t *lookup = NULL;
-       char *batchname = NULL;
-       char batchline[MXNAME];
-       char address[MXNAME];
-       FILE *fp = NULL;
-       int bargc;
-       char *bargv[16];
-       char bargv0[sizeof("dig")];
-       int i, n;
-       int adrs[4];
-       int rc;
-       char **rv;
-       char *ptr;
 
-       /*
-        * The semantics for parsing the args is a bit complex; if
-        * we don't have a host yet, make the arg apply globally,
-        * otherwise make it apply to the latest host.  This is
-        * a bit different than the previous versions, but should
-        * form a consistent user interface.
-        */
+static void
+plus_option(char *option, isc_boolean_t is_batchfile,
+           dig_lookup_t *lookup)
+{
+       char option_store[256];
+       char *cmd, *value, *ptr;
+       isc_boolean_t state = ISC_TRUE;
 
-       rc = argc;
-       rv = argv;
-       for (rc--, rv++; rc > 0; rc--, rv++) {
-               debug("main parsing %s", rv[0]);
-               if (strncmp(rv[0], "%", 1) == 0) 
+       strncpy(option_store, option, sizeof(option_store));
+       option_store[sizeof(option_store)-1]=0;
+       ptr = option_store;
+       cmd=next_token(&ptr,"=");
+       if (cmd == NULL) {
+               printf(";; Invalid option %s\n",option_store);
+               return;
+       }
+       value=ptr;
+       if (strncasecmp(cmd,"no",2)==0) {
+               cmd += 2;
+               state = ISC_FALSE;
+       }
+       switch (tolower(cmd[0])) {
+       case 'a':
+               switch (tolower(cmd[1])) {
+               case 'a': /* aaflag */
+                       lookup->aaonly = state;
                        break;
-               if (strncmp(rv[0], "@", 1) == 0) {
-                       srv = isc_mem_allocate(mctx,
-                                              sizeof(struct dig_server));
-                       if (srv == NULL)
-                               fatal("Memory allocation failure");
-                       strncpy(srv->servername, &rv[0][1], MXNAME-1);
-                       if (is_batchfile && have_host) {
-                               if (!lookup->use_my_server_list) {
-                                       ISC_LIST_INIT(lookup->
-                                                     my_server_list);
-                                       lookup->use_my_server_list =
-                                               ISC_TRUE;
-                               }
-                               ISC_LIST_APPEND(lookup->my_server_list,
-                                               srv, link);
-                       } else {
-                               ISC_LIST_APPEND(server_list, srv, link);
-                       }
-               } else if ((strcmp(rv[0], "+vc") == 0)
-                          && (!is_batchfile)) {
-                       if (have_host)
-                               lookup->tcp_mode = ISC_TRUE;
-                       else
-                               tcpmode = ISC_TRUE;
-               } else if ((strcmp(rv[0], "+novc") == 0)
-                          && (!is_batchfile)) {
-                       if (have_host)
-                               lookup->tcp_mode = ISC_FALSE;
-                       else
-                               tcpmode = ISC_FALSE;
-               } else if ((strcmp(rv[0], "+tcp") == 0)
-                          && (!is_batchfile)) {
-                       if (have_host)
-                               lookup->tcp_mode = ISC_TRUE;
-                       else
-                               tcpmode = ISC_TRUE;
-               } else if ((strcmp(rv[0], "+notcp") == 0)
-                          && (!is_batchfile)) {
-                       if (have_host)
-                               lookup->tcp_mode = ISC_FALSE;
-                       else
-                               tcpmode = ISC_FALSE;
-               } else if (strncmp(rv[0], "+domain=", 8) == 0) {
-                       /* Global option always */
-                       strncpy(fixeddomain, &rv[0][8], MXNAME);
-               } else if (strncmp(rv[0], "+sea", 4) == 0) {
-                       /* Global option always */
-                       usesearch = ISC_TRUE;
-               } else if (strncmp(rv[0], "+nosea", 6) == 0) {
-                       usesearch = ISC_FALSE;
-               } else if (strncmp(rv[0], "+defn", 5) == 0) {
-                       if (have_host)
-                               lookup->defname = ISC_TRUE;
-                       else
-                               defname = ISC_TRUE;
-               } else if (strncmp(rv[0], "+nodefn", 7) == 0) {
-                       if (have_host)
-                               lookup->defname = ISC_FALSE;
-                       else
-                               defname = ISC_FALSE;
-               } else if (strncmp(rv[0], "+time=", 6) == 0) {
-                       /* Global option always */
-                       timeout = atoi(&rv[0][6]);
-                       if (timeout <= 0)
-                               timeout = 1;
-                       debug ("timeout set to %d", timeout);
-               } else if (strncmp(rv[0], "+timeout=", 9) == 0) {
-                       /* Global option always */
-                       timeout = atoi(&rv[0][9]);
-                       if (timeout <= 0)
-                               timeout = 1;
-                       debug ("timeout set to %d", timeout);
-               } else if (strncmp(rv[0], "+tries=", 7) == 0) {
-                       if (have_host) {
-                               lookup->retries = atoi(&rv[0][7]);
-                               if (lookup->retries <= 0)
-                                       lookup->retries = 1;
-                       } else {
-                               tries = atoi(&rv[0][7]);
-                               if (tries <= 0)
-                                       tries = 1;
-                       }
-               } else if (strncmp(rv[0], "+buf=", 5) == 0) {
-                       if (have_host) {
-                               lookup->udpsize = atoi(&rv[0][5]);
-                               if (lookup->udpsize <= 0)
-                                       lookup->udpsize = 0;
-                               if (lookup->udpsize > COMMSIZE)
-                                       lookup->udpsize = COMMSIZE;
-                       } else {
-                               bufsize = atoi(&rv[0][5]);
-                               if (bufsize <= 0)
-                                       bufsize = 0;
-                               if (bufsize > COMMSIZE)
-                                       bufsize = COMMSIZE;
-                       }
-               } else if (strncmp(rv[0], "+bufsize=", 9) == 0) {
-                       if (have_host) {
-                               lookup->udpsize = atoi(&rv[0][9]);
-                               if (lookup->udpsize <= 0)
-                                       lookup->udpsize = 0;
-                               if (lookup->udpsize > COMMSIZE)
-                                       lookup->udpsize = COMMSIZE;
-                       } else {
-                               bufsize = atoi(&rv[0][9]);
-                               if (bufsize <= 0)
-                                       bufsize = 0;
-                               if (bufsize > COMMSIZE)
-                                       bufsize = COMMSIZE;
+               case 'd': 
+                       switch (tolower(cmd[2])) {
+                       case 'd': /* additional */
+                               lookup->section_additional = state;
+                               break;
+                       case 'f': /* adflag */
+                               lookup->adflag = state;
+                               break;
+                       default:
+                               goto invalid_option;
                        }
-               } else if (strncmp(rv[0], "+ndots=", 7) == 0) {
-                       /* Global option always */
-                       ndots = atoi(&rv[0][7]);
+                       break;
+               case 'l': /* all */
+                       lookup->section_question = state;
+                       lookup->section_authority = state;
+                       lookup->section_answer = state;
+                       lookup->section_additional = state;
+                       lookup->comments = state;
+                       break;
+               case 'n': /* answer */
+                       lookup->section_answer = state;
+                       break;
+               case 'u': /* authority */
+                       lookup->section_authority = state;
+                       break;
+               default:
+                       goto invalid_option;
+               }
+               break;
+       case 'b': /* bufsize */
+               if (value == NULL)
+                       goto need_value;
+               if (!state)
+                       goto invalid_option;
+               lookup->udpsize = atoi(value);
+               if (lookup->udpsize <= 0)
+                       lookup->udpsize = 0;
+               if (lookup->udpsize > COMMSIZE)
+                       lookup->udpsize = COMMSIZE;
+               break;
+       case 'c':
+               switch (tolower(cmd[1])) {
+               case 'd':/* cdflag */
+                       lookup->cdflag = state;
+                       break;
+               case 'm': /* cmd */
+                       printcmd = state;
+                       break;
+               case 'o': /* comments */
+                       lookup->comments = state;
+                       break;
+               default:
+                       goto invalid_option;
+               }
+               break;
+       case 'd':
+               switch (tolower(cmd[1])) {
+               case 'e':
+                       lookup->defname = state;
+                       break;
+               case 'o': /* domain */  
+                       if (value == NULL)
+                               goto need_value;
+                       if (!state)
+                               goto invalid_option;
+                       strncpy(fixeddomain, value, sizeof(fixeddomain));
+                       fixeddomain[sizeof(fixeddomain)-1]=0;
+                       break;
+               default:
+                       goto invalid_option;
+               }
+               break;
+       case 'f': /* fail */
+               lookup->servfail_stops = state;
+               break;
+       case 'i':
+               switch (tolower(cmd[1])) {
+               case 'd': /* identify */
+                       lookup->identify = state;
+                       break;
+               case 'g': /* ignore */
+               default: /* Inherets default for compatibility */
+                       lookup->ignore = ISC_TRUE;
+               }
+               break;
+       case 'n':
+               switch (tolower(cmd[1])) {
+               case 'a': /* namelimit */
+                       if (value == NULL)
+                               goto need_value;
+                       if (!state)
+                               goto invalid_option;
+                       name_limit = atoi(value);
+                       break;
+               case 'd': /* ndots */
+                       if (value == NULL)
+                               goto need_value;
+                       if (!state)
+                               goto invalid_option;
+                       ndots = atoi(value);
                        if (ndots < 0)
                                ndots = 0;
-               } else if (strncmp(rv[0], "+rec", 4) == 0) {
-                       if (have_host)
-                               lookup->recurse = ISC_TRUE;
-                       else
-                               recurse = ISC_TRUE;
-               } else if (strncmp(rv[0], "+norec", 6) == 0) {
-                       if (have_host)
-                               lookup->recurse = ISC_FALSE;
-                       else
-                               recurse = ISC_FALSE;
-               } else if (strncmp(rv[0], "+aa", 3) == 0) {
-                       if (have_host) 
-                               lookup->aaonly = ISC_TRUE;
-                       else
-                               aaonly = ISC_TRUE;
-               } else if (strncmp(rv[0], "+noaa", 5) == 0) {
-                       if (have_host) 
-                               lookup->aaonly = ISC_FALSE;
-                       else
-                               aaonly = ISC_FALSE;
-               } else if (strncmp(rv[0], "+adf", 4) == 0) {
-                       if (have_host) 
-                               lookup->adflag = ISC_TRUE;
-                       else
-                               adflag = ISC_TRUE;
-               } else if (strncmp(rv[0], "+noadf", 6) == 0) {
-                       if (have_host) 
-                               lookup->adflag = ISC_FALSE;
-                       else
-                               adflag = ISC_FALSE;
-               } else if (strncmp(rv[0], "+cd", 3) == 0) {
-                       if (have_host) 
-                               lookup->cdflag = ISC_TRUE;
-                       else
-                               cdflag = ISC_TRUE;
-               } else if (strncmp(rv[0], "+nocd", 5) == 0) {
-                       if (have_host) 
-                               lookup->cdflag = ISC_FALSE;
-                       else
-                               cdflag = ISC_FALSE;
-               } else if (strncmp(rv[0], "+ns", 3) == 0) {
-                       if (have_host) {
-                               lookup->ns_search_only = ISC_TRUE;
+                       break;
+               case 's': /* nssearch */
+                       lookup->ns_search_only = state;
+                       if (state) {
                                lookup->trace_root = ISC_TRUE;
                                lookup->recurse = ISC_FALSE;
                                lookup->identify = ISC_TRUE;
                                lookup->stats = ISC_FALSE;
-                               if (!forcecomment)
-                                       lookup->comments = ISC_FALSE;
+                               lookup->comments = ISC_FALSE;
                                lookup->section_additional = ISC_FALSE;
                                lookup->section_authority = ISC_FALSE;
                                lookup->section_question = ISC_FALSE;
-                       } else {
-                               ns_search_only = ISC_TRUE;
-                               recurse = ISC_FALSE;
-                               identify = ISC_TRUE;
-                               stats = ISC_FALSE;
-                               if (!forcecomment)
-                                       comments = ISC_FALSE;
-                               section_additional = ISC_FALSE;
-                               section_authority = ISC_FALSE;
-                               section_question = ISC_FALSE;
+                               lookup->rdtype = dns_rdatatype_soa;
+                               short_form = ISC_TRUE;
                        }
-               } else if (strncmp(rv[0], "+nons", 6) == 0) {
-                       if (have_host)
-                               lookup->ns_search_only = ISC_FALSE;
-                       else
-                               ns_search_only = ISC_FALSE;
-               } else if (strncmp(rv[0], "+tr", 3) == 0) {
-                       if (have_host) {
-                               lookup->trace = ISC_TRUE;
-                               lookup->trace_root = ISC_TRUE;
-                               lookup->recurse = ISC_FALSE;
-                               lookup->identify = ISC_TRUE;
-                               if (!forcecomment) {
-                                       lookup->comments = ISC_FALSE;
-                                       lookup->stats = ISC_FALSE;
-                               }
+                       break;
+               default:
+                       goto invalid_option;
+               }
+               break;
+       case 'q': 
+               switch (tolower(cmd[1])) {
+               case 'r': /* qr */
+                       qr = state;
+                       break;
+               case 'u': /* question */
+                       lookup->section_question = state;
+                       break;
+               default:
+                       goto invalid_option;
+               }
+               break;
+       case 'r':
+               switch (tolower(cmd[1])) {
+               case 'e': /* recurse */
+                       lookup->recurse = state;
+                       break;
+               case 'r': /* rrlimit */
+                       if (value == NULL)
+                               goto need_value;
+                       if (!state)
+                               goto invalid_option;
+                       rr_limit = atoi(value);
+                       break;
+               default:
+                       goto invalid_option;
+               }
+               break;
+       case 's':
+               switch (tolower(cmd[1])) {
+               case 'e': /* search */
+                       usesearch = state;
+                       break;
+               case 'h': /* short */
+                       short_form = state;
+                       if (state) {
+                               printcmd = ISC_FALSE;
                                lookup->section_additional = ISC_FALSE;
                                lookup->section_authority = ISC_FALSE;
                                lookup->section_question = ISC_FALSE;
-                               show_details = ISC_TRUE;
-                       } else {
-                               trace = ISC_TRUE;
-                               recurse = ISC_FALSE;
-                               identify = ISC_TRUE;
-                               if (!forcecomment) {
-                                       comments = ISC_FALSE;
-                                       stats = ISC_FALSE;
-                               }
-                               section_additional = ISC_FALSE;
-                               section_authority = ISC_FALSE;
-                               section_question = ISC_FALSE;
-                               show_details = ISC_TRUE;
-                       }
-               } else if (strncmp(rv[0], "+notr", 6) == 0) {
-                       if (have_host) {
-                               lookup->trace = ISC_FALSE;
-                               lookup->trace_root = ISC_FALSE;
+                               lookup->comments = ISC_FALSE;
+                               lookup->stats = ISC_FALSE;
                        }
-                       else
-                               trace = ISC_FALSE;
-               } else if (strncmp(rv[0], "+det", 4) == 0) {
-                       show_details = ISC_TRUE;
-               } else if (strncmp(rv[0], "+nodet", 6) == 0) {
-                       show_details = ISC_FALSE;
-               } else if (strncmp(rv[0], "+cmd", 4) == 0) {
-                       printcmd = ISC_TRUE;
-               } else if (strncmp(rv[0], "+nocmd", 6) == 0) {
-                       printcmd = ISC_FALSE;
-               } else if (strncmp(rv[0], "+sho", 4) == 0) {
-                       short_form = ISC_TRUE;
-                       printcmd = ISC_FALSE;
-                       if (have_host) {
-                               lookup->section_additional = ISC_FALSE;
-                               lookup->section_authority = ISC_FALSE;
-                               lookup->section_question = ISC_FALSE;
-                               if (!forcecomment) {
+                       break;
+               case 't': /* stats */
+                       lookup->stats = state;
+                       break;
+               default:
+                       goto invalid_option;
+               }
+               break;
+       case 't':
+               switch (tolower(cmd[1])) {
+               case 'c': /* tcp */
+                       if (!is_batchfile)
+                               lookup->tcp_mode = state;
+                       break;
+               case 'i': /* timeout */
+                       if (value == NULL)
+                               goto need_value;
+                       if (!state)
+                               goto invalid_option;
+                       timeout = atoi(value);
+                       if (timeout <= 0)
+                               timeout = 1;
+                       break;
+               case 'r':
+                       switch (tolower(cmd[2])) {
+                       case 'a': /* trace */
+                               lookup->trace = state;
+                               lookup->trace_root = state;
+                               if (state) {
+                                       lookup->recurse = ISC_FALSE;
+                                       lookup->identify = ISC_TRUE;
                                        lookup->comments = ISC_FALSE;
                                        lookup->stats = ISC_FALSE;
+                                       lookup->section_additional = ISC_FALSE;
+                                       lookup->section_authority = ISC_TRUE;
+                                       lookup->section_question = ISC_FALSE;
                                }
-                       } else {
-                               section_additional = ISC_FALSE;
-                               section_authority = ISC_FALSE;
-                               section_question = ISC_FALSE;
-                               if (!forcecomment) {
-                                       comments = ISC_FALSE;
-                                       stats = ISC_FALSE;
-                               }
-                       }
-               } else if (strncmp(rv[0], "+nosho", 6) == 0) {
-                       short_form = ISC_FALSE;
-               } else if (strncmp(rv[0], "+i", 2) == 0) {
-                       ignore = ISC_TRUE;
-               } else if (strncmp(rv[0], "+noi", 4) == 0) {
-                       ignore = ISC_FALSE;
-               } else if (strncmp(rv[0], "+id", 3) == 0) {
-                       if (have_host)
-                               lookup->identify = ISC_TRUE;
-                       else
-                               identify = ISC_TRUE;
-               } else if (strncmp(rv[0], "+noid", 5) == 0) {
-                       if (have_host)
-                               lookup->identify = ISC_FALSE;
-                       else
-                               identify = ISC_FALSE;
-               } else if (strncmp(rv[0], "+com", 4) == 0) {
-                       if (have_host)
-                               lookup->comments = ISC_TRUE;
-                       else
-                               comments = ISC_TRUE;
-                       forcecomment = ISC_TRUE;
-               } else if (strncmp(rv[0], "+nocom", 6) == 0) {
-                       if (have_host) {
-                               lookup->comments = ISC_FALSE;
-                               lookup->stats = ISC_FALSE;
-                       } else {
-                               comments = ISC_FALSE;
-                               stats = ISC_FALSE;
+                               break;
+                       case 'i': /* tries */
+                               if (value == NULL)
+                                       goto need_value;
+                               if (!state)
+                                       goto invalid_option;
+                               lookup->retries = atoi(value);
+                               if (lookup->retries <= 0)
+                                       lookup->retries = 1;
+                               break;
+                       default:
+                               goto invalid_option;
                        }
-                       forcecomment = ISC_FALSE;
-               } else if (strncmp(rv[0], "+sta", 4) == 0) {
-                       if (have_host)
-                               lookup->stats = ISC_TRUE;
-                       else
-                               stats = ISC_TRUE;
-               } else if (strncmp(rv[0], "+nosta", 6) == 0) {
-                       if (have_host)
-                               lookup->stats = ISC_FALSE;
-                       else
-                               stats = ISC_FALSE;
-               } else if (strncmp(rv[0], "+qr", 3) == 0) {
-                       qr = ISC_TRUE;
-               } else if (strncmp(rv[0], "+noqr", 5) == 0) {
-                       qr = ISC_FALSE;
-               } else if (strncmp(rv[0], "+que", 4) == 0) {
-                       if (have_host)
-                               lookup->section_question = ISC_TRUE;
-                       else
-                               section_question = ISC_TRUE;
-               } else if (strncmp(rv[0], "+noque", 6) == 0) {
-                       if (have_host)
-                               lookup->section_question = ISC_FALSE;
-                       else
-                               section_question = ISC_FALSE;
-               } else if (strncmp(rv[0], "+ans", 4) == 0) {
-                       if (have_host)
-                               lookup->section_answer = ISC_TRUE;
-                       else
-                               section_answer = ISC_TRUE;
-               } else if (strncmp(rv[0], "+noans", 6) == 0) {
-                       if (have_host)
-                               lookup->section_answer = ISC_FALSE;
-                       else
-                               section_answer = ISC_FALSE;
-               } else if (strncmp(rv[0], "+add", 4) == 0) {
-                       if (have_host)
-                               lookup->section_additional = ISC_TRUE;
-                       else
-                               section_additional = ISC_TRUE;
-               } else if (strncmp(rv[0], "+noadd", 6) == 0) {
-                       if (have_host)
-                               lookup->section_additional = ISC_FALSE;
-                       else
-                               section_additional = ISC_FALSE;
-               } else if (strncmp(rv[0], "+aut", 4) == 0) {
-                       if (have_host)
-                               lookup->section_authority = ISC_TRUE;
-                       else
-                               section_authority = ISC_TRUE;
-               } else if (strncmp(rv[0], "+noaut", 6) == 0) {
-                       if (have_host)
-                               lookup->section_authority = ISC_FALSE;
-                       else
-                               section_authority = ISC_FALSE;
-               } else if (strncmp(rv[0], "+all", 4) == 0) {
-                       if (have_host) {
-                               lookup->section_question = ISC_TRUE;
-                               lookup->section_authority = ISC_TRUE;
-                               lookup->section_answer = ISC_TRUE;
-                               lookup->section_additional = ISC_TRUE;
-                               lookup->comments = ISC_TRUE;
-                       } else {
-                               section_question = ISC_TRUE;
-                               section_authority = ISC_TRUE;
-                               section_answer = ISC_TRUE;
-                               section_additional = ISC_TRUE;
-                               comments = ISC_TRUE;
+                       break;
+               default:
+                       goto invalid_option;
+               }
+               break;
+       case 'v': /* vc */
+               if (!is_batchfile)
+                       lookup->tcp_mode = state;
+               break;
+       default:
+       invalid_option:
+       need_value:
+               fprintf(stderr, "Invalid option: +%s\n",
+                        option);
+               show_usage();
+               exit(1);
+       }
+       return;
+}
+
+/*
+ * ISC_TRUE returned if value was used
+ */
+static isc_boolean_t
+dash_option(char *option, char *next, dig_lookup_t **lookup,
+           isc_boolean_t *open_type_class)
+{
+       char cmd, *value, *ptr;
+       isc_result_t result;
+       isc_boolean_t value_from_next;
+       isc_textregion_t tr;
+       dns_rdatatype_t rdtype;
+       dns_rdataclass_t rdclass;
+       int adrs[4];
+       int n, i;
+       char batchline[MXNAME];
+
+       cmd = option[0];
+       if (strlen(option) > 1) {
+               value_from_next = ISC_FALSE;
+               value = &option[1];
+       }
+       else {
+               value_from_next = ISC_TRUE;
+               value = next;
+       }
+       switch (tolower(cmd)) {
+       case 'd':
+               debugging = ISC_TRUE;
+               return (ISC_FALSE);
+       case 'h':
+               show_usage();
+               exit(0);
+               break;
+       case 'm':
+               isc_mem_debugging = ISC_TRUE;
+               return (ISC_FALSE);
+       case 'n':
+               nibble = ISC_TRUE;
+               return (ISC_FALSE);
+       case 'w': 
+               show_packets = ISC_TRUE;
+               return (ISC_FALSE);
+
+       }
+       if (value == NULL)
+               goto invalid_option;
+       switch (tolower(cmd)) {
+       case 'b':
+               get_address(value, 0, &bind_address);
+               specified_source = ISC_TRUE;
+               return (value_from_next);
+       case 'c':
+               *open_type_class = ISC_FALSE;
+               tr.base = value;
+               tr.length = strlen(value);
+               result = dns_rdataclass_fromtext(&rdclass,
+                                                (isc_textregion_t *)&tr);
+               if (result == ISC_R_SUCCESS)
+                       (*lookup)->rdclass = rdclass;
+               else
+                       fprintf(stderr, ";; Warning, ignoring "
+                               "invalid class %s\n",
+                               value);
+               return (value_from_next);
+       case 'f':
+               batchname = value;
+               return (value_from_next);
+       case 'k':
+               strncpy(keyfile, value, sizeof(keyfile));
+               keyfile[sizeof(keyfile)-1]=0;
+               return (value_from_next);
+       case 'p':
+               port = atoi(value);
+               return (value_from_next);
+       case 't':
+               *open_type_class = ISC_FALSE;
+               if (strncasecmp(value, "ixfr=", 5) == 0) {
+                       (*lookup)->rdtype = dns_rdatatype_ixfr;
+                       (*lookup)->ixfr_serial =
+                               atoi(&value[5]);
+                       return (value_from_next);
+               }
+               tr.base = value;
+               tr.length = strlen(value);
+               result = dns_rdatatype_fromtext(&rdtype,
+                                               (isc_textregion_t *)&tr);
+               if (result == ISC_R_SUCCESS)
+                       (*lookup)->rdtype = rdtype;
+               else
+                       fprintf(stderr, ";; Warning, ignoring "
+                                "invalid type %s\n",
+                                value);
+               return (value_from_next);
+       case 'y':
+               ptr = next_token(&value,":");
+               if (ptr == NULL) {
+                       show_usage();
+                       exit(1);
+               }
+               strncpy(keynametext, ptr, sizeof(keynametext));
+               keynametext[sizeof(keynametext)-1]=0;
+               ptr = next_token(&value, "");
+               if (ptr == NULL) {
+                       show_usage();
+                       exit(1);
+               }
+               strncpy(keysecret, ptr, sizeof(keysecret));
+               keysecret[sizeof(keysecret)-1]=0;
+               return (value_from_next);
+       case 'x':
+               *lookup = clone_lookup(default_lookup, ISC_TRUE);
+               if (strchr(value, ':') == NULL) {
+                       n = sscanf(value, "%d.%d.%d.%d",
+                                  &adrs[0], &adrs[1],
+                                  &adrs[2], &adrs[3]);
+                       if (n == 0) {
+                               show_usage();
+                               exit (1);
                        }
-               } else if (strncmp(rv[0], "+noall", 6) == 0) {
-                       if (have_host) {
-                               lookup->section_question = ISC_FALSE;
-                               lookup->section_authority = ISC_FALSE;
-                               lookup->section_answer = ISC_FALSE;
-                               lookup->section_additional = ISC_FALSE;
-                               lookup->comments = ISC_FALSE;
-                       } else {
-                               section_question = ISC_FALSE;
-                               section_authority = ISC_FALSE;
-                               section_answer = ISC_FALSE;
-                               section_additional = ISC_FALSE;
-                               comments = ISC_FALSE;
+                       for (i = n - 1; i >= 0; i--) {
+                               snprintf(batchline, MXNAME/8, "%d.",
+                                        adrs[i]);
+                               strncat((*lookup)->textname, batchline,
+                                       MXNAME);
                        }
+                       strncat((*lookup)->textname, "in-addr.arpa.",
+                               MXNAME);
+               } else {
+                       isc_netaddr_t addr;
+                       dns_fixedname_t fname;
+                       dns_name_t *name;
+                       isc_buffer_t b;
+                       
+                       addr.family = AF_INET6;
+                       n = inet_pton(AF_INET6, value, &addr.type.in6);
+                       if (n <= 0)
+                               show_usage();
+                       dns_fixedname_init(&fname);
+                       name = dns_fixedname_name(&fname);
+                       (*lookup)->nibble = nibble;
+                       result = dns_byaddr_createptrname(&addr, nibble,
+                                                         name);
+                       if (result != ISC_R_SUCCESS)
+                               show_usage();
+                       isc_buffer_init(&b, (*lookup)->textname,
+                                       sizeof (*lookup)->textname);
+                       result = dns_name_totext(name, ISC_FALSE, &b);
+                       isc_buffer_putuint8(&b, 0);
+                       if (result != ISC_R_SUCCESS)
+                               show_usage();
+               }
+               debug("looking up %s", (*lookup)->textname);
+               (*lookup)->trace_root = ISC_TF((*lookup)->trace  ||
+                                           (*lookup)->ns_search_only);
+               (*lookup)->rdtype = dns_rdatatype_ptr;
+               (*lookup)->rdclass = dns_rdataclass_in;
+               (*lookup)->new_search = ISC_TRUE;
+               
+               ISC_LIST_APPEND(lookup_list, *lookup, link);
+               return (value_from_next);
+       invalid_option:
+       default:
+               fprintf(stderr, "Invalid option: -%s\n", option);
+               show_usage();
+               exit(1);
+       }
+       return (ISC_FALSE);
+}
 
-               } else if (strncmp(rv[0], "-c", 2) == 0) {
-                       if (have_host) {
-                               if (rv[0][2] != 0) {
-                                       strncpy(lookup->rctext, &rv[0][2],
-                                               MXRD);
-                               } else {
-                                       strncpy(lookup->rctext, rv[1],
-                                               MXRD);
-                                       rv++;
-                                       rc--;
+static void
+parse_args(isc_boolean_t is_batchfile, isc_boolean_t config_only,
+          int argc, char **argv) {
+       isc_result_t result;
+       isc_textregion_t tr;
+       isc_boolean_t firstarg = ISC_TRUE;
+       dig_server_t *srv = NULL;
+       dig_lookup_t *lookup = NULL;
+       dns_rdatatype_t rdtype;
+       dns_rdataclass_t rdclass;
+       isc_boolean_t open_type_class = ISC_TRUE;
+       char batchline[MXNAME];
+       int bargc;
+       char *bargv[16];
+       int rc;
+       char **rv;
+#ifndef NOPOSIX
+       char *homedir;
+       char rcfile[132];
+#endif
+       char *input;
+
+       /*
+        * The semantics for parsing the args is a bit complex; if
+        * we don't have a host yet, make the arg apply globally,
+        * otherwise make it apply to the latest host.  This is
+        * a bit different than the previous versions, but should
+        * form a consistent user interface.
+        *
+        * First, create a "default lookup" which won't actually be used
+        * anywhere, except for cloning into new lookups
+        */
+
+       debug("parse_args()");
+       if (!is_batchfile) {
+               debug("making new lookup");
+               default_lookup = make_empty_lookup();
+
+#ifndef NOPOSIX
+               /*
+                * Treat .digrc as a special batchfile
+                */
+               homedir = getenv("HOME");
+               if (homedir != NULL)
+                       snprintf(rcfile, 132, "%s/.digrc", homedir);
+               else
+                       strcpy(rcfile, ".digrc");
+               batchfp = fopen(rcfile, "r");
+               if (batchfp != NULL) {
+                       while (fgets(batchline, sizeof(batchline),
+                                    batchfp) != 0) {
+                               debug("config line %s", batchline);
+                               bargc = 1;
+                               input = batchline;
+                               bargv[bargc] = next_token(&input, " \t\r\n");
+                               while ((bargv[bargc] != NULL) &&
+                                      (bargc < 14)) {
+                                       bargc++;
+                                       bargv[bargc] = next_token(&input, " \t\r\n");
                                }
+
+                               bargv[0] = argv[0];
+                               argv0 = argv[0];
+
+                               reorder_args(bargc, (char **)bargv);
+                               parse_args(ISC_TRUE, ISC_TRUE, bargc,
+                                          (char **)bargv);
                        }
-               } else if (strncmp(rv[0], "-t", 2) == 0) {
-                       if (have_host) {
-                               if (rv[0][2] != 0) {
-                                       strncpy(lookup->rttext, &rv[0][2],
-                                               MXRD);
-                               } else {
-                                       strncpy(lookup->rttext, rv[1],
-                                               MXRD);
-                                       rv++;
+                       fclose(batchfp);
+               }
+#endif
+       }
+
+       lookup = default_lookup;
+
+       rc = argc;
+       rv = argv;
+       for (rc--, rv++; rc > 0; rc--, rv++) {
+               debug("main parsing %s", rv[0]);
+               if (strncmp(rv[0], "%", 1) == 0)
+                       break;
+               if (strncmp(rv[0], "@", 1) == 0) {
+                       srv = make_server(&rv[0][1]);
+                       ISC_LIST_APPEND(lookup->my_server_list,
+                                       srv, link);
+               } else if (rv[0][0] == '+') {
+                       plus_option(&rv[0][1], is_batchfile,
+                                   lookup);
+               } else if (rv[0][0] == '-') {
+                       if (rc <= 1) {
+                               if (dash_option(&rv[0][1], NULL,
+                                               &lookup, &open_type_class)) {
                                        rc--;
+                                       rv++;
                                }
-                       }
-               } else if (strncmp(rv[0], "-f", 2) == 0) {
-                       if (rv[0][2] != 0) {
-                               batchname = &rv[0][2];
-                       } else {
-                               batchname = rv[1];
-                               rv++;
-                               rc--;
-                       }
-               } else if (strncmp(rv[0], "-y", 2) == 0) {
-                       if (rv[0][2] != 0)
-                               ptr = &rv[0][2];
-                       else {
-                               ptr = rv[1];
-                               rv++;
-                               rc--;
-                       }
-                       ptr = strtok(ptr,":");
-                       if (ptr == NULL) {
-                               show_usage();
-                               exit(exitcode);
-                       }
-                       strncpy(keynametext, ptr, MXNAME);
-                       ptr = strtok(NULL, "");
-                       if (ptr == NULL) {
-                               show_usage();
-                               exit(exitcode);
-                       }
-                       strncpy(keysecret, ptr, MXNAME);
-               } else if (strncmp(rv[0], "-p", 2) == 0) {
-                       if (rv[0][2] != 0) {    
-                               port = atoi(&rv[0][2]);
-                       } else {
-                               port = atoi(rv[1]);
-                               rv++;
-                               rc--;
-                       }
-               } else if (strncmp(rv[0], "-b", 2) == 0) {
-                       if (rv[0][2] != 0) {
-                               strncpy(address, &rv[0][2],
-                                       MXRD);
                        } else {
-                               strncpy(address, rv[1],
-                                       MXRD);
-                               rv++;
-                               rc--;
+                               if (dash_option(&rv[0][1], rv[1],
+                                               &lookup, &open_type_class)) {
+                                       rc--;
+                                       rv++;
+                               }
                        }
-                       get_address(address, 0, &bind_address);
-                       specified_source = ISC_TRUE;
-               } else if (strncmp(rv[0], "-h", 2) == 0) {
-                       show_usage();
-                       exit(exitcode);
-               } else if (strcmp(rv[0], "-memdebug") == 0) {
-                       isc_mem_debugging = ISC_TRUE;
-               } else if (strcmp(rv[0], "-debug") == 0) {
-                       debugging = ISC_TRUE;
-               } else if (strncmp(rv[0], "-x", 2) == 0) {
+               } else {
                        /*
-                        * XXXMWS Only works for ipv4 now.
-                        * Can't use inet_pton here, since we allow
-                        * partial addresses.
+                        * Anything which isn't an option
                         */
-                       if (rc == 1) {
-                               show_usage();
-                               exit(exitcode);
-                       }
-                       n = sscanf(rv[1], "%d.%d.%d.%d", &adrs[0], &adrs[1],
-                                   &adrs[2], &adrs[3]);
-                       if (n == 0)
-                               show_usage();
-                       lookup = isc_mem_allocate(mctx,
-                                                 sizeof(struct dig_lookup));
-                       if (lookup == NULL)
-                               fatal("Memory allocation failure");
-                       lookup->pending = ISC_FALSE;
-                       lookup->textname[0] = 0;
-                       for (i = n - 1; i >= 0; i--) {
-                               snprintf(batchline, MXNAME/8, "%d.",
-                                         adrs[i]);
-                               strncat(lookup->textname, batchline, MXNAME);
-                       }
-                       strncat(lookup->textname, "in-addr.arpa.", MXNAME);
-                       debug("looking up %s", lookup->textname);
-                       strcpy(lookup->rttext, "ptr");
-                       strcpy(lookup->rctext, "in");
-                       lookup->namespace[0] = 0;
-                       lookup->sendspace = NULL;
-                       lookup->sendmsg = NULL;
-                       lookup->name = NULL;
-                       lookup->oname = NULL;
-                       lookup->timer = NULL;
-                       lookup->xfr_q = NULL;
-                       lookup->origin = NULL;
-                       lookup->querysig = NULL;
-                       lookup->use_my_server_list = ISC_FALSE;
-                       lookup->trace = trace;
-                       lookup->trace_root = ISC_TF(trace || ns_search_only);
-                       lookup->ns_search_only = ns_search_only;
-                       lookup->doing_xfr = ISC_FALSE;
-                       lookup->ixfr_serial = 0;
-                       lookup->defname = ISC_FALSE;
-                       lookup->identify = identify;
-                       lookup->recurse = recurse;
-                       lookup->aaonly = aaonly;
-                       lookup->adflag = adflag;
-                       lookup->cdflag = cdflag;
-                       lookup->retries = tries;
-                       lookup->udpsize = bufsize;
-                       lookup->nsfound = 0;
-                       lookup->comments = comments;
-                       lookup->tcp_mode = tcpmode;
-                       lookup->stats = stats;
-                       lookup->section_question = section_question;
-                       lookup->section_answer = section_answer;
-                       lookup->section_authority = section_authority;
-                       lookup->section_additional = section_additional;
-                       lookup->new_search = ISC_TRUE;
-                       ISC_LIST_INIT(lookup->q);
-                       lookup->origin = NULL;
-                       ISC_LIST_INIT(lookup->my_server_list);
-                       ISC_LIST_APPEND(lookup_list, lookup, link);
-                       have_host = ISC_TRUE;
-                       rv++;
-                       rc--;
-               } else {
-                       if (have_host) {
-                               ENSURE(lookup != NULL);
+                       if (open_type_class) {
+                               tr.base = rv[0];
+                               tr.length = strlen(rv[0]);
                                if (strncmp(rv[0], "ixfr=", 5) == 0) {
-                                       strcpy(lookup->rttext, "ixfr");
-                                       lookup->ixfr_serial = 
+                                       lookup->rdtype = dns_rdatatype_ixfr;
+                                       lookup->ixfr_serial =
                                                atoi(&rv[0][5]);
                                        continue;
                                }
-                               if (istype(rv[0])) {
-                                       strncpy(lookup->rttext, rv[0], MXRD);
+                               result = dns_rdatatype_fromtext(&rdtype,
+                                                    (isc_textregion_t *)&tr);
+                               if ((result == ISC_R_SUCCESS) &&
+                                   (rdtype != dns_rdatatype_ixfr)) {
+                                       lookup->rdtype = rdtype;
                                        continue;
-                               } else if (isclass(rv[0])) {
-                                       strncpy(lookup->rctext, rv[0],
-                                               MXRD);
+                               }
+                               result = dns_rdataclass_fromtext(&rdclass,
+                                                    (isc_textregion_t *)&tr);
+                               if (result == ISC_R_SUCCESS) {
+                                       lookup->rdclass = rdclass;
                                        continue;
                                }
                        }
-                       lookup = isc_mem_allocate(mctx, 
-                                                 sizeof(struct dig_lookup));
-                       if (lookup == NULL)
-                               fatal("Memory allocation failure");
-                       lookup->pending = ISC_FALSE;
-                       strncpy(lookup->textname, rv[0], MXNAME-1);
-                       lookup->rttext[0] = 0;
-                       lookup->rctext[0] = 0;
-                       lookup->namespace[0] = 0;
-                       lookup->sendspace = NULL;
-                       lookup->sendmsg = NULL;
-                       lookup->name = NULL;
-                       lookup->oname = NULL;
-                       lookup->timer = NULL;
-                       lookup->xfr_q = NULL;
-                       lookup->origin = NULL;
-                       lookup->querysig = NULL;
-                       lookup->use_my_server_list = ISC_FALSE;
-                       lookup->doing_xfr = ISC_FALSE;
-                       lookup->ixfr_serial = 0;
-                       lookup->defname = ISC_FALSE;
-                       lookup->trace_root = ISC_TF(trace || ns_search_only);
-                       lookup->trace = trace;
-                       lookup->ns_search_only = ns_search_only;
-                       lookup->identify = identify;
-                       lookup->recurse = recurse;
-                       lookup->aaonly = aaonly;
-                       lookup->adflag = adflag;
-                       lookup->cdflag = cdflag;
-                       lookup->retries = tries;
-                       lookup->udpsize = bufsize;
-                       lookup->nsfound = 0;
-                       lookup->comments = comments;
-                       lookup->tcp_mode = tcpmode;
-                       lookup->stats = stats;
-                       lookup->section_question = section_question;
-                       lookup->section_answer = section_answer;
-                       lookup->section_authority = section_authority;
-                       lookup->section_additional = section_additional;
-                       lookup->new_search = ISC_TRUE;
-                       ISC_LIST_INIT(lookup->q);
-                       ISC_LIST_APPEND(lookup_list, lookup, link);
-                       lookup->origin = NULL;
-                       ISC_LIST_INIT(lookup->my_server_list);
-                       have_host = ISC_TRUE;
-                       debug("looking up %s", lookup->textname);
+                       if (!config_only) {
+                               lookup = clone_lookup(default_lookup,
+                                                     ISC_TRUE);
+                               if (firstarg) {
+                                       printgreeting(argc, argv, lookup);
+                                       firstarg = ISC_FALSE;
+                               }
+                               strncpy(lookup->textname, rv[0], 
+                                       sizeof(lookup->textname));
+                               lookup->textname[sizeof(lookup->textname)-1]=0;
+                               lookup->trace_root = ISC_TF(lookup->trace  ||
+                                                    lookup->ns_search_only);
+                               lookup->new_search = ISC_TRUE;
+                               ISC_LIST_APPEND(lookup_list, lookup, link);
+                               debug("looking up %s", lookup->textname);
+                       }
+                       /* XXX Error message */
                }
        }
-       if (batchname != NULL) {
-               fp = fopen(batchname, "r");
-               if (fp == NULL) {
+       /*
+        * If we have a batchfile, seed the lookup list with the
+        * first entry, then trust the callback in dighost_shutdown
+        * to get the rest
+        */
+       if ((batchname != NULL) && !(is_batchfile)) {
+               if (strcmp(batchname, "-") == 0)
+                       batchfp = stdin;
+               else
+                       batchfp = fopen(batchname, "r");
+               if (batchfp == NULL) {
                        perror(batchname);
-                       if (exitcode < 10)
-                               exitcode = 10;
+                       if (exitcode < 8)
+                               exitcode = 8;
                        fatal("Couldn't open specified batch file");
                }
-               while (fgets(batchline, sizeof(batchline), fp) != 0) {
-                       debug("batch line %s", batchline);
+               /* XXX Remove code dup from shutdown code */
+       next_line:
+               if (fgets(batchline, sizeof(batchline), batchfp) != 0) {
                        bargc = 1;
-                       bargv[bargc] = strtok(batchline, " \t\r\n");
-                       while ((bargv[bargc] != NULL) && (bargc < 14 )) {
+                       debug("batch line %s", batchline);
+                       if (batchline[0] == '\r' || batchline[0] == '\n'
+                           || batchline[0] == '#' || batchline[0] == ';')
+                               goto next_line;
+                       input = batchline;
+                       bargv[bargc] = next_token(&input, " \t\r\n");
+                       while ((bargv[bargc] != NULL) && (bargc < 14)) {
                                bargc++;
-                               bargv[bargc] = strtok(NULL, " \t\r\n");
+                               bargv[bargc] = next_token(&input, " \t\r\n");
                        }
 
-                       /*
-                        * This silliness (instead of ``bargv[0] = "dig";'')
-                        * dances around the const string issue.  If in
-                        * the future the 2nd argument to strncpy() is made
-                        * longer than three characters, don't forget to resize
-                        * bargv0 to accommodate it.
-                        */
-                       strncpy(bargv0, "dig", sizeof(bargv0));
-                       bargv[0] = bargv0;
+                       bargv[0] = argv[0];
+                       argv0 = argv[0];
 
                        reorder_args(bargc, (char **)bargv);
-                       parse_args(ISC_TRUE, bargc, (char **)bargv);
+                       parse_args(ISC_TRUE, ISC_FALSE, bargc, (char **)bargv);
                }
        }
-       if (lookup_list.head == NULL) {
-               lookup = isc_mem_allocate(mctx, sizeof(struct dig_lookup));
-               if (lookup == NULL)
-                       fatal("Memory allocation failure");
-               lookup->pending = ISC_FALSE;
-               lookup->rctext[0] = 0;
-               lookup->namespace[0] = 0;
-               lookup->sendspace = NULL;
-               lookup->sendmsg = NULL;
-               lookup->name = NULL;
-               lookup->oname = NULL;
-               lookup->timer = NULL;
-               lookup->xfr_q = NULL;
-               lookup->origin = NULL;
-               lookup->querysig = NULL;
-               lookup->use_my_server_list = ISC_FALSE;
-               lookup->doing_xfr = ISC_FALSE;
-               lookup->ixfr_serial = 0;
-               lookup->defname = ISC_FALSE;
-               lookup->trace_root = ISC_TF(trace || ns_search_only);
-               lookup->trace = trace;
-               lookup->ns_search_only = ns_search_only;
-               lookup->identify = identify;
-               lookup->recurse = recurse;
-               lookup->aaonly = aaonly;
-               lookup->adflag = adflag;
-               lookup->cdflag = cdflag;
-               lookup->retries = tries;
-               lookup->udpsize = bufsize;
-               lookup->nsfound = 0;
-               lookup->comments = comments;
-               lookup->tcp_mode = tcpmode;
-               lookup->stats = stats;
-               lookup->section_question = section_question;
-               lookup->section_answer = section_answer;
-               lookup->section_authority = section_authority;
-               lookup->section_additional = section_additional;
+       /*
+        * If no lookup specified, search for root
+        */
+       if ((lookup_list.head == NULL) && !config_only) {
+               lookup = clone_lookup(default_lookup, ISC_TRUE);
+               lookup->trace_root = ISC_TF(lookup->trace ||
+                                           lookup->ns_search_only);
                lookup->new_search = ISC_TRUE;
-               ISC_LIST_INIT(lookup->q);
-               ISC_LIST_INIT(lookup->my_server_list);
                strcpy(lookup->textname, ".");
-               strcpy(lookup->rttext, "NS");
-               lookup->rctext[0] = 0;
+               lookup->rdtype = dns_rdatatype_ns;
                ISC_LIST_APPEND(lookup_list, lookup, link);
        }
-       if (!is_batchfile)
-               printgreeting(argc, argv);
+}
+
+/*
+ * Callback from dighost.c to allow program-specific shutdown code.  Here,
+ * Here, we're possibly reading from a batch file, then shutting down for
+ * real if there's nothing in the batch file to read.
+ */
+void
+dighost_shutdown(void) {
+       char batchline[MXNAME];
+       int bargc;
+       char *bargv[16];
+       char *input;
+
+
+       if (batchname == NULL) {
+               isc_app_shutdown();
+               return;
+       }
+
+       if (feof(batchfp)) {
+               batchname = NULL;
+               isc_app_shutdown();
+               if (batchfp != stdin)
+                       fclose(batchfp);
+               return;
+       }
+
+       if (fgets(batchline, sizeof(batchline), batchfp) != 0) {
+               debug("batch line %s", batchline);
+               bargc = 1;
+               input = batchline;
+               bargv[bargc] = next_token(&input, " \t\r\n");
+               while ((bargv[bargc] != NULL) && (bargc < 14)) {
+                       bargc++;
+                       bargv[bargc] = next_token(&input, " \t\r\n");
+               }
+
+               bargv[0] = argv0;
+
+               reorder_args(bargc, (char **)bargv);
+               parse_args(ISC_TRUE, ISC_FALSE, bargc, (char **)bargv);
+               start_lookup();
+       } else {
+               batchname = NULL;
+               if (batchfp != stdin)
+                       fclose(batchfp);
+               isc_app_shutdown();
+               return;
+       }
 }
 
 int
 main(int argc, char **argv) {
        isc_result_t result;
+       dig_server_t *s, *s2;
 
        ISC_LIST_INIT(lookup_list);
        ISC_LIST_INIT(server_list);
@@ -1244,28 +1276,35 @@ main(int argc, char **argv) {
 
        debug("main()");
        progname = argv[0];
+       result = isc_app_start();
+       check_result(result, "isc_app_start");
        setup_libs();
-       parse_args(ISC_FALSE, argc, argv);
+       parse_args(ISC_FALSE, ISC_FALSE, argc, argv);
        setup_system();
        result = isc_app_onrun(mctx, global_task, onrun_callback, NULL);
        check_result(result, "isc_app_onrun");
        isc_app_run();
-       /*
-        * XXXMWS This code should really NOT be bypassed.  However,
-        * until the proper code can be added to handle SIGTERM/INT
-        * correctly, just exit out "hard" and deal as best we can.
-        */
-#if 0
-       if (taskmgr != NULL) {
-               debug ("Freeing taskmgr");
-               isc_taskmgr_destroy(&taskmgr);
-        }
-       if (isc_mem_debugging)
+       s = ISC_LIST_HEAD(default_lookup->my_server_list);
+       while (s != NULL) {
+               debug("freeing server %p belonging to %p",
+                     s, default_lookup);
+               s2 = s;
+               s = ISC_LIST_NEXT(s, link);
+               ISC_LIST_DEQUEUE(default_lookup->my_server_list,
+                                (dig_server_t *)s2, link);
+               isc_mem_free(mctx, s2);
+       }
+       if (isc_mem_debugging != 0)
                isc_mem_stats(mctx, stderr);
-       if (mctx != NULL)
-               isc_mem_destroy(&mctx); 
+       isc_mem_free(mctx, default_lookup);
+       if (batchname != NULL) {
+               if (batchfp != stdin)
+                       fclose(batchfp);
+               batchname = NULL;
+       }
+       cancel_all();
+       destroy_libs();
        isc_app_finish();
-#endif
        return (exitcode);
 }
 
index f6aea0ba9577550b33c77e80a6202cc367c9caaa..41a8a84d652cfdef0d078a293d09aa6793d683ac 100644 (file)
@@ -1,21 +1,21 @@
 /*
  * Copyright (C) 2000  Internet Software Consortium.
- * 
+ *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
- * 
- * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
- * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
- * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
- * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
- * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
- * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
- * SOFTWARE.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+ * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: dighost.c,v 1.58.2.11 2000/09/15 22:56:12 gson Exp $ */
+/* $Id: dighost.c,v 1.58.2.12 2000/10/06 19:08:03 mws Exp $ */
 
 /*
  * Notice to programmers:  Do not use this code as an example of how to
@@ -52,7 +52,6 @@ extern int h_errno;
 #include <isc/base64.h>
 #include <isc/entropy.h>
 #include <isc/lang.h>
-#include <isc/lex.h>
 #include <isc/netdb.h>
 #include <isc/result.h>
 #include <isc/string.h>
@@ -64,18 +63,20 @@ extern int h_errno;
 #include <dig/dig.h>
 
 ISC_LIST(dig_lookup_t) lookup_list;
-ISC_LIST(dig_server_t) server_list;
+dig_serverlist_t server_list;
 ISC_LIST(dig_searchlist_t) search_list;
 
 isc_boolean_t
        have_ipv6 = ISC_FALSE,
        specified_source = ISC_FALSE,
        free_now = ISC_FALSE,
-       show_details = ISC_FALSE,
+       cancel_now = ISC_FALSE,
        usesearch = ISC_FALSE,
        qr = ISC_FALSE,
        is_dst_up = ISC_FALSE,
-       ignore = ISC_FALSE;
+       have_domain = ISC_FALSE,
+       is_blocking =ISC_FALSE,
+       show_packets = ISC_FALSE;
 
 in_port_t port = 53;
 unsigned int timeout = 0;
@@ -86,31 +87,76 @@ isc_timermgr_t *timermgr = NULL;
 isc_socketmgr_t *socketmgr = NULL;
 isc_sockaddr_t bind_address;
 isc_sockaddr_t bind_any;
-char *rootspace[BUFSIZE];
 isc_buffer_t rootbuf;
 int sendcount = 0;
+int recvcount = 0;
 int sockcount = 0;
 int ndots = -1;
-int tries = 3;
+int tries = 2;
 int lookup_counter = 0;
 char fixeddomain[MXNAME] = "";
-int exitcode = 9;
+/*
+ * Exit Codes:
+ *   0   Everything went well, including things like NXDOMAIN
+ *   1   Usage error
+ *   7   Got too many RR's or Names
+ *   8   Couldn't open batch file
+ *   9   No reply from server
+ *   10  Internal error
+ */
+int exitcode = 0;
 char keynametext[MXNAME];
+char keyfile[MXNAME] = "";
 char keysecret[MXNAME] = "";
 dns_name_t keyname;
-dns_tsig_keyring_t *keyring = NULL;
 isc_buffer_t *namebuf = NULL;
 dns_tsigkey_t *key = NULL;
 isc_boolean_t validated = ISC_TRUE;
 isc_entropy_t *entp = NULL;
 isc_mempool_t *commctx = NULL;
-extern isc_boolean_t isc_mem_debugging;
 isc_boolean_t debugging = ISC_FALSE;
 char *progname = NULL;
+isc_mutex_t lookup_lock;
+dig_lookup_t *current_lookup = NULL;
+isc_uint32_t name_limit = INT_MAX;
+isc_uint32_t rr_limit = INT_MAX;
 
-static isc_boolean_t
+/*
+ * Apply and clear locks at the event level in global task.
+ * Can I get rid of these using shutdown events?  XXX
+ */
+#define LOCK_LOOKUP {\
+        debug("lock_lookup %s:%d", __FILE__, __LINE__);\
+        check_result(isc_mutex_lock((&lookup_lock)), "isc_mutex_lock");\
+        debug("success");\
+}
+#define UNLOCK_LOOKUP {\
+        debug("unlock_lookup %s:%d", __FILE__, __LINE__);\
+        check_result(isc_mutex_unlock((&lookup_lock)),\
+                     "isc_mutex_unlock");\
+}
+
+static void
 cancel_lookup(dig_lookup_t *lookup);
 
+static void
+recv_done(isc_task_t *task, isc_event_t *event);
+
+static void
+connect_timeout(isc_task_t *task, isc_event_t *event);
+
+char *
+next_token(char **stringp, const char *delim) {
+       char *res;
+
+       do {
+               res = strsep(stringp, delim);
+               if (res == NULL)
+                       break;
+       } while (*res == '\0');
+       return (res);
+}                       
+
 static int
 count_dots(char *string) {
        char *s;
@@ -130,9 +176,9 @@ hex_dump(isc_buffer_t *b) {
        unsigned int len;
        isc_region_t r;
 
-       isc_buffer_remainingregion(b, &r);
+       isc_buffer_usedregion(b, &r);
 
-       printf("Printing a buffer with length %d\n", r.length);
+       printf("%d bytes\n", r.length);
        for (len = 0; len < r.length; len++) {
                printf("%02x ", r.base[len]);
                if (len != 0 && len % 16 == 0)
@@ -146,22 +192,13 @@ void
 fatal(const char *format, ...) {
        va_list args;
 
-       fprintf (stderr, "%s: ", progname);
-       va_start(args, format); 
+       fprintf(stderr, "%s: ", progname);
+       va_start(args, format);
        vfprintf(stderr, format, args);
        va_end(args);
        fprintf(stderr, "\n");
-       if (exitcode == 0)
-               exitcode = 8;
-#ifdef NEVER
-       dighost_shutdown();
-       free_lists(exitcode);
-       if (mctx != NULL) {
-               if (isc_mem_debugging)
-                       isc_mem_stats(mctx, stderr);
-               isc_mem_destroy(&mctx);
-       }
-#endif
+       if (exitcode < 10)
+               exitcode = 10;
        exit(exitcode);
 }
 
@@ -170,7 +207,7 @@ debug(const char *format, ...) {
        va_list args;
 
        if (debugging) {
-               va_start(args, format); 
+               va_start(args, format);
                vfprintf(stderr, format, args);
                va_end(args);
                fprintf(stderr, "\n");
@@ -180,7 +217,6 @@ debug(const char *format, ...) {
 void
 check_result(isc_result_t result, const char *msg) {
        if (result != ISC_R_SUCCESS) {
-               exitcode = 1;
                fatal("%s: %s", msg, isc_result_totext(result));
        }
 }
@@ -202,98 +238,126 @@ make_server(const char *servname) {
                fatal("Memory allocation failure in %s:%d",
                      __FILE__, __LINE__);
        strncpy(srv->servername, servname, MXNAME);
+       srv->servername[MXNAME-1] = 0;
        return (srv);
 }
 
-isc_boolean_t
-isclass(char *text) {
-       /*
-        * Tests if a field is a class, without needing isc libs
-        * initialized.  This list will have to be manually kept in 
-        * sync with what the libs support.
-        */
-       const char *classlist[] = { "in", "hs", "chaos" };
-       const int numclasses = 3;
-       int i;
-
-       for (i = 0; i < numclasses; i++)
-               if (strcasecmp(text, classlist[i]) == 0)
-                       return (ISC_TRUE);
-
-       return (ISC_FALSE);
-}
+/*
+ * Produce a cloned server list.  The dest list must have already had
+ * ISC_LIST_INIT applied.
+ */
+void
+clone_server_list(dig_serverlist_t src,
+                 dig_serverlist_t *dest)
+{
+       dig_server_t *srv, *newsrv;
 
-isc_boolean_t
-istype(char *text) {
-       /*
-        * Tests if a field is a type, without needing isc libs
-        * initialized.  This list will have to be manually kept in 
-        * sync with what the libs support.
-        */
-       const char *typelist[] = {"a", "ns", "md", "mf", "cname",
-                                 "soa", "mb", "mg", "mr", "null",
-                                 "wks", "ptr", "hinfo", "minfo",
-                                 "mx", "txt", "rp", "afsdb",
-                                 "x25", "isdn", "rt", "nsap",
-                                 "nsap-ptr", "sig", "key", "px",
-                                 "gpos", "aaaa", "loc", "nxt",
-                                 "srv", "naptr", "kx", "cert",
-                                 "a6", "dname", "opt", "unspec",
-                                 "tkey", "tsig", "axfr", "any"};
-       const int numtypes = 42;
-       int i;
-
-       for (i = 0; i < numtypes; i++) {
-               if (strcasecmp(text, typelist[i]) == 0)
-                       return (ISC_TRUE);
+       debug("clone_server_list()");
+       srv = ISC_LIST_HEAD(src);
+       while (srv != NULL) {
+               newsrv = make_server(srv->servername);
+               ISC_LIST_ENQUEUE(*dest, newsrv, link);
+               srv = ISC_LIST_NEXT(srv, link);
        }
-       return (ISC_FALSE);
 }
 
+/*
+ * Create an empty lookup structure, which holds all the information needed
+ * to get an answer to a user's question.  This structure contains two
+ * linked lists: the server list (servers to query) and the query list
+ * (outstanding queries which have been made to the listed servers).
+ */
 dig_lookup_t *
-requeue_lookup(dig_lookup_t *lookold, isc_boolean_t servers) {
+make_empty_lookup(void) {
        dig_lookup_t *looknew;
-       dig_server_t *s, *srv;
 
-       debug("requeue_lookup()");
+       debug("make_empty_lookup()");
 
-       if (free_now)
-               return(ISC_R_SUCCESS);
+       INSIST(!free_now);
 
-       lookup_counter++;
-       if (lookup_counter > LOOKUP_LIMIT)
-               fatal("Too many lookups");
        looknew = isc_mem_allocate(mctx, sizeof(struct dig_lookup));
        if (looknew == NULL)
                fatal("Memory allocation failure in %s:%d",
                       __FILE__, __LINE__);
-       looknew->pending = ISC_FALSE;
-       strncpy(looknew->textname, lookold-> textname, MXNAME);
-       strncpy(looknew->rttext, lookold-> rttext, 32);
-       strncpy(looknew->rctext, lookold-> rctext, 32);
-       looknew->namespace[0] = 0;
+       looknew->pending = ISC_TRUE;
+       looknew->textname[0] = 0;
+       looknew->cmdline[0] = 0; /* Not copied in clone_lookup! */
+       looknew->rdtype = dns_rdatatype_a;
+       looknew->rdclass = dns_rdataclass_in;
        looknew->sendspace = NULL;
        looknew->sendmsg = NULL;
        looknew->name = NULL;
        looknew->oname = NULL;
        looknew->timer = NULL;
        looknew->xfr_q = NULL;
+       looknew->current_query = NULL;
+       looknew->doing_xfr = ISC_FALSE;
+       looknew->ixfr_serial = ISC_FALSE;
+       looknew->defname = ISC_FALSE;
+       looknew->trace = ISC_FALSE;
+       looknew->trace_root = ISC_FALSE;
+       looknew->identify = ISC_FALSE;
+       looknew->ignore = ISC_FALSE;
+       looknew->servfail_stops = ISC_FALSE;
+       looknew->udpsize = 0;
+       looknew->recurse = ISC_TRUE;
+       looknew->aaonly = ISC_FALSE;
+       looknew->adflag = ISC_FALSE;
+       looknew->cdflag = ISC_FALSE;
+       looknew->ns_search_only = ISC_FALSE;
+       looknew->origin = NULL;
+       looknew->querysig = NULL;
+       looknew->retries = tries;
+       looknew->nsfound = 0;
+       looknew->tcp_mode = ISC_FALSE;
+       looknew->nibble = ISC_FALSE;
+       looknew->comments = ISC_TRUE;
+       looknew->stats = ISC_TRUE;
+       looknew->section_question = ISC_TRUE;
+       looknew->section_answer = ISC_TRUE;
+       looknew->section_authority = ISC_TRUE;
+       looknew->section_additional = ISC_TRUE;
+       looknew->new_search = ISC_FALSE;
+       ISC_LIST_INIT(looknew->q);
+       ISC_LIST_INIT(looknew->my_server_list);
+       return (looknew);
+}
+
+/*
+ * Clone a lookup, perhaps copying the server list.  This does not clone
+ * the query list, since it will be regenerated by the setup_lookup()
+ * function, nor does it queue up the new lookup for processing.
+ * Caution: If you don't clone the servers, you MUST clone the server
+ * list seperately from somewhere else, or construct it by hand.
+ */
+dig_lookup_t *
+clone_lookup(dig_lookup_t *lookold, isc_boolean_t servers) {
+       dig_lookup_t *looknew;
+
+       debug("clone_lookup()");
+
+       INSIST(!free_now);
+
+       looknew = make_empty_lookup();
+       INSIST(looknew != NULL);
+       strncpy(looknew->textname, lookold-> textname, MXNAME);
+       looknew->textname[MXNAME-1]=0;
+       looknew->rdtype = lookold->rdtype;
+       looknew->rdclass = lookold->rdclass;
        looknew->doing_xfr = lookold->doing_xfr;
        looknew->ixfr_serial = lookold->ixfr_serial;
        looknew->defname = lookold->defname;
        looknew->trace = lookold->trace;
        looknew->trace_root = lookold->trace_root;
        looknew->identify = lookold->identify;
+       looknew->ignore = lookold->ignore;
+       looknew->servfail_stops = lookold->servfail_stops;
        looknew->udpsize = lookold->udpsize;
        looknew->recurse = lookold->recurse;
-       looknew->aaonly = lookold->aaonly;
+        looknew->aaonly = lookold->aaonly;
        looknew->adflag = lookold->adflag;
        looknew->cdflag = lookold->cdflag;
        looknew->ns_search_only = lookold->ns_search_only;
-       looknew->origin = NULL;
-       looknew->querysig = NULL;
-       looknew->retries = tries;
-       looknew->nsfound = 0;
        looknew->tcp_mode = lookold->tcp_mode;
        looknew->comments = lookold->comments;
        looknew->stats = lookold->stats;
@@ -301,39 +365,153 @@ requeue_lookup(dig_lookup_t *lookold, isc_boolean_t servers) {
        looknew->section_answer = lookold->section_answer;
        looknew->section_authority = lookold->section_authority;
        looknew->section_additional = lookold->section_additional;
-       looknew->new_search = ISC_FALSE;
-       ISC_LIST_INIT(looknew->my_server_list);
-       ISC_LIST_INIT(looknew->q);
+       looknew->retries = lookold->retries;
+
+       if (servers)
+               clone_server_list(lookold->my_server_list,
+                                 &looknew->my_server_list);
+       return (looknew);
+}
+
+/*
+ * Requeue a lookup for further processing, perhaps copying the server
+ * list.  The new lookup structure is returned to the caller, and is
+ * queued for processing.  If servers are not cloned in the requeue, they
+ * must be added before allowing the current event to complete, since the
+ * completion of the event may result in the next entry on the lookup
+ * queue getting run.
+ */
+dig_lookup_t *
+requeue_lookup(dig_lookup_t *lookold, isc_boolean_t servers) {
+       dig_lookup_t *looknew;
+
+       debug("requeue_lookup()");
+
+       lookup_counter++;
+       if (lookup_counter > LOOKUP_LIMIT)
+               fatal("Too many lookups");
+
+       looknew = clone_lookup(lookold, servers);
+       INSIST(looknew != NULL);
 
-       looknew->use_my_server_list = ISC_FALSE;
-       if (servers) {
-               looknew->use_my_server_list = lookold->use_my_server_list;
-               if (looknew->use_my_server_list) {
-                       s = ISC_LIST_HEAD(lookold->my_server_list);
-                       while (s != NULL) {
-                               srv = isc_mem_allocate(mctx,
-                                               sizeof(struct dig_server));
-                               if (srv == NULL)
-                                       fatal("Memory allocation failure "
-                                             "in %s:%d", __FILE__, __LINE__);
-                               strncpy(srv->servername, s->servername,
-                                       MXNAME);
-                               ISC_LIST_ENQUEUE(looknew->my_server_list, srv,
-                                                link);
-                               s = ISC_LIST_NEXT(s, link);
-                       }
-               }
-       }
        debug("before insertion, init@%p "
               "-> %p, new@%p -> %p",
              lookold, lookold->link.next, looknew, looknew->link.next);
-       ISC_LIST_INSERTAFTER(lookup_list, lookold, looknew, link);
+       ISC_LIST_PREPEND(lookup_list, looknew, link);
        debug("after insertion, init -> "
-             "%p, new = %p, new -> %p", 
+             "%p, new = %p, new -> %p",
              lookold, looknew, looknew->link.next);
        return (looknew);
-}      
+}
+
+
+static void
+setup_text_key(void) {
+       isc_result_t result;
+       isc_buffer_t secretbuf;
+       int secretsize;
+       unsigned char *secretstore;
+       isc_stdtime_t now;
+
+       debug("setup_text_key()");
+       result = isc_buffer_allocate(mctx, &namebuf, MXNAME);
+       check_result(result, "isc_buffer_allocate");
+       dns_name_init(&keyname, NULL);
+       check_result(result, "dns_name_init");
+       isc_buffer_putstr(namebuf, keynametext);
+       secretsize = strlen(keysecret) * 3 / 4;
+       secretstore = isc_mem_allocate(mctx, secretsize);
+       if (secretstore == NULL)
+               fatal("Memory allocation failure in %s:%d",
+                     __FILE__, __LINE__);
+       isc_buffer_init(&secretbuf, secretstore, secretsize);
+       result = isc_base64_decodestring(mctx, keysecret,
+                                        &secretbuf);
+       if (result != ISC_R_SUCCESS) {
+               printf(";; Couldn't create key %s: %s\n",
+                      keynametext, isc_result_totext(result));
+               goto failure;
+       }
+       secretsize = isc_buffer_usedlength(&secretbuf);
+       isc_stdtime_get(&now);
+
+       result = dns_name_fromtext(&keyname, namebuf,
+                                  dns_rootname, ISC_FALSE,
+                                  namebuf);
+       if (result != ISC_R_SUCCESS) {
+               printf(";; Couldn't create key %s: %s\n",
+                      keynametext, dns_result_totext(result));
+               goto failure;
+       }
+       result = dns_tsigkey_create(&keyname, dns_tsig_hmacmd5_name,
+                                   secretstore, secretsize,
+                                   ISC_TRUE, NULL, now, now, mctx,
+                                   NULL, &key);
+       if (result != ISC_R_SUCCESS) {
+               printf(";; Couldn't create key %s: %s\n",
+                      keynametext, dns_result_totext(result));
+       }
+ failure:
+       isc_mem_free(mctx, secretstore);
+       dns_name_invalidate(&keyname);
+       isc_buffer_free(&namebuf);
+}
+
+
+static void
+setup_file_key(void) {
+       isc_result_t result;
+       isc_buffer_t secretbuf;
+       unsigned char *secretstore = NULL;
+       int secretlen;
+       dst_key_t *dstkey = NULL;
+       isc_stdtime_t now;
 
+
+       debug("setup_file_key()");
+       result = dst_key_fromnamedfile(keyfile, DST_TYPE_PRIVATE,
+                                      mctx, &dstkey);
+       if (result != ISC_R_SUCCESS) {
+               fprintf(stderr, "Couldn't read key from %s: %s\n",
+                       keyfile, isc_result_totext(result));
+               goto failure;
+       }
+       /*
+        * Get key size in bits, convert to bytes, rounding up (?)
+        */
+       secretlen = (dst_key_size(dstkey) + 7) >> 3;
+       secretstore = isc_mem_allocate(mctx, secretlen);
+       if (secretstore == NULL)
+               fatal("out of memory");
+       isc_buffer_init(&secretbuf, secretstore, secretlen);
+       result = dst_key_tobuffer(dstkey, &secretbuf);
+       if (result != ISC_R_SUCCESS) {
+               fprintf(stderr, "Couldn't read key from %s: %s\n",
+                       keyfile, isc_result_totext(result));
+               goto failure;
+       }
+       isc_stdtime_get(&now);
+       dns_name_init(&keyname, NULL);
+       dns_name_clone(dst_key_name(dstkey), &keyname);
+       result = dns_tsigkey_create(&keyname, dns_tsig_hmacmd5_name,
+                                   secretstore, secretlen,
+                                   ISC_TRUE, NULL, now, now, mctx,
+                                   NULL, &key);
+       if (result != ISC_R_SUCCESS) {
+               printf(";; Couldn't create key %s: %s\n",
+                      keynametext, dns_result_totext(result));
+       }
+ failure:
+       if (dstkey != NULL)
+               dst_key_free(&dstkey);
+       if (secretstore != NULL)
+               isc_mem_free(mctx, secretstore);
+}
+
+/*
+ * Setup the system as a whole, reading key information and resolv.conf
+ * settings.
+ */
 void
 setup_system(void) {
        char rcinput[MXNAME];
@@ -341,16 +519,9 @@ setup_system(void) {
        char *ptr;
        dig_server_t *srv;
        dig_searchlist_t *search;
-       dig_lookup_t *l;
        isc_boolean_t get_servers;
-       isc_result_t result;
-       isc_buffer_t secretsrc;
-       isc_buffer_t secretbuf;
-       int secretsize;
-       unsigned char *secretstore;
-       isc_lex_t *lex = NULL;
-       isc_stdtime_t now;
-       
+       char *input;
+
        debug("setup_system()");
 
        if (fixeddomain[0] != 0) {
@@ -359,41 +530,34 @@ setup_system(void) {
                if (search == NULL)
                        fatal("Memory allocation failure in %s:%d",
                              __FILE__, __LINE__);
-               strncpy(search->origin, fixeddomain, MXNAME - 1);
+               strncpy(search->origin, fixeddomain,
+                       sizeof(search->origin));
+               search->origin[sizeof(search->origin)-1]=0;
+               /* XXX Check ordering, with search -vs- domain */
                ISC_LIST_PREPEND(search_list, search, link);
        }
 
        free_now = ISC_FALSE;
        get_servers = ISC_TF(server_list.head == NULL);
        fp = fopen(RESOLVCONF, "r");
+       /* XXX Use lwres resolv.conf reader */
        if (fp != NULL) {
                while (fgets(rcinput, MXNAME, fp) != 0) {
-                       ptr = strtok(rcinput, " \t\r\n");
+                       input = rcinput;
+                       ptr = next_token(&input, " \t\r\n");
                        if (ptr != NULL) {
                                if (get_servers &&
                                    strcasecmp(ptr, "nameserver") == 0) {
                                        debug("got a nameserver line");
-                                       ptr = strtok(NULL, " \t\r\n");
+                                       ptr = next_token(&input, " \t\r\n");
                                        if (ptr != NULL) {
-                                               srv = isc_mem_allocate(mctx,
-                                                  sizeof(struct dig_server));
-                                               if (srv == NULL)
-                                                       fatal("Memory "
-                                                             "allocation "
-                                                             "failure in "
-                                                             "%s:%d",
-                                                             __FILE__,
-                                                             __LINE__);
-                                                       strncpy((char *)srv->
-                                                               servername,
-                                                               ptr,
-                                                               MXNAME - 1);
-                                                       ISC_LIST_APPEND
-                                                               (server_list,
-                                                                srv, link);
+                                               srv = make_server(ptr);
+                                               ISC_LIST_APPEND
+                                                       (server_list,
+                                                        srv, link);
                                        }
                                } else if (strcasecmp(ptr, "options") == 0) {
-                                       ptr = strtok(NULL, " \t\r\n");
+                                       ptr = next_token(&input, " \t\r\n");
                                        if (ptr != NULL) {
                                                if((strncasecmp(ptr, "ndots:",
                                                            6) == 0) &&
@@ -405,10 +569,11 @@ setup_system(void) {
                                                               ndots);
                                                }
                                        }
-                               } else if ((strcasecmp(ptr, "search") == 0)
-                                          && usesearch){
-                                       while ((ptr = strtok(NULL, " \t\r\n"))
+                               } else if (strcasecmp(ptr, "search") == 0){
+                                       while ((ptr = next_token(&input, " \t\r\n"))
                                               != NULL) {
+                                               debug("adding search %s",
+                                                     ptr);
                                                search = isc_mem_allocate(
                                                   mctx, sizeof(struct
                                                                dig_server));
@@ -416,12 +581,13 @@ setup_system(void) {
                                                        fatal("Memory "
                                                              "allocation "
                                                              "failure in %s:"
-                                                             "%d", __FILE__, 
+                                                             "%d", __FILE__,
                                                              __LINE__);
                                                strncpy(search->
                                                        origin,
                                                        ptr,
-                                                       MXNAME - 1);
+                                                       MXNAME);
+                                               search->origin[MXNAME-1]=0;
                                                ISC_LIST_APPEND
                                                        (search_list,
                                                         search,
@@ -429,7 +595,8 @@ setup_system(void) {
                                        }
                                } else if ((strcasecmp(ptr, "domain") == 0) &&
                                           (fixeddomain[0] == 0 )){
-                                       while ((ptr = strtok(NULL, " \t\r\n"))
+                                       have_domain = ISC_TRUE;
+                                       while ((ptr = next_token(&input, " \t\r\n"))
                                               != NULL) {
                                                search = isc_mem_allocate(
                                                   mctx, sizeof(struct
@@ -438,12 +605,13 @@ setup_system(void) {
                                                        fatal("Memory "
                                                              "allocation "
                                                              "failure in %s:"
-                                                             "%d", __FILE__, 
+                                                             "%d", __FILE__,
                                                              __LINE__);
                                                strncpy(search->
                                                        origin,
                                                        ptr,
                                                        MXNAME - 1);
+                                               search->origin[MXNAME-1]=0;
                                                ISC_LIST_PREPEND
                                                        (search_list,
                                                         search,
@@ -459,89 +627,19 @@ setup_system(void) {
                ndots = 1;
 
        if (server_list.head == NULL) {
-               srv = isc_mem_allocate(mctx, sizeof(dig_server_t));
-               if (srv == NULL)
-                       fatal("Memory allocation failure");
-               strcpy(srv->servername, "127.0.0.1");
+               srv = make_server("127.0.0.1");
                ISC_LIST_APPEND(server_list, srv, link);
        }
 
-       for (l = ISC_LIST_HEAD(lookup_list) ;
-            l != NULL;
-            l = ISC_LIST_NEXT(l, link) ) {
-            l -> origin = ISC_LIST_HEAD(search_list);
-       }
-
-       if (keysecret[0] != 0) {
-               debug("keyring");
-               result = dns_tsigkeyring_create(mctx, &keyring);
-               check_result(result, "dns_tsigkeyring_create");
-               debug("buffer");
-               result = isc_buffer_allocate(mctx, &namebuf, MXNAME);
-               check_result(result, "isc_buffer_allocate");
-               debug("name");
-               dns_name_init(&keyname, NULL);
-               check_result(result, "dns_name_init");
-               isc_buffer_putstr(namebuf, keynametext);
-               secretsize = strlen(keysecret) * 3 / 4;
-               debug("secretstore");
-               secretstore = isc_mem_allocate(mctx, secretsize);
-               if (secretstore == NULL)
-                       fatal("Memory allocation failure in %s:%d",
-                             __FILE__, __LINE__);
-               isc_buffer_init(&secretsrc, keysecret, strlen(keysecret));
-               isc_buffer_add(&secretsrc, strlen(keysecret));
-               isc_buffer_init(&secretbuf, secretstore, secretsize);
-               debug("lex");
-               result = isc_lex_create(mctx, strlen(keysecret), &lex);
-               check_result(result, "isc_lex_create");
-               result = isc_lex_openbuffer(lex, &secretsrc);
-               check_result(result, "isc_lex_openbuffer");
-               result = isc_base64_tobuffer(lex, &secretbuf, -1);
-               if (result != ISC_R_SUCCESS) {
-                       printf(";; Couldn't create key %s: %s\n",
-                              keynametext, isc_result_totext(result));
-                       isc_lex_close(lex);
-                       isc_lex_destroy(&lex);
-                       goto SYSSETUP_FAIL;
-               }
-               secretsize = isc_buffer_usedlength(&secretbuf);
-               debug("close");
-               isc_lex_close(lex);
-               isc_lex_destroy(&lex);
-               isc_stdtime_get(&now);
-               
-               debug("namefromtext");
-               result = dns_name_fromtext(&keyname, namebuf,
-                                          dns_rootname, ISC_FALSE,
-                                          namebuf);
-               if (result != ISC_R_SUCCESS) {
-                       printf (";; Couldn't create key %s: %s\n",
-                               keynametext, dns_result_totext(result));
-                       goto SYSSETUP_FAIL;
-               }
-               debug("tsigkey");
-               result = dns_tsigkey_create(&keyname, dns_tsig_hmacmd5_name,
-                                           secretstore, secretsize,
-                                           ISC_TRUE, NULL, now, now, mctx,
-                                           keyring, &key);
-               if (result != ISC_R_SUCCESS) {
-                       printf(";; Couldn't create key %s: %s\n",
-                              keynametext, dns_result_totext(result));
-               }
-               isc_mem_free(mctx, secretstore);
-               dns_name_invalidate(&keyname);
-               isc_buffer_free(&namebuf);
-               return;
-       SYSSETUP_FAIL:
-               isc_mem_free(mctx, secretstore);
-               dns_name_invalidate(&keyname);
-               isc_buffer_free(&namebuf);
-               dns_tsigkeyring_destroy(&keyring);
-               return;
-       }
+       if (keyfile[0] != 0)
+               setup_file_key();
+       else if (keysecret[0] != 0)
+               setup_text_key();
 }
-       
+
+/*
+ * Setup the ISC and DNS libraries for use by the system.
+ */
 void
 setup_libs(void) {
        isc_result_t result;
@@ -555,9 +653,6 @@ setup_libs(void) {
         */
        srandom(getpid() + (int)&setup_libs);
 
-       result = isc_app_start();
-       check_result(result, "isc_app_start");
-
        result = isc_net_probeipv4();
        check_result(result, "isc_net_probeipv4");
 
@@ -596,8 +691,17 @@ setup_libs(void) {
         */
        isc_mempool_setfreemax(commctx, 6);
        isc_mempool_setfillcount(commctx, 2);
+
+       result = isc_mutex_init(&lookup_lock);
+       check_result(result, "isc_mutex_init");
+
+       dns_result_register();
 }
 
+/*
+ * Add EDNS0 option record to a message.  Currently, the only supported
+ * option is UDP buffer size.
+ */
 static void
 add_opt(dns_message_t *msg, isc_uint16_t udpsize) {
        dns_rdataset_t *rdataset = NULL;
@@ -613,7 +717,7 @@ add_opt(dns_message_t *msg, isc_uint16_t udpsize) {
        check_result(result, "dns_message_gettemprdatalist");
        result = dns_message_gettemprdata(msg, &rdata);
        check_result(result, "dns_message_gettemprdata");
-       
+
        debug("setting udp size of %d", udpsize);
        rdatalist->type = dns_rdatatype_opt;
        rdatalist->covers = 0;
@@ -628,6 +732,10 @@ add_opt(dns_message_t *msg, isc_uint16_t udpsize) {
        check_result(result, "dns_message_setopt");
 }
 
+/*
+ * Add a question section to a message, asking for the specified name,
+ * type, and class.
+ */
 static void
 add_question(dns_message_t *message, dns_name_t *name,
             dns_rdataclass_t rdclass, dns_rdatatype_t rdtype)
@@ -635,7 +743,7 @@ add_question(dns_message_t *message, dns_name_t *name,
        dns_rdataset_t *rdataset;
        isc_result_t result;
 
-       debug("add_question()"); 
+       debug("add_question()");
        rdataset = NULL;
        result = dns_message_gettemprdataset(message, &rdataset);
        check_result(result, "dns_message_gettemprdataset()");
@@ -645,76 +753,177 @@ add_question(dns_message_t *message, dns_name_t *name,
 }
 
 /*
- * Return ISC_TRUE if we're in the process of shutting down on the
- * return.
+ * Check if we're done with all the queued lookups, which is true iff
+ * all sockets, sends, and recvs are accounted for (counters == 0),
+ * and the lookup list is empty.
+ * If we are done, pass control back out to dighost_shutdown() (which is
+ * part of dig.c, host.c, or nslookup.c) to either shutdown the system as
+ * a whole or reseed the lookup list.
+ */
+static void
+check_if_done(void) {
+       debug("check_if_done()");
+       debug("list %s", ISC_LIST_EMPTY(lookup_list) ? "empty" : "full");
+       if (ISC_LIST_EMPTY(lookup_list) && current_lookup == NULL &&
+           sendcount == 0) {
+               INSIST(sockcount == 0);
+               INSIST(recvcount == 0);
+               debug("shutting down");
+               dighost_shutdown();
+       }
+}
+
+/*
+ * Clear out a query when we're done with it.  WARNING: This routine
+ * WILL invalidate the query pointer.
+ */
+static void
+clear_query(dig_query_t *query) {
+       dig_lookup_t *lookup;
+
+       REQUIRE(query != NULL);
+
+       debug("clear_query(%p)",query);
+
+       lookup = query->lookup;
+
+       if (lookup->current_query == query)
+               lookup->current_query = NULL;
+
+       ISC_LIST_UNLINK(lookup->q, query, link);
+       if (ISC_LINK_LINKED(&query->recvbuf, link))
+               ISC_LIST_DEQUEUE(query->recvlist, &query->recvbuf,
+                                link);
+       if (ISC_LINK_LINKED(&query->lengthbuf, link))
+               ISC_LIST_DEQUEUE(query->lengthlist, &query->lengthbuf,
+                                link);
+       INSIST(query->recvspace != NULL);
+       if (query->sock != NULL) {
+               isc_socket_detach(&query->sock);
+               sockcount--;
+               debug("sockcount=%d", sockcount);
+       }
+       isc_mempool_put(commctx, query->recvspace);
+       isc_buffer_invalidate(&query->recvbuf);
+       isc_buffer_invalidate(&query->lengthbuf);
+       isc_mem_free(mctx, query);
+}
+
+/*
+ * Try and clear out a lookup if we're done with it.  Return ISC_TRUE if
+ * the lookup was successfully cleared.  If ISC_TRUE is returned, the
+ * lookup pointer has been invalidated.
  */
 static isc_boolean_t
-check_next_lookup(dig_lookup_t *lookup) {
-       dig_lookup_t *next;
-       dig_query_t *query;
-       isc_boolean_t still_working=ISC_FALSE;
-       
-       if (free_now)
-               return (ISC_TRUE);
+try_clear_lookup(dig_lookup_t *lookup) {
+       dig_server_t *s;
+       dig_query_t *q;
+       void *ptr;
 
-       debug("check_next_lookup(%p)", lookup);
-       for (query = ISC_LIST_HEAD(lookup->q);
-            query != NULL;
-            query = ISC_LIST_NEXT(query, link)) {
-               if (query->working) {
-                       debug("still have a worker", stderr);
-                       still_working=ISC_TRUE;
+       REQUIRE(lookup != NULL);
+
+       debug("try_clear_lookup(%p)", lookup);
+
+       if (ISC_LIST_HEAD(lookup->q) != NULL) {
+               if (debugging) {
+                       q = ISC_LIST_HEAD(lookup->q);
+                       while (q != NULL) {
+                               debug("query to %s still pending",
+                                      q->servname);
+                               q = ISC_LIST_NEXT(q, link);
+                       }
+               return (ISC_FALSE);
                }
        }
-       if (still_working)
-               return (ISC_FALSE);
+       /*
+        * At this point, we know there are no queries on the lookup,
+        * so can make it go away also.
+        */
+       debug("cleared");
+       s = ISC_LIST_HEAD(lookup->my_server_list);
+       while (s != NULL) {
+               debug("freeing server %p belonging to %p",
+                     s, lookup);
+               ptr = s;
+               s = ISC_LIST_NEXT(s, link);
+               ISC_LIST_DEQUEUE(lookup->my_server_list,
+                                (dig_server_t *)ptr, link);
+               isc_mem_free(mctx, ptr);
+       }
+       if (lookup->sendmsg != NULL)
+               dns_message_destroy(&lookup->sendmsg);
+       if (lookup->querysig != NULL) {
+               debug("freeing buffer %p", lookup->querysig);
+               isc_buffer_free(&lookup->querysig);
+       }
+       if (lookup->timer != NULL)
+               isc_timer_detach(&lookup->timer);
+       if (lookup->sendspace != NULL)
+               isc_mempool_put(commctx, lookup->sendspace);
 
-       debug("have %d retries left for %s",
-              lookup->retries-1, lookup->textname);
-       debug("lookup %s pending", lookup->pending ? "is" : "is not");
+       isc_mem_free(mctx, lookup);
+       return (ISC_TRUE);
+}
 
-       next = ISC_LIST_NEXT(lookup, link);
-       
-       if (lookup->tcp_mode) {
-               if (next == NULL) {
-                       debug("shutting down", stderr);
-                       dighost_shutdown();
-                       return (ISC_TRUE);
-               }
-               if (next->sendmsg == NULL) {
-                       debug("setting up for TCP");
-                       setup_lookup(next);
-                       do_lookup(next);
-               }
+
+/*
+ * If we can, start the next lookup in the queue running.
+ * This assumes that the lookup on the head of the queue hasn't been
+ * started yet.  It also removes the lookup from the head of the queue,
+ * setting the current_lookup pointer pointing to it.
+ */
+void
+start_lookup(void) {
+       debug("start_lookup()");
+       if (cancel_now)
+               return;
+
+       /*
+        * If there's a current lookup running, we really shouldn't get
+        * here.
+        */
+       INSIST(current_lookup == NULL);
+
+       current_lookup = ISC_LIST_HEAD(lookup_list);
+       /*
+        * Put the current lookup somewhere so cancel_all can find it
+        */
+       if (current_lookup != NULL) {
+               ISC_LIST_DEQUEUE(lookup_list, current_lookup, link);
+               setup_lookup(current_lookup);
+               do_lookup(current_lookup);
        } else {
-               if (!lookup->pending) {
-                       if (next == NULL) {
-                               debug("shutting down", stderr);
-                               dighost_shutdown();
-                               return (ISC_TRUE);
-                       }
-                       if (next->sendmsg == NULL) {
-                               debug("setting up for UDP");
-                               setup_lookup(next);
-                               do_lookup(next);
-                       }
-               } else {
-                       if (lookup->retries > 1) {
-                               debug("retrying");
-                               lookup->retries --;
-                               if (lookup->timer != NULL)
-                                       isc_timer_detach(&lookup->timer);
-                               send_udp(lookup);
-                       } else {
-                               debug("cancelling");
-                               return(cancel_lookup(lookup));
-                       }
-               }
+               check_if_done();
        }
-       return (ISC_FALSE);
 }
 
+/*
+ * If we can, clear the current lookup and start the next one running.
+ * This calls try_clear_lookup, so may invalidate the lookup pointer.
+ */
+static void
+check_next_lookup(dig_lookup_t *lookup) {
+
+       INSIST(!free_now);
+
+       debug("check_next_lookup(%p)", lookup);
 
+       if (ISC_LIST_HEAD(lookup->q) != NULL) {
+               debug("still have a worker");
+               return;
+       }
+       if (try_clear_lookup(lookup)) {
+               current_lookup = NULL;
+               start_lookup();
+       }
+}
+
+/*
+ * Create and queue a new lookup as a followup to the current lookup,
+ * based on the supplied message and section.  This is used in trace and
+ * name server search modes to start a new lookup using servers from
+ * NS records in a reply.
+ */
 static void
 followup_lookup(dns_message_t *msg, dig_query_t *query,
                dns_section_t section) {
@@ -729,16 +938,17 @@ followup_lookup(dns_message_t *msg, dig_query_t *query,
        isc_boolean_t success = ISC_FALSE;
        int len;
 
-       debug("followup_lookup()"); 
-       if (free_now)
-               return;
+       INSIST(!free_now);
+
+       debug("followup_lookup()");
        result = dns_message_firstname(msg,section);
+
        if (result != ISC_R_SUCCESS) {
                debug("firstname returned %s",
                        isc_result_totext(result));
                if ((section == DNS_SECTION_ANSWER) &&
                    (query->lookup->trace || query->lookup->ns_search_only))
-                       followup_lookup (msg, query, DNS_SECTION_AUTHORITY);
+                       followup_lookup(msg, query, DNS_SECTION_AUTHORITY);
                 return;
        }
 
@@ -757,7 +967,7 @@ followup_lookup(dns_message_t *msg, dig_query_t *query,
                                       rdata.type);
                                if ((rdata.type == dns_rdatatype_ns) &&
                                    (!query->lookup->trace_root ||
-                                    (query->lookup->nsfound < ROOTNS)))
+                                    (query->lookup->nsfound < MXSERV)))
                                {
                                        query->lookup->nsfound++;
                                        result = isc_buffer_allocate(mctx, &b,
@@ -780,13 +990,12 @@ followup_lookup(dns_message_t *msg, dig_query_t *query,
                                        if (!success) {
                                                success = ISC_TRUE;
                                                lookup_counter++;
+                                               cancel_lookup(query->lookup);
                                                lookup = requeue_lookup
                                                        (query->lookup,
                                                         ISC_FALSE);
                                                lookup->doing_xfr = ISC_FALSE;
                                                lookup->defname = ISC_FALSE;
-                                               lookup->use_my_server_list = 
-                                                       ISC_TRUE;
                                                if (section ==
                                                    DNS_SECTION_ANSWER) {
                                                      lookup->trace =
@@ -803,20 +1012,11 @@ followup_lookup(dns_message_t *msg, dig_query_t *query,
                                                        lookup->ns_search_only;
                                                }
                                                lookup->trace_root = ISC_FALSE;
-                                               ISC_LIST_INIT(lookup->
-                                                             my_server_list);
                                        }
-                                       srv = isc_mem_allocate(mctx,
-                                                      sizeof(struct dig_server));
-                                       if (srv == NULL)
-                                               fatal("Memory allocation "
-                                                     "failure in %s:%d",
-                                                     __FILE__, __LINE__);
-                                       strncpy(srv->servername, 
-                                               (char *)r.base, len);
-                                       srv->servername[len] = 0;
+                                       r.base[len] = 0;
+                                       srv = make_server((char *)r.base);
                                        debug("adding server %s",
-                                              srv->servername);
+                                             srv->servername);
                                        ISC_LIST_APPEND
                                                (lookup->my_server_list,
                                                 srv, link);
@@ -825,7 +1025,7 @@ followup_lookup(dns_message_t *msg, dig_query_t *query,
                                loopresult = dns_rdataset_next(rdataset);
                        }
                }
-               result = dns_message_nextname (msg, section);
+               result = dns_message_nextname(msg, section);
                if (result != ISC_R_SUCCESS)
                        break;
        }
@@ -834,30 +1034,43 @@ followup_lookup(dns_message_t *msg, dig_query_t *query,
                followup_lookup(msg, query, DNS_SECTION_AUTHORITY);
 }
 
-static void
+/*
+ * Create and queue a new lookup using the next origin from the origin
+ * list, read in setup_system().
+ */
+static isc_boolean_t
 next_origin(dns_message_t *msg, dig_query_t *query) {
        dig_lookup_t *lookup;
 
        UNUSED(msg);
 
-       debug("next_origin()"); 
-       if (free_now)
-               return;
+       INSIST(!free_now);
+
+       debug("next_origin()");
        debug("following up %s", query->lookup->textname);
 
-       if (query->lookup->origin == NULL) {
+       if (!usesearch)
+               /*
+                * We're not using a search list, so don't even think
+                * about finding the next entry.
+                */
+               return (ISC_FALSE);
+       if (query->lookup->origin == NULL)
                /*
                 * Then we just did rootorg; there's nothing left.
                 */
-               debug("made it to the root with nowhere to go");
-               return;
-       }
+               return (ISC_FALSE);
+       cancel_lookup(query->lookup);
        lookup = requeue_lookup(query->lookup, ISC_TRUE);
        lookup->defname = ISC_FALSE;
        lookup->origin = ISC_LIST_NEXT(query->lookup->origin, link);
+       return (ISC_TRUE);
 }
 
-
+/*
+ * Insert an SOA record into the sendmessage in a lookup.  Used for
+ * creating IXFR queries.
+ */
 static void
 insert_soa(dig_lookup_t *lookup) {
        isc_result_t result;
@@ -866,7 +1079,7 @@ insert_soa(dig_lookup_t *lookup) {
        dns_rdatalist_t *rdatalist = NULL;
        dns_rdataset_t *rdataset = NULL;
        dns_name_t *soaname = NULL;
-       
+
        debug("insert_soa()");
        soa.mctx = mctx;
        soa.serial = lookup->ixfr_serial;
@@ -874,7 +1087,7 @@ insert_soa(dig_lookup_t *lookup) {
        soa.retry = 1;
        soa.expire = 1;
        soa.minimum = 1;
-       soa.common.rdclass = dns_rdataclass_in;
+       soa.common.rdclass = lookup->rdclass;
        soa.common.rdtype = dns_rdatatype_soa;
 
        dns_name_init(&soa.origin, NULL);
@@ -882,26 +1095,27 @@ insert_soa(dig_lookup_t *lookup) {
 
        dns_name_clone(lookup->name, &soa.origin);
        dns_name_clone(lookup->name, &soa.mname);
-       
+
        isc_buffer_init(&lookup->rdatabuf, lookup->rdatastore,
-                       MXNAME);
+                       sizeof(lookup->rdatastore));
 
        result = dns_message_gettemprdata(lookup->sendmsg, &rdata);
        check_result(result, "dns_message_gettemprdata");
-       result = dns_rdata_fromstruct(rdata, dns_rdataclass_in,
+
+       result = dns_rdata_fromstruct(rdata, lookup->rdclass,
                                      dns_rdatatype_soa, &soa,
                                      &lookup->rdatabuf);
        check_result(result, "isc_rdata_fromstruct");
 
        result = dns_message_gettemprdatalist(lookup->sendmsg, &rdatalist);
        check_result(result, "dns_message_gettemprdatalist");
-       
+
        result = dns_message_gettemprdataset(lookup->sendmsg, &rdataset);
        check_result(result, "dns_message_gettemprdataset");
 
        dns_rdatalist_init(rdatalist);
        rdatalist->type = dns_rdatatype_soa;
-       rdatalist->rdclass = dns_rdataclass_in;
+       rdatalist->rdclass = lookup->rdclass;
        rdatalist->covers = dns_rdatatype_soa;
        rdatalist->ttl = 1;
        ISC_LIST_INIT(rdatalist->rdata);
@@ -919,29 +1133,26 @@ insert_soa(dig_lookup_t *lookup) {
        dns_message_addname(lookup->sendmsg, soaname, DNS_SECTION_AUTHORITY);
 }
 
+/*
+ * Setup the supplied lookup structure, making it ready to start sending
+ * queries to servers.  Create and initialize the message to be sent as
+ * well as the query structures and buffer space for the replies.  If the
+ * server list is empty, clone it from the system default list.
+ */
 void
 setup_lookup(dig_lookup_t *lookup) {
-       isc_result_t result, res2;
+       isc_result_t result;
        int len;
-       dns_rdatatype_t rdtype;
-       dns_rdataclass_t rdclass;
        dig_server_t *serv;
        dig_query_t *query;
        isc_region_t r;
-       isc_constregion_t tr;
        isc_buffer_t b;
        char store[MXNAME];
-       
-       REQUIRE(lookup != NULL);
 
-       debug("setup_lookup(%p)",lookup);
-
-       if (free_now)
-               return;
+       REQUIRE(lookup != NULL);
+       INSIST(!free_now);
 
-       debug("setting up for looking up %s @%p->%p", 
-               lookup->textname, lookup,
-               lookup->link.next);
+       debug("setup_lookup(%p)", lookup);
 
        result = dns_message_create(mctx, DNS_MESSAGE_INTENTRENDER,
                                    &lookup->sendmsg);
@@ -952,6 +1163,10 @@ setup_lookup(dig_lookup_t *lookup) {
                lookup_counter = 0;
        }
 
+       if (ISC_LIST_EMPTY(lookup->my_server_list)) {
+               debug("cloning server list");
+               clone_server_list(server_list, &lookup->my_server_list);
+       }
        result = dns_message_gettempname(lookup->sendmsg, &lookup->name);
        check_result(result, "dns_message_gettempname");
        dns_name_init(lookup->name, NULL);
@@ -961,15 +1176,26 @@ setup_lookup(dig_lookup_t *lookup) {
        isc_buffer_init(&lookup->onamebuf, lookup->onamespace,
                        sizeof(lookup->onamespace));
 
+       /*
+        * If the name has too many dots, force the origin to be NULL
+        * (which produces an absolute lookup).  Otherwise, take the origin
+        * we have if there's one in the struct already.  If it's NULL,
+        * take the first entry in the searchlist iff either usesearch
+        * is TRUE or we got a domain line in the resolv.conf file.
+        */
+       /* XXX New search here? */
        if ((count_dots(lookup->textname) >= ndots) || lookup->defname)
-               lookup->origin = NULL; /* Force root lookup */
-       debug("lookup->origin = %p", lookup->origin);
+               lookup->origin = NULL; /* Force abs lookup */
+       else if (lookup->origin == NULL && lookup->new_search &&
+                (usesearch || have_domain))
+               lookup->origin = ISC_LIST_HEAD(search_list);
        if (lookup->origin != NULL) {
                debug("trying origin %s", lookup->origin->origin);
                result = dns_message_gettempname(lookup->sendmsg,
                                                 &lookup->oname);
                check_result(result, "dns_message_gettempname");
                dns_name_init(lookup->oname, NULL);
+               /* XXX Helper funct to conv char* to name? */
                len = strlen(lookup->origin->origin);
                isc_buffer_init(&b, lookup->origin->origin, len);
                isc_buffer_add(&b, len);
@@ -980,26 +1206,26 @@ setup_lookup(dig_lookup_t *lookup) {
                                                &lookup->name);
                        dns_message_puttempname(lookup->sendmsg,
                                                &lookup->oname);
-                       fatal("%s is not a legal name syntax (%s)",
+                       fatal("'%s' is not in legal name syntax (%s)",
                              lookup->origin->origin,
                              dns_result_totext(result));
                }
-               if (!lookup->trace_root) {
+               if (lookup->trace_root) {
+                       dns_name_clone(dns_rootname, lookup->name);
+               } else {
                        len = strlen(lookup->textname);
                        isc_buffer_init(&b, lookup->textname, len);
                        isc_buffer_add(&b, len);
                        result = dns_name_fromtext(lookup->name, &b,
-                                                  lookup->oname, ISC_FALSE, 
+                                                  lookup->oname, ISC_FALSE,
                                                   &lookup->namebuf);
-               } else {
-                       dns_name_clone(dns_rootname, lookup->name);
-               }                       
+               }
                if (result != ISC_R_SUCCESS) {
                        dns_message_puttempname(lookup->sendmsg,
                                                &lookup->name);
                        dns_message_puttempname(lookup->sendmsg,
                                                &lookup->oname);
-                       fatal("%s is not a legal name syntax (%s)",
+                       fatal("'%s' is not in legal name syntax (%s)",
                              lookup->textname, dns_result_totext(result));
                }
                dns_message_puttempname(lookup->sendmsg, &lookup->oname);
@@ -1020,23 +1246,17 @@ setup_lookup(dig_lookup_t *lookup) {
                        dns_message_puttempname(lookup->sendmsg,
                                                &lookup->name);
                        isc_buffer_init(&b, store, MXNAME);
-                       res2 = dns_name_totext(dns_rootname, ISC_FALSE, &b);
-                       check_result(res2, "dns_name_totext");
-                       isc_buffer_usedregion(&b, &r);
-                       fatal("%s/%.*s is not a legal name syntax "
-                             "(%s)", lookup->textname, (int)r.length,
-                             (char *)r.base, dns_result_totext(result));
+                       fatal("'%s' is not a legal name syntax "
+                             "(%s)", lookup->textname,
+                             dns_result_totext(result));
                }
-       }               
-       isc_buffer_init(&b, store, MXNAME);
+       }
+       isc_buffer_init(&b, store, sizeof(store));
+       /* XXX Move some of this into function, dns_name_format. */
        dns_name_totext(lookup->name, ISC_FALSE, &b);
        isc_buffer_usedregion(&b, &r);
        trying((int)r.length, (char *)r.base, lookup);
-       ENSURE(dns_name_isabsolute(lookup->name));
-       if (lookup->rctext[0] == 0)
-               strcpy(lookup->rctext, "IN");
-       if (lookup->rttext[0] == 0)
-               strcpy(lookup->rttext, "A");
+       INSIST(dns_name_isabsolute(lookup->name));
 
        lookup->sendmsg->id = (unsigned short)(random() & 0xFFFF);
        lookup->sendmsg->opcode = dns_opcode_query;
@@ -1050,6 +1270,7 @@ setup_lookup(dig_lookup_t *lookup) {
                lookup->sendmsg->flags |= DNS_MESSAGEFLAG_RD;
        }
 
+       /* XXX aaflag */
        if (lookup->aaonly) {
                debug("AA query");
                lookup->sendmsg->flags |= DNS_MESSAGEFLAG_AA;
@@ -1068,45 +1289,32 @@ setup_lookup(dig_lookup_t *lookup) {
        dns_message_addname(lookup->sendmsg, lookup->name,
                            DNS_SECTION_QUESTION);
 
-       if (lookup->trace_root) {
-               debug("doing trace_root");
-               tr.base = "SOA";
-               tr.length = 3;
-       } else {
-               tr.base = lookup->rttext;
-               tr.length = strlen(lookup->rttext);
-       }
-       debug("data type is %s", lookup->rttext);
-       result = dns_rdatatype_fromtext(&rdtype, (isc_textregion_t *)&tr);
-       check_result(result, "dns_rdatatype_fromtext");
-       if ((rdtype == dns_rdatatype_axfr) ||
-           (rdtype == dns_rdatatype_ixfr)) {
+       if (lookup->trace_root)
+               lookup->rdtype = dns_rdatatype_soa;
+
+       if ((lookup->rdtype == dns_rdatatype_axfr) ||
+           (lookup->rdtype == dns_rdatatype_ixfr)) {
                lookup->doing_xfr = ISC_TRUE;
                /*
                 * Force TCP mode if we're doing an xfr.
+                * XXX UDP ixfr's would be useful
                 */
                lookup->tcp_mode = ISC_TRUE;
        }
-       if (lookup->trace_root) {
-               tr.base = "IN";
-               tr.length = 2;
-       } else {
-               tr.base = lookup->rctext;
-               tr.length = strlen(lookup->rctext);
-       }
-       result = dns_rdataclass_fromtext(&rdclass, (isc_textregion_t *)&tr);
-       check_result(result, "dns_rdataclass_fromtext");
-       add_question(lookup->sendmsg, lookup->name, rdclass, rdtype);
+       add_question(lookup->sendmsg, lookup->name, lookup->rdclass,
+                    lookup->rdtype);
 
-       if (rdtype == dns_rdatatype_ixfr)
+       /* XXX add_soa */
+       if (lookup->rdtype == dns_rdatatype_ixfr)
                insert_soa(lookup);
 
+       /* XXX Insist this? */
+       lookup->tsigctx = NULL;
+       lookup->querysig = NULL;
        if (key != NULL) {
                debug("initializing keys");
                result = dns_message_settsigkey(lookup->sendmsg, key);
                check_result(result, "dns_message_settsigkey");
-               lookup->tsigctx = NULL;
-               lookup->querysig = NULL;
        }
 
        lookup->sendspace = isc_mempool_get(commctx);
@@ -1117,9 +1325,9 @@ setup_lookup(dig_lookup_t *lookup) {
        isc_buffer_init(&lookup->sendbuf, lookup->sendspace, COMMSIZE);
        result = dns_message_renderbegin(lookup->sendmsg, &lookup->sendbuf);
        check_result(result, "dns_message_renderbegin");
-       if (lookup->udpsize > 0) {
+       if (lookup->udpsize > 0)
                add_opt(lookup->sendmsg, lookup->udpsize);
-       }
+
        result = dns_message_rendersection(lookup->sendmsg,
                                           DNS_SECTION_QUESTION, 0);
        check_result(result, "dns_message_rendersection");
@@ -1132,11 +1340,8 @@ setup_lookup(dig_lookup_t *lookup) {
 
        lookup->pending = ISC_FALSE;
 
-       if (lookup->use_my_server_list)
-               serv = ISC_LIST_HEAD(lookup->my_server_list);
-       else
-               serv = ISC_LIST_HEAD(server_list);
-       for (; serv != NULL;
+       for (serv = ISC_LIST_HEAD(lookup->my_server_list);
+            serv != NULL;
             serv = ISC_LIST_NEXT(serv, link)) {
                query = isc_mem_allocate(mctx, sizeof(dig_query_t));
                if (query == NULL)
@@ -1145,14 +1350,15 @@ setup_lookup(dig_lookup_t *lookup) {
                debug("create query %p linked to lookup %p",
                       query, lookup);
                query->lookup = lookup;
-               query->working = ISC_FALSE;
                query->waiting_connect = ISC_FALSE;
+               query->recv_made = ISC_FALSE;
                query->first_pass = ISC_TRUE;
                query->first_soa_rcvd = ISC_FALSE;
                query->second_rr_rcvd = ISC_FALSE;
                query->second_rr_serial = 0;
                query->servname = serv->servername;
-               ISC_LIST_INIT(query->sendlist);
+               query->name_count = 0;
+               query->rr_count = 0;
                ISC_LIST_INIT(query->recvlist);
                ISC_LIST_INIT(query->lengthlist);
                query->sock = NULL;
@@ -1166,208 +1372,355 @@ setup_lookup(dig_lookup_t *lookup) {
 
                ISC_LIST_ENQUEUE(lookup->q, query, link);
        }
+       /* XXX qrflag, print_query, etc... */
        if (!ISC_LIST_EMPTY(lookup->q) && qr) {
                printmessage(ISC_LIST_HEAD(lookup->q), lookup->sendmsg,
                             ISC_TRUE);
        }
-}      
+}
 
+/*
+ * Event handler for send completion.  Track send counter, and clear out
+ * the query if the send was canceled.
+ */
 static void
-send_done(isc_task_t *task, isc_event_t *event) {
-       UNUSED(task);
+send_done(isc_task_t *_task, isc_event_t *event) {
+       REQUIRE(event->ev_type == ISC_SOCKEVENT_SENDDONE);
+
+       UNUSED(_task);
+
+       LOCK_LOOKUP;
 
        isc_event_free(&event);
 
        debug("send_done()");
+       sendcount--;
+       debug("sendcount=%d", sendcount);
+       INSIST(sendcount >= 0);
+       check_if_done();
+       UNLOCK_LOOKUP;
 }
 
 /*
- * Return ISC_TRUE if we're in the process of shutting down
+ * Cancel a lookup, sending isc_socket_cancel() requests to all outstanding
+ * IO sockets.  The cancel handlers should take care of cleaning up the
+ * query and lookup structures
  */
-static isc_boolean_t
+static void
 cancel_lookup(dig_lookup_t *lookup) {
-       dig_query_t *query = NULL;
+       dig_query_t *query, *next;
 
        debug("cancel_lookup()");
-       for (query = ISC_LIST_HEAD(lookup->q);
-            query != NULL;
-            query = ISC_LIST_NEXT(query, link)) {
-               if (query->working) {
-                       debug("cancelling a worker");
-               }
+       query = ISC_LIST_HEAD(lookup->q);
+       while (query != NULL) {
+               next = ISC_LIST_NEXT(query, link);
                if (query->sock != NULL) {
                        isc_socket_cancel(query->sock, global_task,
                                          ISC_SOCKCANCEL_ALL);
-                       isc_socket_detach(&query->sock);
-                       sockcount--;
-                       debug("socket = %d", sockcount);
+                       check_if_done();
+               } else {
+                       clear_query(query);
                }
+               query = next;
        }
+       if (lookup->timer != NULL)
+               isc_timer_detach(&lookup->timer);
        lookup->pending = ISC_FALSE;
        lookup->retries = 0;
-       return(check_next_lookup(lookup));
 }
 
 static void
-recv_done(isc_task_t *task, isc_event_t *event);
+bringup_timer(dig_query_t *query, unsigned int default_timeout) {
+       dig_lookup_t *l;
+       unsigned int local_timeout;
+       isc_result_t result;
+
+       debug("bringup_timer()");
+       /*
+        * If the timer already exists, that means we're calling this
+        * a second time (for a retry).  Don't need to recreate it,
+        * just reset it.
+        */
+       l = query->lookup;
+       if (ISC_LIST_NEXT(query, link) != NULL)
+               local_timeout = SERVER_TIMEOUT;
+       else {
+               if (timeout == 0) {
+                       local_timeout = default_timeout;
+               } else
+                       local_timeout = timeout;
+       }
+       debug("have local timeout of %d", local_timeout);
+       isc_interval_set(&l->interval, local_timeout, 0);
+       if (l->timer != NULL)
+               isc_timer_detach(&l->timer);
+       result = isc_timer_create(timermgr,
+                                 isc_timertype_once,
+                                 NULL,
+                                 &l->interval,
+                                 global_task,
+                                 connect_timeout,
+                                 l, &l->timer);
+       check_result(result, "isc_timer_create");
+}      
 
 static void
-connect_timeout(isc_task_t *task, isc_event_t *event);
+connect_done(isc_task_t *task, isc_event_t *event);
 
-void
-send_udp(dig_lookup_t *lookup) {
-       dig_query_t *query;
+/*
+ * Unlike send_udp, this can't be called multiple times with the same
+ * query.  When we retry TCP, we requeue the whole lookup, which should
+ * start anew.
+ */
+static void
+send_tcp_connect(dig_query_t *query) {
        isc_result_t result;
-       unsigned int local_timeout;
+       dig_query_t *next;
+       dig_lookup_t *l;
 
-       debug("send_udp()");
+       debug("send_tcp_connect(%lx)", query);
 
-       if (timeout != INT_MAX) {
-               if (timeout == 0) {
-                       if (lookup->tcp_mode)
-                               local_timeout = TCP_TIMEOUT;
-                       else
-                               local_timeout = UDP_TIMEOUT;
-               } else
-                       local_timeout = timeout;
-               debug ("have local timeout of %d", local_timeout);
-               isc_interval_set(&lookup->interval, local_timeout, 0);
-               result = isc_timer_create(timermgr, isc_timertype_once, NULL,
-                                         &lookup->interval, global_task,
-                                         connect_timeout, lookup,
-                                         &lookup->timer);
-               check_result(result, "isc_timer_create");
-       }
-for (query = ISC_LIST_HEAD(lookup->q);
-            query != NULL;
-            query = ISC_LIST_NEXT(query, link)) {
-               debug("working on lookup %p, query %p",
-                      query->lookup, query);
-               ISC_LIST_ENQUEUE(query->recvlist, &query->recvbuf, link);
-               query->working = ISC_TRUE;
+       l = query->lookup;
+       query->waiting_connect = ISC_TRUE;
+       query->lookup->current_query = query;
+       get_address(query->servname, port, &query->sockaddr);
+       
+       if (specified_source &&
+           (isc_sockaddr_pf(&query->sockaddr) !=
+            isc_sockaddr_pf(&bind_address))) {
+               printf(";; Skipping server %s, incompatible "
+                      "address family\n", query->servname);
+               query->waiting_connect = ISC_FALSE;
+               next = ISC_LIST_NEXT(query, link);
+               l = query->lookup;
+               clear_query(query);
+               if (next == NULL) {
+                       printf(";; No acceptable nameservers\n");
+                       check_next_lookup(l);
+                       return;
+               }
+               send_tcp_connect(next);
+               return;
+       }
+       INSIST(query->sock == NULL);
+       result = isc_socket_create(socketmgr,
+                                  isc_sockaddr_pf(&query->sockaddr),
+                                  isc_sockettype_tcp, &query->sock) ;
+       check_result(result, "isc_socket_create");
+       sockcount++;
+       debug("sockcount=%d",sockcount);
+       if (specified_source)
+               result = isc_socket_bind(query->sock, &bind_address);
+       else {
+               if (isc_sockaddr_pf(&query->sockaddr) == AF_INET)
+                       isc_sockaddr_any(&bind_any);
+               else
+                       isc_sockaddr_any6(&bind_any);
+               result = isc_socket_bind(query->sock, &bind_any);
+       }
+       check_result(result, "isc_socket_bind");
+       bringup_timer(query, TCP_TIMEOUT);
+       result = isc_socket_connect(query->sock, &query->sockaddr,
+                                   global_task, connect_done, query);
+       check_result(result, "isc_socket_connect");
+       /*
+        * If we're doing a nameserver search, we need to immediately
+        * bring up all the queries.  Do it here.
+        */
+       if (l->ns_search_only) {
+               debug("sending next, since searching");
+               next = ISC_LIST_NEXT(query, link);
+               if (next != NULL)
+                       send_tcp_connect(next);
+       }
+}
+
+/*
+ * Send a UDP packet to the remote nameserver, possible starting the
+ * recv action as well.  Also make sure that the timer is running and
+ * is properly reset.
+ */
+static void
+send_udp(dig_query_t *query) {
+       dig_lookup_t *l = NULL;
+       dig_query_t *next;
+       isc_result_t result;
+
+       debug("send_udp(%lx)", query);
+
+       l = query->lookup;
+       bringup_timer(query, UDP_TIMEOUT);
+       l->current_query = query;
+       debug("working on lookup %p, query %p",
+             query->lookup, query);
+       if (!query->recv_made) {
+               /* XXX Check the sense of this, need assertion? */
+               query->waiting_connect = ISC_FALSE;
+               get_address(query->servname, port, &query->sockaddr);
+
+               result = isc_socket_create(socketmgr,
+                                          isc_sockaddr_pf(&query->sockaddr),
+                                          isc_sockettype_udp, &query->sock);
+               check_result(result, "isc_socket_create");
+               sockcount++;
+               debug("sockcount=%d", sockcount);
+               if (specified_source) {
+                       result = isc_socket_bind(query->sock, &bind_address);
+               } else {
+                       isc_sockaddr_anyofpf(&bind_any,
+                                       isc_sockaddr_pf(&query->sockaddr));
+                       result = isc_socket_bind(query->sock, &bind_any);
+               }
+               check_result(result, "isc_socket_bind");
+
+               query->recv_made = ISC_TRUE;
+               ISC_LIST_ENQUEUE(query->recvlist, &query->recvbuf,
+                                link);
                debug("recving with lookup=%p, query=%p, sock=%p",
-                      query->lookup, query,
-                      query->sock);
-               result = isc_socket_recvv(query->sock, &query->recvlist, 1,
-                                         global_task, recv_done, query);
+                     query->lookup, query,
+                     query->sock);
+               result = isc_socket_recvv(query->sock,
+                                         &query->recvlist, 1,
+                                         global_task, recv_done,
+                                         query);
                check_result(result, "isc_socket_recvv");
-               sendcount++;
-               debug("sent count number %d", sendcount);
-               ISC_LIST_ENQUEUE(query->sendlist, &lookup->sendbuf, link);
-               debug("sending a request");
-               result = isc_time_now(&query->time_sent);
-               check_result(result, "isc_time_now");
-               ENSURE(query->sock != NULL);
-               result = isc_socket_sendtov(query->sock, &query->sendlist,
-                                           global_task, send_done, query,
-                                           &query->sockaddr, NULL);
-               check_result(result, "isc_socket_sendtov");
+               recvcount++;
+               debug("recvcount=%d", recvcount);
+       }
+       ISC_LIST_INIT(query->sendlist);
+       ISC_LIST_ENQUEUE(query->sendlist, &l->sendbuf,
+                        link);
+       debug("sending a request");
+       result = isc_time_now(&query->time_sent);
+       check_result(result, "isc_time_now");
+       INSIST(query->sock != NULL);
+       result = isc_socket_sendtov(query->sock, &query->sendlist,
+                                   global_task, send_done, query,
+                                   &query->sockaddr, NULL);
+       check_result(result, "isc_socket_sendtov");
+       sendcount++;
+       /*
+        * If we're doing a nameserver search, we need to immediately
+        * bring up all the queries.  Do it here.
+        */
+       if (l->ns_search_only) {
+               debug("sending next, since searching");
+               next = ISC_LIST_NEXT(query, link);
+               if (next != NULL)
+                       send_udp(next);
        }
 }
 
 /*
- * connect_timeout is used for both UDP recieves and TCP connects.
+ * IO timeout handler, used for both connect and recv timeouts.  If
+ * retries are still allowed, either resend the UDP packet or queue a
+ * new TCP lookup.  Otherwise, cancel the lookup.
  */
 static void
 connect_timeout(isc_task_t *task, isc_event_t *event) {
-       dig_lookup_t *lookup=NULL;
-       dig_query_t *q=NULL;
-       isc_result_t result;
-       isc_buffer_t *b=NULL;
-       isc_region_t r;
+       dig_lookup_t *l=NULL;
+       dig_query_t *query=NULL, *cq;
 
+       UNUSED(task);
        REQUIRE(event->ev_type == ISC_TIMEREVENT_IDLE);
 
        debug("connect_timeout()");
-       lookup = event->ev_arg;
 
+       LOCK_LOOKUP;
+       l = event->ev_arg;
+       query = l->current_query;
        isc_event_free(&event);
 
-       debug("buffer allocate connect_timeout");
-       result = isc_buffer_allocate(mctx, &b, 256);
-       check_result(result, "isc_buffer_allocate");
-       for (q = ISC_LIST_HEAD(lookup->q);
-            q != NULL;
-            q = ISC_LIST_NEXT(q, link)) {
-               if (q->working) {
-                       if (!free_now) {
-                               isc_buffer_clear(b);
-                               result = isc_sockaddr_totext(&q->sockaddr, b);
-                               check_result(result, "isc_sockaddr_totext");
-                               isc_buffer_usedregion(b, &r);
-                               if ((q->lookup->retries > 1) &&
-                                   (!q->lookup->tcp_mode))
-                                       printf(";; Connection to server %.*s "
-                                              "for %s timed out.  "
-                                              "Retrying %d.\n",
-                                              (int)r.length, r.base,
-                                              q->lookup->textname,
-                                              q->lookup->retries-1);
-                               else {
-                                       printf(";; Connection to "
-                                              "server %.*s "
-                                              "for %s timed out.  "
-                                              "Giving up.\n",
-                                              (int)r.length, r.base,
-                                              q->lookup->textname);
-                               }
-                       }
-                       isc_socket_cancel(q->sock, task,
-                                         ISC_SOCKCANCEL_ALL);
+       INSIST(!free_now);
+
+       if ((query != NULL) && (query->lookup->current_query != NULL) &&
+           (ISC_LIST_NEXT(query->lookup->current_query, link) != NULL)) {
+               debug("trying next server...");
+               cq = query->lookup->current_query;
+               if (!l->tcp_mode)
+                       send_udp(ISC_LIST_NEXT(cq, link));
+               else
+                       send_tcp_connect(ISC_LIST_NEXT(cq, link));
+               UNLOCK_LOOKUP;
+               return;
+       }
+
+       if (l->retries > 1) {
+               if (!l->tcp_mode) {
+                       l->retries--;
+                       debug("resending UDP request to first server");
+                       send_udp(ISC_LIST_HEAD(l->q));
+               } else {
+                       debug("making new TCP request, %d tries left",
+                             l->retries);
+                       cancel_lookup(l);
+                       l->retries--;
+                       requeue_lookup(l, ISC_TRUE);
                }
        }
-       ENSURE(lookup->timer != NULL);
-       isc_timer_detach(&lookup->timer);
-       isc_buffer_free(&b);
-       debug("done with connect_timeout()");
+       else {
+               printf(";; connection timed out; no servers could be "
+                      "reached\n");
+               cancel_lookup(l);
+       }
+       UNLOCK_LOOKUP;
 }
 
+/*
+ * Event handler for the TCP recv which gets the length header of TCP
+ * packets.  Start the next recv of length bytes.
+ */
 static void
-tcp_length_done(isc_task_t *task, isc_event_t *event) { 
+tcp_length_done(isc_task_t *task, isc_event_t *event) {
        isc_socketevent_t *sevent;
        isc_buffer_t *b=NULL;
        isc_region_t r;
        isc_result_t result;
        dig_query_t *query=NULL;
+       dig_lookup_t *l;
        isc_uint16_t length;
 
-       REQUIRE(event->ev_type == ISC_SOCKEVENT_RECVDONE);
-
-       UNUSED(task);
-
-       debug("tcp_length_done()");
-
-       if (free_now) {
-               isc_event_free(&event);
-               return;
-       }
-
-       sevent = (isc_socketevent_t *)event;    
+       REQUIRE(event->ev_type == ISC_SOCKEVENT_RECVDONE);
+       INSIST(!free_now);
+
+       UNUSED(task);
+
+       debug("tcp_length_done()");
 
+       LOCK_LOOKUP;
+       sevent = (isc_socketevent_t *)event;
        query = event->ev_arg;
 
+       recvcount--;
+       INSIST(recvcount >= 0);
+
        if (sevent->result == ISC_R_CANCELED) {
-               query->working = ISC_FALSE;
                isc_event_free(&event);
-               check_next_lookup(query->lookup);
+               l = query->lookup;
+               clear_query(query);
+               check_next_lookup(l);
+               UNLOCK_LOOKUP;
                return;
        }
        if (sevent->result != ISC_R_SUCCESS) {
-               debug("buffer allocate connect_timeout");
                result = isc_buffer_allocate(mctx, &b, 256);
                check_result(result, "isc_buffer_allocate");
                result = isc_sockaddr_totext(&query->sockaddr, b);
                check_result(result, "isc_sockaddr_totext");
                isc_buffer_usedregion(b, &r);
-               printf("%.*s: %s\n", (int)r.length, r.base,
+               printf(";; communications error to %.*s: %s\n",
+                      (int)r.length, r.base,
                       isc_result_totext(sevent->result));
                isc_buffer_free(&b);
-               query->working = ISC_FALSE;
-               sockcount--;
-               debug("socket = %d",sockcount);
+               l = query->lookup;
                isc_socket_detach(&query->sock);
+               sockcount--;
+               debug("sockcount=%d",sockcount);
+               INSIST(sockcount >= 0);
                isc_event_free(&event);
-               check_next_lookup(query->lookup);
+               clear_query(query);
+               check_next_lookup(l);
+               UNLOCK_LOOKUP;
                return;
        }
        b = ISC_LIST_HEAD(sevent->bufferlist);
@@ -1391,33 +1744,43 @@ tcp_length_done(isc_task_t *task, isc_event_t *event) {
        result = isc_socket_recvv(query->sock, &query->recvlist, length, task,
                                  recv_done, query);
        check_result(result, "isc_socket_recvv");
-       debug("resubmitted recv request with length %d", length);
+       recvcount++;
+       debug("resubmitted recv request with length %d, recvcount=%d",
+             length, recvcount);
        isc_event_free(&event);
+       UNLOCK_LOOKUP;
 }
 
+/*
+ * For transfers that involve multiple recvs (XFR's in particular),
+ * launch the next recv.
+ */
 static void
 launch_next_query(dig_query_t *query, isc_boolean_t include_question) {
        isc_result_t result;
+       dig_lookup_t *l;
 
-       debug("launch_next_query()");
+       INSIST(!free_now);
 
-       if (free_now)
-               return;
+       debug("launch_next_query()");
 
        if (!query->lookup->pending) {
                debug("ignoring launch_next_query because !pending");
-               sockcount--;
-               debug("socket = %d", sockcount);
                isc_socket_detach(&query->sock);
-               query->working = ISC_FALSE;
+               sockcount--;
+               debug("sockcount=%d", sockcount);
+               INSIST(sockcount >= 0);
                query->waiting_connect = ISC_FALSE;
-               check_next_lookup(query->lookup);
+               l = query->lookup;
+               clear_query(query);
+               check_next_lookup(l);
                return;
        }
 
        isc_buffer_clear(&query->slbuf);
        isc_buffer_clear(&query->lengthbuf);
        isc_buffer_putuint16(&query->slbuf, query->lookup->sendbuf.used);
+       ISC_LIST_INIT(query->sendlist);
        ISC_LIST_ENQUEUE(query->sendlist, &query->slbuf, link);
        if (include_question) {
                ISC_LIST_ENQUEUE(query->sendlist, &query->lookup->sendbuf,
@@ -1428,103 +1791,117 @@ launch_next_query(dig_query_t *query, isc_boolean_t include_question) {
        result = isc_socket_recvv(query->sock, &query->lengthlist, 0,
                                  global_task, tcp_length_done, query);
        check_result(result, "isc_socket_recvv");
-       sendcount++;
+       recvcount++;
+       debug("recvcount=%d",recvcount);
        if (!query->first_soa_rcvd) {
-               debug("sending a request");
+               debug("sending a request in launch_next_query");
                result = isc_time_now(&query->time_sent);
                check_result(result, "isc_time_now");
                result = isc_socket_sendv(query->sock, &query->sendlist,
                                          global_task, send_done, query);
-               check_result(result, "isc_socket_recvv");
+               check_result(result, "isc_socket_sendv");
+               sendcount++;
+               debug("sendcount=%d", sendcount);
        }
        query->waiting_connect = ISC_FALSE;
+#if 0
        check_next_lookup(query->lookup);
+#endif
        return;
 }
-       
+
+/*
+ * Event handler for TCP connect complete.  Make sure the connection was
+ * successful, then pass into launch_next_query to actually send the
+ * question.
+ */
 static void
 connect_done(isc_task_t *task, isc_event_t *event) {
        isc_result_t result;
        isc_socketevent_t *sevent = NULL;
-       dig_query_t *query = NULL;
+       dig_query_t *query = NULL, *next;
+       dig_lookup_t *l;
        isc_buffer_t *b = NULL;
        isc_region_t r;
 
        UNUSED(task);
 
        REQUIRE(event->ev_type == ISC_SOCKEVENT_CONNECT);
+       INSIST(!free_now);
 
        debug("connect_done()");
 
-       if (free_now) {
-               isc_event_free(&event);
-               return;
-       }
-
+       LOCK_LOOKUP;
        sevent = (isc_socketevent_t *)event;
        query = sevent->ev_arg;
 
-       REQUIRE(query->waiting_connect);
+       INSIST(query->waiting_connect);
 
        query->waiting_connect = ISC_FALSE;
 
-       if (sevent->result == ISC_R_CANCELED) {
-               debug("in cancel handler");
-               query->working = ISC_FALSE;
-               query->waiting_connect = ISC_FALSE;
-               isc_event_free(&event);
-               check_next_lookup(query->lookup);
-               return;
-       }
+       if (sevent->result == ISC_R_CANCELED) {
+               debug("in cancel handler");
+               isc_socket_detach(&query->sock);
+               sockcount--;
+               INSIST(sockcount >= 0);
+               debug("sockcount=%d", sockcount);
+               query->waiting_connect = ISC_FALSE;
+               isc_event_free(&event);
+               l = query->lookup;
+               clear_query(query);
+               check_next_lookup(l);
+               UNLOCK_LOOKUP;
+               return;
+       }
        if (sevent->result != ISC_R_SUCCESS) {
-               debug("buffer allocate connect_timeout");
+               debug("unsuccessful connection: %s",
+                     isc_result_totext(sevent->result));
                result = isc_buffer_allocate(mctx, &b, 256);
                check_result(result, "isc_buffer_allocate");
                result = isc_sockaddr_totext(&query->sockaddr, b);
                check_result(result, "isc_sockaddr_totext");
                isc_buffer_usedregion(b, &r);
-               printf(";; Connection to server %.*s for %s failed: %s.\n",
-                      (int)r.length, r.base, query->lookup->textname,
-                      isc_result_totext(sevent->result));
+               /* XXX isc_sockaddr_format */
+               if (sevent->result != ISC_R_CANCELED)
+                       printf(";; Connection to %.*s(%s) for %s failed: "
+                              "%s.\n", (int)r.length, r.base,
+                              query->servname, query->lookup->textname,
+                              isc_result_totext(sevent->result));
+               isc_socket_detach(&query->sock);
+               sockcount--;
+               INSIST(sockcount >= 0);
+               /* XXX Clean up exitcodes */
                if (exitcode < 9)
                        exitcode = 9;
+               debug("sockcount=%d", sockcount);
                isc_buffer_free(&b);
-               query->working = ISC_FALSE;
                query->waiting_connect = ISC_FALSE;
                isc_event_free(&event);
-               check_next_lookup(query->lookup);
+               l = query->lookup;
+               if (l->current_query != NULL)
+                       next = ISC_LIST_NEXT(l->current_query, link);
+               else
+                       next = NULL;
+               clear_query(query);
+               if (next != NULL) {
+                       bringup_timer(next, TCP_TIMEOUT);
+                       send_tcp_connect(next);
+               } else {
+                       check_next_lookup(l);
+               }
+               UNLOCK_LOOKUP;
                return;
        }
        launch_next_query(query, ISC_TRUE);
        isc_event_free(&event);
+       UNLOCK_LOOKUP;
 }
 
-
-#if 0
-static isc_boolean_t
-msg_contains_soa(dns_message_t *msg, dig_query_t *query) {
-       isc_result_t result;
-       dns_name_t *name=NULL;
-       
-       debug("msg_contains_soa()");
-       
-       result = dns_message_findname(msg, DNS_SECTION_ANSWER,
-                                     query->lookup->name, dns_rdatatype_soa,
-                                     0, &name, NULL);
-       if (result == ISC_R_SUCCESS) {
-               debug("found SOA", stderr);
-               return (ISC_TRUE);
-       } else {
-               debug("didn't find SOA, result=%d:%s",
-                     result, dns_result_totext(result));
-               return (ISC_FALSE);
-       }
-       
-}
-#endif
-
 /*
- * Returns true if we should call cancel_lookup().  This is a hack.
+ * Check if the ongoing XFR needs more data before it's complete, using
+ * the semantics of IXFR and AXFR protocols.  Much of the complexity of
+ * this routine comes from determining when an IXFR is complete.
+ * ISC_FALSE means more data is on the way, and the recv has been issued.
  */
 static isc_boolean_t
 check_for_more_data(dig_query_t *query, dns_message_t *msg,
@@ -1536,7 +1913,8 @@ check_for_more_data(dig_query_t *query, dns_message_t *msg,
        isc_result_t result;
        isc_buffer_t b;
        isc_region_t r;
-       char *abspace[MXNAME];
+       char abspace[MXNAME];
+       isc_boolean_t atlimit=ISC_FALSE;
 
        debug("check_for_more_data()");
 
@@ -1548,18 +1926,15 @@ check_for_more_data(dig_query_t *query, dns_message_t *msg,
         * rr's in the message, acting as necessary whenever we hit
         * an SOA rr.
         */
-       
+
        result = dns_message_firstname(msg, DNS_SECTION_ANSWER);
        if (result != ISC_R_SUCCESS) {
                puts("; Transfer failed.");
-               query->working = ISC_FALSE;
                return (ISC_TRUE);
        }
-#ifdef NEVER
-       check_result(result, "dns_message_firstname");
-#endif
        do {
-               dns_name_t *name = NULL;
+               dns_name_t *name;
+               name = NULL;
                dns_message_currentname(msg, DNS_SECTION_ANSWER,
                                        &name);
                for (rdataset = ISC_LIST_HEAD(name->list);
@@ -1569,6 +1944,9 @@ check_for_more_data(dig_query_t *query, dns_message_t *msg,
                        if (result != ISC_R_SUCCESS)
                                continue;
                        do {
+                               query->rr_count++;
+                               if (query->rr_count >= rr_limit)
+                                       atlimit = ISC_TRUE;
                                dns_rdataset_current(rdataset, &rdata);
                                /*
                                 * If this is the first rr, make sure
@@ -1579,7 +1957,6 @@ check_for_more_data(dig_query_t *query, dns_message_t *msg,
                                        puts("; Transfer failed.  "
                                             "Didn't start with "
                                             "SOA answer.");
-                                       query->working = ISC_FALSE;
                                        return (ISC_TRUE);
                                }
                                if ((!query->second_rr_rcvd) &&
@@ -1613,12 +1990,25 @@ check_for_more_data(dig_query_t *query, dns_message_t *msg,
                                        if (query->lookup->ixfr_serial >=
                                            soa.serial) {
                                                dns_rdata_freestruct(&soa);
-                                               goto xfr_done;
+                                               goto doexit;
                                        }
                                        dns_rdata_freestruct(&soa);
                                        goto next_rdata;
                                }
+                               if (query->lookup->rdtype ==
+                                   dns_rdatatype_axfr) {
+                                       debug("doing axfr, got second SOA");
+                                       dns_rdata_freestruct(&soa);
+                                       goto doexit;
+                               }
                                if (!query->second_rr_rcvd) {
+                                       if (soa.serial ==
+                                           query->first_rr_serial) {
+                                               debug("doing ixfr, got "
+                                                     "empty zone");
+                                               dns_rdata_freestruct(&soa);
+                                               goto doexit;
+                                       }
                                        debug("this is the second %d",
                                               query->lookup->ixfr_serial);
                                        query->second_rr_rcvd = ISC_TRUE;
@@ -1635,19 +2025,8 @@ check_for_more_data(dig_query_t *query, dns_message_t *msg,
                                         * AXFR, and we're done.
                                         */
                                        debug("done, since axfr");
-                               xfr_done:
-                                       isc_buffer_init(&b, abspace, MXNAME);
-                                       result = isc_sockaddr_totext(&sevent->
-                                                                    address,
-                                                                    &b);
-                                       check_result(result,
-                                                    "isc_sockaddr_totext");
-                                       isc_buffer_usedregion(&b, &r);
-                                       received(b.used, r.length,
-                                                (char *)r.base, query);
-                                       query->working = ISC_FALSE;
                                        dns_rdata_freestruct(&soa);
-                                       return (ISC_TRUE);
+                                       goto doexit;
                                }
                                /*
                                 * If we get to this point, we're doing an
@@ -1664,7 +2043,7 @@ check_for_more_data(dig_query_t *query, dns_message_t *msg,
                                        }
                                        debug("done with ixfr");
                                        dns_rdata_freestruct(&soa);
-                                       goto xfr_done;
+                                       goto doexit;
                                }
                                debug("meaningless soa %d",
                                       soa.serial);
@@ -1673,12 +2052,36 @@ check_for_more_data(dig_query_t *query, dns_message_t *msg,
                                result = dns_rdataset_next(rdataset);
                        } while (result == ISC_R_SUCCESS);
                }
+               query->name_count++;
+               if (query->name_count >= name_limit) {
+                       debug("name_count(%d) > name_limit(%d)",
+                             query->name_count, name_limit);
+                       atlimit = ISC_TRUE;
+               }
                result = dns_message_nextname(msg, DNS_SECTION_ANSWER);
        } while (result == ISC_R_SUCCESS);
+       if (atlimit) {
+       doexit:
+               isc_buffer_init(&b, abspace, MXNAME);
+               result = isc_sockaddr_totext(&sevent->address, &b);
+               check_result(result,
+                            "isc_sockaddr_totext");
+               isc_buffer_usedregion(&b, &r);
+               received(b.used, r.length,
+                        (char *)r.base, query);
+               if (atlimit)
+                       if (exitcode < 7)
+                               exitcode = 7;
+               return (ISC_TRUE);
+       }
        launch_next_query(query, ISC_FALSE);
        return (ISC_FALSE);
 }
 
+/*
+ * Event handler for recv complete.  Perform whatever actions are necessary,
+ * based on the specifics of the user's request.
+ */
 static void
 recv_done(isc_task_t *task, isc_event_t *event) {
        isc_socketevent_t *sevent = NULL;
@@ -1689,49 +2092,42 @@ recv_done(isc_task_t *task, isc_event_t *event) {
        isc_buffer_t ab;
        char abspace[MXNAME];
        isc_region_t r;
-       dig_lookup_t *n;
+       dig_lookup_t *n, *l;
        isc_boolean_t docancel = ISC_FALSE;
-       isc_boolean_t result_bool;
        unsigned int local_timeout;
-       
+
        UNUSED(task);
+       INSIST(!free_now);
 
+       if (show_packets)
+               puts(";; begin of DNS packet");
        debug("recv_done()");
 
-       if (free_now) {
-               isc_event_free(&event);
-               return;
-       }
+       LOCK_LOOKUP;
+       recvcount--;
+       debug("recvcount=%d", recvcount);
+       INSIST(recvcount >= 0);
 
        query = event->ev_arg;
        debug("lookup=%p, query=%p", query->lookup, query);
 
-       if (free_now) {
-               debug("bailing out, since freeing now");
-               isc_event_free(&event);
-               return;
-       }
+       l = query->lookup;
 
-       sendcount--;
-       debug("in recv_done, counter down to %d", sendcount);
        REQUIRE(event->ev_type == ISC_SOCKEVENT_RECVDONE);
        sevent = (isc_socketevent_t *)event;
 
-       if ((query->lookup->tcp_mode) &&
-           (query->lookup->timer != NULL))
-               isc_timer_touch(query->lookup->timer);
-       if (!query->lookup->pending && !query->lookup->ns_search_only) {
-
+       if ((l->tcp_mode) && (l->timer != NULL))
+               isc_timer_touch(l->timer);
+       if ((!l->pending && !l->ns_search_only)
+           || cancel_now) {
                debug("no longer pending.  Got %s",
                        isc_result_totext(sevent->result));
-               query->working = ISC_FALSE;
                query->waiting_connect = ISC_FALSE;
-               
+
                isc_event_free(&event);
-               /*
-                * In this case, we don't actually use result_bool
-                */
-               result_bool = cancel_lookup(query->lookup);
+               clear_query(query);
+               check_next_lookup(l);
+               UNLOCK_LOOKUP;
                return;
        }
 
@@ -1741,86 +2137,117 @@ recv_done(isc_task_t *task, isc_event_t *event) {
                result = dns_message_create(mctx, DNS_MESSAGE_INTENTPARSE,
                                            &msg);
                check_result(result, "dns_message_create");
-               
+
                if (key != NULL) {
-                       debug("querysig 1 is %p", query->lookup->querysig);
-                       if (query->lookup->querysig == NULL) {
+                       if (l->querysig == NULL) {
                                debug("getting initial querysig");
                                result = dns_message_getquerytsig(
-                                            query->lookup->sendmsg,
-                                            mctx, &query->lookup->querysig);
+                                            l->sendmsg,
+                                            mctx, &l->querysig);
                                check_result(result,
                                             "dns_message_getquerytsig");
                        }
                        result = dns_message_setquerytsig(msg,
-                                                query->lookup->querysig);
+                                                l->querysig);
                        check_result(result, "dns_message_setquerytsig");
                        result = dns_message_settsigkey(msg, key);
                        check_result(result, "dns_message_settsigkey");
-                       msg->tsigctx = query->lookup->tsigctx;
-                       if (query->lookup->msgcounter != 0) 
+                       msg->tsigctx = l->tsigctx;
+                       if (l->msgcounter != 0)
                                msg->tcp_continuation = 1;
-                       query->lookup->msgcounter++;
+                       l->msgcounter++;
                }
                debug("before parse starts");
                result = dns_message_parse(msg, b, ISC_TRUE);
                if (result != ISC_R_SUCCESS) {
-                       printf(";; Got bad UDP packet:\n");
+                       printf(";; Got bad packet: %s\n",
+                              dns_result_totext(result));
                        hex_dump(b);
-                       query->working = ISC_FALSE;
                        query->waiting_connect = ISC_FALSE;
                        dns_message_destroy(&msg);
                        isc_event_free(&event);
-                       cancel_lookup(query->lookup);
+                       clear_query(query);
+                       cancel_lookup(l);
+                       check_next_lookup(l);
+                       UNLOCK_LOOKUP;
                        return;
                }
-               if (((msg->flags & DNS_MESSAGEFLAG_TC) != 0) 
-                   && !query->lookup->tcp_mode && !ignore) {
-                       printf(";; Truncated, retrying in TCP mode.\n");
-                       n = requeue_lookup(query->lookup, ISC_TRUE);
-                       n->tcp_mode = ISC_TRUE;
-                       query->working = ISC_FALSE;
-                       query->waiting_connect = ISC_FALSE;
-                      dns_message_destroy(&msg);
-                      isc_event_free(&event);
-                      cancel_lookup(query->lookup);
-                      return;
+               if (((msg->flags & DNS_MESSAGEFLAG_TC) != 0) 
+                   && ! l->ignore && !l->tcp_mode) {
+                       printf(";; Truncated, retrying in TCP mode.\n");
+                       n = requeue_lookup(l, ISC_TRUE);
+                       n->tcp_mode = ISC_TRUE;
+                       dns_message_destroy(&msg);
+                       isc_event_free(&event);
+                       clear_query(query);
+                       cancel_lookup(l);
+                       check_next_lookup(l);
+                       UNLOCK_LOOKUP;
+                       return;
+               }                       
+               if ((msg->rcode == dns_rcode_servfail) &&
+                   l->servfail_stops) {
+                       dig_query_t *next = ISC_LIST_NEXT(query, link);
+                       if (l->current_query == query)
+                               l->current_query = NULL;
+                       if (next != NULL) {
+                               debug("sending query %lx\n", next);
+                               if (l->tcp_mode)
+                                       send_tcp_connect(next);
+                               else
+                                       send_udp(next);
+                       }
+                       /*
+                        * If our query is at the head of the list and there
+                        * is no next, we're the only one left, so fall
+                        * through to print the message.
+                        */
+                       if ((ISC_LIST_HEAD(l->q) != query) ||
+                           (ISC_LIST_NEXT(query, link) != NULL)) {
+                               printf(";; Got SERVFAIL reply from %s, "
+                                      "trying next server\n",
+                                      query->servname);
+                               clear_query(query);
+                               check_next_lookup(l);
+                               dns_message_destroy(&msg);
+                               isc_event_free(&event);
+                               UNLOCK_LOOKUP;
+                               return;
+                       }
                }
+
                if (key != NULL) {
-                       debug("querysig 2 is %p", query->lookup->querysig);
-                       debug("before verify");
                        result = dns_tsig_verify(&query->recvbuf, msg,
-                                                NULL, keyring);
-                       debug("after verify");
+                                                NULL, NULL);
                        if (result != ISC_R_SUCCESS) {
                                printf(";; Couldn't verify signature: %s\n",
                                       dns_result_totext(result));
                                validated = ISC_FALSE;
                        }
-                       query->lookup->tsigctx = msg->tsigctx;
-                       if (query->lookup->querysig != NULL) {
+                       l->tsigctx = msg->tsigctx;
+                       if (l->querysig != NULL) {
                                debug("freeing querysig buffer %p",
-                                      query->lookup->querysig);
-                               isc_buffer_free(&query->lookup->querysig);
+                                      l->querysig);
+                               isc_buffer_free(&l->querysig);
                        }
                        result = dns_message_getquerytsig(msg, mctx,
-                                                    &query->lookup->querysig);
+                                                    &l->querysig);
                        check_result(result,"dns_message_getquerytsig");
-                       debug("querysig 3 is %p", query->lookup->querysig);
+                       debug("querysig 3 is %p", l->querysig);
                }
                debug("after parse");
-               if (query->lookup->xfr_q == NULL) {
-                       query->lookup->xfr_q = query;
+               if (l->xfr_q == NULL) {
+                       l->xfr_q = query;
                        /*
                         * Once we are in the XFR message, increase
                         * the timeout to much longer, so brief network
                         * outages won't cause the XFR to abort
                         */
                        if ((timeout != INT_MAX) &&
-                           (query->lookup->timer != NULL) &&
-                           query->lookup->doing_xfr ) {
+                           (l->timer != NULL) &&
+                           l->doing_xfr ) {
                                if (timeout == 0) {
-                                       if (query->lookup->tcp_mode)
+                                       if (l->tcp_mode)
                                                local_timeout = TCP_TIMEOUT;
                                        else
                                                local_timeout = UDP_TIMEOUT;
@@ -1830,74 +2257,94 @@ recv_done(isc_task_t *task, isc_event_t *event) {
                                        else
                                                local_timeout = INT_MAX;
                                }
-                               debug ("have local timeout of %d",
-                                      local_timeout);          
-                               isc_interval_set(&query->lookup->interval,
+                               debug("have local timeout of %d",
+                                      local_timeout);
+                               isc_interval_set(&l->interval,
                                                 local_timeout, 0);
-                               result = isc_timer_reset(query->lookup->timer,
+                               result = isc_timer_reset(l->timer,
                                                      isc_timertype_once,
                                                      NULL,
-                                                     &query->lookup->interval,
+                                                     &l->interval,
                                                      ISC_FALSE);
                                check_result(result, "isc_timer_reset");
                        }
                }
-               if (query->lookup->xfr_q == query) {
-                       if ((query->lookup->trace)||
-                           (query->lookup->ns_search_only)) {
+               if (l->xfr_q == query) {
+                       if ((l->trace)||
+                           (l->ns_search_only)) {
                                debug("in TRACE code");
-                               if (show_details ||
-                                   (((dns_message_firstname(msg,
-                                                        DNS_SECTION_ANSWER)
-                                      == ISC_R_SUCCESS)) &&
-                                    !query->lookup->trace_root)) {
-                                       printmessage(query, msg, ISC_TRUE);
-                               }
+                               printmessage(query, msg, ISC_TRUE);
                                if ((msg->rcode != 0) &&
-                                   (query->lookup->origin != NULL)) {
-                                       next_origin(msg, query);
+                                   (l->origin != NULL)) {
+                                       if (!next_origin(msg, query)) {
+                                               printmessage(query, msg,
+                                                            ISC_TRUE);
+                                               isc_buffer_init(&ab, abspace,
+                                                               MXNAME);
+                                               result = isc_sockaddr_totext(
+                                                       &sevent->address,
+                                                       &ab);
+                                               check_result(result,
+                                                     "isc_sockaddr_totext");
+                                               isc_buffer_usedregion(&ab, &r);
+                                               received(b->used, r.length,
+                                                        (char *)r.base,
+                                                        query);
+                                       }
                                } else {
                                        result = dns_message_firstname
                                                (msg,DNS_SECTION_ANSWER);
                                        if ((result != ISC_R_SUCCESS) ||
-                                           query->lookup->trace_root)
+                                           l->trace_root)
                                                followup_lookup(msg, query,
                                                        DNS_SECTION_AUTHORITY);
                                }
                        } else if ((msg->rcode != 0) &&
-                                (query->lookup->origin != NULL)) {
-                               next_origin(msg, query);
-                               if (show_details) {
-                                      printmessage(query, msg, ISC_TRUE);
+                                (l->origin != NULL)) {
+                               if (!next_origin(msg, query)) {
+                                       printmessage(query, msg,
+                                                    ISC_TRUE);
+                                       isc_buffer_init(&ab, abspace, MXNAME);
+                                       result = isc_sockaddr_totext(
+                                                            &sevent->address,
+                                                            &ab);
+                                       check_result(result,
+                                                    "isc_sockaddr_totext");
+                                       isc_buffer_usedregion(&ab, &r);
+                                       received(b->used, r.length,
+                                                (char *)r.base,
+                                                query);
                                }
                        } else {
                                if (query->first_soa_rcvd &&
-                                   query->lookup->doing_xfr)
+                                   l->doing_xfr)
                                        printmessage(query, msg, ISC_FALSE);
                                else
                                        printmessage(query, msg, ISC_TRUE);
                        }
                } else if ((dns_message_firstname(msg, DNS_SECTION_ANSWER)
                            == ISC_R_SUCCESS) &&
-                          query->lookup->ns_search_only &&
-                          !query->lookup->trace_root ) {
+                          l->ns_search_only &&
+                          !l->trace_root ) {
                        printmessage(query, msg, ISC_TRUE);
                }
-               
-               if (query->lookup->pending)
+
+               if (l->pending)
                        debug("still pending.");
-               if (query->lookup->doing_xfr) {
-                       if (query != query->lookup->xfr_q) {
+               if (l->doing_xfr) {
+                       if (query != l->xfr_q) {
                                dns_message_destroy(&msg);
                                isc_event_free (&event);
-                               query->working = ISC_FALSE;
                                query->waiting_connect = ISC_FALSE;
+                               UNLOCK_LOOKUP;
                                return;
                        }
                        docancel = check_for_more_data(query, msg, sevent);
                        if (docancel) {
                                dns_message_destroy(&msg);
-                               result_bool = cancel_lookup(query->lookup);
+                               clear_query(query);
+                               cancel_lookup(l);
+                               check_next_lookup(l);
                        }
                        if (msg != NULL)
                                dns_message_destroy(&msg);
@@ -1905,53 +2352,63 @@ recv_done(isc_task_t *task, isc_event_t *event) {
                }
                else {
                        if ((msg->rcode == 0) ||
-                           (query->lookup->origin == NULL)) {
+                           (l->origin == NULL)) {
                                isc_buffer_init(&ab, abspace, MXNAME);
                                result = isc_sockaddr_totext(&sevent->address,
                                                             &ab);
                                check_result(result, "isc_sockaddr_totext");
                                isc_buffer_usedregion(&ab, &r);
-                               if ((dns_message_firstname(msg,
-                                                          DNS_SECTION_ANSWER)
-                                     == ISC_R_SUCCESS) ||
-                                   query->lookup->trace ) {
-                                       received(b->used, r.length,
-                                                (char *)r.base,
-                                                query);
-                               }
+                               received(b->used, r.length,
+                                        (char *)r.base,
+                                        query);
                        }
-                       query->working = ISC_FALSE;
                        query->lookup->pending = ISC_FALSE;
-                       result_bool = ISC_FALSE;
                        if (!query->lookup->ns_search_only ||
                            query->lookup->trace_root) {
                                dns_message_destroy(&msg);
-                               result_bool = cancel_lookup(query->lookup);
+                               cancel_lookup(l);
                        }
                        if (msg != NULL)
                                dns_message_destroy(&msg);
                        isc_event_free(&event);
-                       if ((!free_now) && (!result_bool))
-                               check_next_lookup(query->lookup);
+                       clear_query(query);
+                       check_next_lookup(l);
                }
+               UNLOCK_LOOKUP;
                return;
        }
        /*
         * In truth, we should never get into the CANCELED routine, since
         * the cancel_lookup() routine clears the pending flag.
+        * XXX Is this true anymore, since the bulk changes?
         */
        if (sevent->result == ISC_R_CANCELED) {
-               debug("in cancel handler");
-               query->working = ISC_FALSE;
+               debug("in recv cancel handler");
                query->waiting_connect = ISC_FALSE;
                isc_event_free(&event);
-               check_next_lookup(query->lookup);
+               clear_query(query);
+               check_next_lookup(l);
+               UNLOCK_LOOKUP;
                return;
        }
-       fatal("recv_done got result %s",
-             isc_result_totext(sevent->result));
+       printf(";; communications error: %s\n",
+              isc_result_totext(sevent->result));
+       isc_socket_detach(&query->sock);
+       sockcount--;
+       debug("sockcount=%d",sockcount);
+       INSIST(sockcount >= 0);
+       isc_event_free(&event);
+       clear_query(query);
+       check_next_lookup(l);
+       UNLOCK_LOOKUP;
+       return;
 }
 
+/*
+ * Turn a name into an address, using system-supplied routines.  This is
+ * used in looking up server names, etc... and needs to use system-supplied
+ * routines, since they may be using a non-DNS system for these lookups.
+ */
 void
 get_address(char *host, in_port_t port, isc_sockaddr_t *sockaddr) {
        struct in_addr in4;
@@ -1971,7 +2428,10 @@ get_address(char *host, in_port_t port, isc_sockaddr_t *sockaddr) {
                isc_sockaddr_fromin(sockaddr, &in4, port);
        else {
 #if defined(HAVE_ADDRINFO) && defined(HAVE_GETADDRINFO)
+               debug ("before getaddrinfo()");
+               is_blocking = ISC_TRUE;
                result = getaddrinfo(host, NULL, NULL, &res);
+               is_blocking = ISC_FALSE;
                if (result != 0) {
                        fatal("Couldn't find server '%s': %s",
                              host, gai_strerror(result));
@@ -1981,7 +2441,10 @@ get_address(char *host, in_port_t port, isc_sockaddr_t *sockaddr) {
                isc_sockaddr_setport(sockaddr, port);
                freeaddrinfo(res);
 #else
+               debug ("before gethostbyname()");
+               is_blocking = ISC_TRUE;
                he = gethostbyname(host);
+               is_blocking = ISC_FALSE;
                if (he == NULL)
                     fatal("Couldn't find server '%s' (h_errno=%d)",
                           host, h_errno);
@@ -1993,173 +2456,140 @@ get_address(char *host, in_port_t port, isc_sockaddr_t *sockaddr) {
        }
 }
 
-static void
-do_lookup_tcp(dig_lookup_t *lookup) {
-       dig_query_t *query;
-       isc_result_t result;
-       unsigned int local_timeout;
-
-       debug("do_lookup_tcp()");
-       lookup->pending = ISC_TRUE;
-       if (timeout != INT_MAX) {
-               if (timeout == 0) {
-                       if (lookup->tcp_mode)
-                               local_timeout = TCP_TIMEOUT;
-                       else
-                               local_timeout = UDP_TIMEOUT;
-               } else
-                       local_timeout = timeout;
-               debug ("have local timeout of %d", local_timeout);
-               isc_interval_set(&lookup->interval, local_timeout, 0);
-               result = isc_timer_create(timermgr, isc_timertype_once, NULL,
-                                         &lookup->interval, global_task,
-                                         connect_timeout, lookup,
-                                         &lookup->timer);
-               check_result(result, "isc_timer_create");
-       }
-
-       for (query = ISC_LIST_HEAD(lookup->q);
-            query != NULL;
-            query = ISC_LIST_NEXT(query, link)) {
-               query->working = ISC_TRUE;
-               query->waiting_connect = ISC_TRUE;
-               get_address(query->servname, port, &query->sockaddr);
-
-               sockcount++;
-               debug("socket = %d",sockcount);
-               ENSURE(query->sock == NULL);
-               result = isc_socket_create(socketmgr,
-                                          isc_sockaddr_pf(&query->sockaddr),
-                                          isc_sockettype_tcp, &query->sock) ;
-               check_result(result, "isc_socket_create");
-               if (specified_source)
-                       result = isc_socket_bind(query->sock, &bind_address);
-               else {
-                       if (isc_sockaddr_pf(&query->sockaddr) == AF_INET)
-                               isc_sockaddr_any(&bind_any);
-                       else
-                               isc_sockaddr_any6(&bind_any);
-                       result = isc_socket_bind(query->sock, &bind_any);
-               }
-               check_result(result, "isc_socket_bind");
-               result = isc_socket_connect(query->sock, &query->sockaddr,
-                                           global_task, connect_done, query);
-               check_result(result, "isc_socket_connect");
-       }
-}
-
-static void
-do_lookup_udp(dig_lookup_t *lookup) {
-       dig_query_t *query;
-       isc_result_t result;
-
-       debug("do_lookup_udp()");
-       ENSURE(!lookup->tcp_mode);
-       lookup->pending = ISC_TRUE;
-
-       for (query = ISC_LIST_HEAD(lookup->q);
-            query != NULL;
-            query = ISC_LIST_NEXT(query, link)) {
-               query->working = ISC_TRUE;
-               query->waiting_connect = ISC_FALSE;
-               get_address(query->servname, port, &query->sockaddr);
-
-               sockcount++;
-               debug("socket = %d", sockcount);
-               result = isc_socket_create(socketmgr,
-                                          isc_sockaddr_pf(&query->sockaddr),
-                                          isc_sockettype_udp, &query->sock);
-               check_result(result, "isc_socket_create");
-               if (specified_source)
-                       result = isc_socket_bind(query->sock, &bind_address);
-               else {
-                       if (isc_sockaddr_pf(&query->sockaddr) == AF_INET)
-                               isc_sockaddr_any(&bind_any);
-                       else
-                               isc_sockaddr_any6(&bind_any);
-                       result = isc_socket_bind(query->sock, &bind_any);
-               }
-               check_result(result, "isc_socket_bind");
-       }
-
-       send_udp(lookup);
-}
-
+/*
+ * Initiate either a TCP or UDP lookup
+ */
 void
 do_lookup(dig_lookup_t *lookup) {
 
        REQUIRE(lookup != NULL);
 
        debug("do_lookup()");
+       lookup->pending = ISC_TRUE;
        if (lookup->tcp_mode)
-               do_lookup_tcp(lookup);
+               send_tcp_connect(ISC_LIST_HEAD(lookup->q));
        else
-               do_lookup_udp(lookup);
-}
-
-void
-start_lookup(void) {
-       dig_lookup_t *lookup;
-
-       debug("start_lookup()");
-
-       if (free_now)
-               return;
-
-       lookup = ISC_LIST_HEAD(lookup_list);
-       if (lookup != NULL) {
-               setup_lookup(lookup);
-               do_lookup(lookup);
-       }
+               send_udp(ISC_LIST_HEAD(lookup->q));
 }
 
+/*
+ * Start everything in action upon task startup.
+ */
 void
 onrun_callback(isc_task_t *task, isc_event_t *event) {
        UNUSED(task);
+
        isc_event_free(&event);
+       LOCK_LOOKUP;
        start_lookup();
+       UNLOCK_LOOKUP;
 }
 
+/*
+ * Make everything on the lookup queue go away.  Mainly used by the
+ * SIGINT handler.
+ */
 void
-free_lists(void) {
-       void *ptr;
-       dig_lookup_t *l;
-       dig_query_t *q;
-       dig_server_t *s;
-       dig_searchlist_t *o;
+cancel_all(void) {
+       dig_lookup_t *l, *n;
+       dig_query_t *q, *nq;
 
-       debug("free_lists()");
+       debug("cancel_all()");
 
-       if (free_now)
+       if (is_blocking) {
+               /*
+                * If we get here while another thread is blocking, there's
+                * really nothing we can do to make a clean shutdown
+                * without waiting for the block to complete.  The only
+                * way to get the system down now is to just exit out,
+                * and trust the OS to clean up for us.
+                */
+               fputs ("Abort.\n",stderr);
+               exit(1);
+       }
+       LOCK_LOOKUP;
+       if (free_now) {
+               UNLOCK_LOOKUP;
                return;
-
-       free_now = ISC_TRUE;
-
-       l = ISC_LIST_HEAD(lookup_list);
-       while (l != NULL) {
-               if (l->timer != NULL)
-                       isc_timer_detach(&l->timer);
-               q = ISC_LIST_HEAD(l->q);
+       }
+       cancel_now = ISC_TRUE;
+       if (current_lookup != NULL) {
+               if (current_lookup->timer != NULL)
+                       isc_timer_detach(&current_lookup->timer);
+               q = ISC_LIST_HEAD(current_lookup->q);
                while (q != NULL) {
                        debug("cancelling query %p, belonging to %p",
-                              q, l);
+                              q, current_lookup);
+                       nq = ISC_LIST_NEXT(q, link);
                        if (q->sock != NULL) {
                                isc_socket_cancel(q->sock, NULL,
                                                  ISC_SOCKCANCEL_ALL);
-                               isc_socket_detach(&q->sock);
-                               sockcount--;
-                               debug("socket = %d",sockcount);
+                       } else {
+                               clear_query (q);
                        }
-                       q = ISC_LIST_NEXT(q, link);
+                       q = nq;
                }
-               l = ISC_LIST_NEXT(l, link);
        }
+       l = ISC_LIST_HEAD(lookup_list);
+       while (l != NULL) {
+               n = ISC_LIST_NEXT(l, link);
+               ISC_LIST_DEQUEUE(lookup_list, l, link);
+               try_clear_lookup(l);
+               l = n;
+       }
+       UNLOCK_LOOKUP;
+}
+
+/*
+ * Destroy all of the libs we are using, and get everything ready for a
+ * clean shutdown.
+ */
+void
+destroy_libs(void) {
+       void *ptr;
+       dig_server_t *s;
+       dig_searchlist_t *o;
+
+       debug("destroy_libs()");
+       if (is_blocking) {
+               /*
+                * If we get here while another thread is blocking, there's
+                * really nothing we can do to make a clean shutdown
+                * without waiting for the block to complete.  The only
+                * way to get the system down now is to just exit out,
+                * and trust the OS to clean up for us.
+                */
+               fputs ("Abort.\n",stderr);
+               exit(1);
+       }
+       if (global_task != NULL) {
+               debug("freeing task");
+               isc_task_detach(&global_task);
+       }
+       /*
+        * The taskmgr_destroy() call blocks until all events are cleared
+        * from the task.
+        */
+       if (taskmgr != NULL) {
+               debug("freeing taskmgr");
+               isc_taskmgr_destroy(&taskmgr);
+        }
+       LOCK_LOOKUP;
+       REQUIRE(sockcount == 0);
+       REQUIRE(recvcount == 0);
+       REQUIRE(sendcount == 0);
+
+       INSIST(ISC_LIST_HEAD(lookup_list) == NULL);
+       INSIST(current_lookup == NULL);
+       INSIST(!free_now);
+
+       free_now = ISC_TRUE;
+
        s = ISC_LIST_HEAD(server_list);
        while (s != NULL) {
                debug("freeing global server %p", s);
                ptr = s;
                s = ISC_LIST_NEXT(s, link);
-               debug("ptr is now %p", ptr);
                isc_mem_free(mctx, ptr);
        }
        o = ISC_LIST_HEAD(search_list);
@@ -2169,6 +2599,10 @@ free_lists(void) {
                o = ISC_LIST_NEXT(o, link);
                isc_mem_free(mctx, ptr);
        }
+       if (commctx != NULL) {
+               debug("freeing commctx");
+               isc_mempool_destroy(&commctx);
+       }
        if (socketmgr != NULL) {
                debug("freeing socketmgr");
                isc_socketmgr_destroy(&socketmgr);
@@ -2177,67 +2611,13 @@ free_lists(void) {
                debug("freeing timermgr");
                isc_timermgr_destroy(&timermgr);
        }
-       if (global_task != NULL) {
-               debug("freeing task");
-               isc_task_detach(&global_task);
-       }
        if (key != NULL) {
                debug("freeing key %p", key);
-               dns_tsigkey_setdeleted(key);
                dns_tsigkey_detach(&key);
        }
        if (namebuf != NULL)
                isc_buffer_free(&namebuf);
 
-       l = ISC_LIST_HEAD(lookup_list);
-       while (l != NULL) {
-               q = ISC_LIST_HEAD(l->q);
-               while (q != NULL) {
-                       debug("freeing query %p, belonging to %p",
-                              q, l);
-                       if (ISC_LINK_LINKED(&q->recvbuf, link))
-                               ISC_LIST_DEQUEUE(q->recvlist, &q->recvbuf,
-                                                link);
-                       if (ISC_LINK_LINKED(&q->lengthbuf, link))
-                               ISC_LIST_DEQUEUE(q->lengthlist, &q->lengthbuf,
-                                                link);
-                       INSIST(q->recvspace != NULL);
-                       isc_mempool_put(commctx, q->recvspace);
-                       isc_buffer_invalidate(&q->recvbuf);
-                       isc_buffer_invalidate(&q->lengthbuf);
-                       ptr = q;
-                       q = ISC_LIST_NEXT(q, link);
-                       isc_mem_free(mctx, ptr);
-               }
-               if (l->use_my_server_list) {
-                       s = ISC_LIST_HEAD(l->my_server_list);
-                       while (s != NULL) {
-                               debug("freeing server %p belonging to %p",
-                                      s, l);
-                               ptr = s;
-                               s = ISC_LIST_NEXT(s, link);
-                               isc_mem_free(mctx, ptr);
-
-                       }
-               }
-               if (l->sendmsg != NULL)
-                       dns_message_destroy(&l->sendmsg);
-               if (l->sendspace != NULL)
-                       isc_mempool_put(commctx, l->sendspace);
-               if (l->querysig != NULL) {
-                       debug("freeing buffer %p", l->querysig);
-                       isc_buffer_free(&l->querysig);
-               }
-
-               ptr = l;
-               l = ISC_LIST_NEXT(l, link);
-               isc_mem_free(mctx, ptr);
-       }
-
-       if (keyring != NULL) {
-               debug("freeing keyring %p", keyring);
-               dns_tsigkeyring_destroy(&keyring);
-       }
        if (is_dst_up) {
                debug("destroy DST lib");
                dst_lib_destroy();
@@ -2247,8 +2627,11 @@ free_lists(void) {
                debug("detach from entropy");
                isc_entropy_detach(&entp);
        }
-       if (commctx != NULL) {
-               debug("freeing commctx");
-               isc_mempool_destroy(&commctx);
-       }
+
+       UNLOCK_LOOKUP;
+       DESTROYLOCK(&lookup_lock);
+       if (isc_mem_debugging != 0)
+               isc_mem_stats(mctx, stderr);
+       if (mctx != NULL)
+               isc_mem_destroy(&mctx);
 }
index 4f51277c9f0498210ae634e2b4ac23f3ca4cbf0e..643dc4c5adb18f291b5e306938ef9511a3aba9c2 100644 (file)
@@ -1,21 +1,21 @@
 /*
  * Copyright (C) 2000  Internet Software Consortium.
- * 
+ *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
- * 
- * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
- * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
- * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
- * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
- * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
- * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
- * SOFTWARE.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+ * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: host.c,v 1.29.2.6 2000/09/15 22:56:14 gson Exp $ */
+/* $Id: host.c,v 1.29.2.7 2000/10/06 19:08:05 mws Exp $ */
 
 #include <config.h>
 #include <stdlib.h>
@@ -25,14 +25,19 @@ extern int h_errno;
 
 #include <isc/app.h>
 #include <isc/commandline.h>
+#include <isc/netaddr.h>
 #include <isc/string.h>
 #include <isc/util.h>
 #include <isc/task.h>
 
+#include <dns/byaddr.h>
+#include <dns/fixedname.h>
 #include <dns/message.h>
 #include <dns/name.h>
 #include <dns/rdata.h>
+#include <dns/rdataclass.h>
 #include <dns/rdataset.h>
+#include <dns/rdatatype.h>
 
 #include <dig/dig.h>
 
@@ -40,30 +45,17 @@ extern ISC_LIST(dig_lookup_t) lookup_list;
 extern ISC_LIST(dig_server_t) server_list;
 extern ISC_LIST(dig_searchlist_t) search_list;
 
-extern isc_boolean_t have_ipv6, show_details, debugging;
-extern in_port_t port;
+extern isc_boolean_t debugging;
 extern unsigned int timeout;
 extern isc_mem_t *mctx;
-extern dns_messageid_t id;
-extern dns_name_t rootorg;
-extern char *rootspace[BUFSIZE];
-extern isc_buffer_t rootbuf;
-extern int sendcount;
 extern int ndots;
 extern int tries;
 extern isc_boolean_t usesearch;
 extern int lookup_counter;
-extern int exitcode;
-extern isc_taskmgr_t *taskmgr;
 extern char *progname;
 extern isc_task_t *global_task;
 
-isc_boolean_t 
-       short_form = ISC_TRUE,
-       filter = ISC_FALSE,
-       showallsoa = ISC_FALSE,
-       tcpmode = ISC_FALSE,
-       listed_server = ISC_FALSE;
+isc_boolean_t short_form = ISC_TRUE, listed_server = ISC_FALSE;
 
 static const char *opcodetext[] = {
        "QUERY",
@@ -112,7 +104,7 @@ static const char *rtypetext[] = {
        "MF",                           /* 4 */
        "is an alias for",              /* 5 */
        "SOA",                          /* 6 */
-       "MB",                           /* 7 */ 
+       "MB",                           /* 7 */
        "MG",                           /* 8 */
        "MR",                           /* 9 */
        "NULL",                         /* 10 */
@@ -213,13 +205,14 @@ static const char *rtypetext[] = {
 static void
 show_usage(void) {
        fputs(
-"Usage: host [-aCdlrTwv] [-c class] [-N ndots] [-t type] [-W time]\n"
+"Usage: host [-aCdlrTwv] [-c class] [-n] [-N ndots] [-t type] [-W time]\n"
 "            [-R number] hostname [server]\n"
 "       -a is equivalent to -v -t *\n"
 "       -c specifies query class for non-IN data\n"
 "       -C compares SOA records on authorative nameservers\n"
 "       -d is equivalent to -v\n"
 "       -l lists all hosts in a domain, using AXFR\n"
+"       -n Use the nibble form of IPv6 reverse lookup\n"
 "       -N changes the number of dots allowed before root lookup is done\n"
 "       -r disables recursive processing\n"
 "       -R specifies number of retries for UDP packets\n"
@@ -228,12 +221,11 @@ show_usage(void) {
 "       -v enables verbose output\n"
 "       -w specifies to wait forever for a reply\n"
 "       -W specifies how long to wait for a reply\n", stderr);
-       exit(exitcode);
-}                              
+       exit(1);
+}
 
 void
 dighost_shutdown(void) {
-       free_lists();
        isc_app_shutdown();
 }
 
@@ -243,7 +235,7 @@ received(int bytes, int frmsize, char *frm, dig_query_t *query) {
        isc_result_t result;
        int diff;
 
-       if ((!short_form) || (show_details)) {
+       if (!short_form) {
                result = isc_time_now(&now);
                check_result(result, "isc_time_now");
                diff = isc_time_microdiff(&now, &query->time_sent);
@@ -305,7 +297,7 @@ printsection(dns_message_t *msg, dns_section_t sectionid,
        isc_boolean_t first;
        isc_boolean_t no_rdata;
        const char *rtt;
-       
+
        if (sectionid == DNS_SECTION_QUESTION)
                no_rdata = ISC_TRUE;
        else
@@ -349,7 +341,7 @@ printsection(dns_message_t *msg, dns_section_t sectionid,
 #else
                                UNUSED(first); /* Shut up compiler. */
 #endif
-                       } else { 
+                       } else {
                                loopresult = dns_rdataset_first(rdataset);
                                while (loopresult == ISC_R_SUCCESS) {
                                        dns_rdataset_current(rdataset, &rdata);
@@ -376,14 +368,14 @@ printsection(dns_message_t *msg, dns_section_t sectionid,
                        else
                                printf("%.*s", (int)r.length, (char *)r.base);
                }
-               
+
                result = dns_message_nextname(msg, sectionid);
                if (result == ISC_R_NOMORE)
                        break;
                else if (result != ISC_R_SUCCESS)
                        return (result);
        }
-       
+
        return (ISC_R_SUCCESS);
 }
 
@@ -397,7 +389,7 @@ printrdata(dns_message_t *msg, dns_rdataset_t *rdataset, dns_name_t *owner,
        char t[4096];
 
        UNUSED(msg);
-       if (headers) 
+       if (headers)
                printf(";; %s SECTION:\n", set_name);
 
        isc_buffer_init(&target, t, sizeof(t));
@@ -423,13 +415,19 @@ printmessage(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers) {
 
        UNUSED(headers);
 
-       /*
-        * Exitcode 9 means we timed out, but if we're printing a message,
-        * we much have recovered.  Go ahead and reset it to code 0, and
-        * call this a success.
-        */
-       if (exitcode == 9)
-               exitcode = 0;
+       if (listed_server) {
+               printf("Using domain server:\n");
+               printf("Name: %s\n", query->servname);
+               result = isc_buffer_allocate(mctx, &b, MXNAME);
+               check_result(result, "isc_buffer_allocate");
+               result = isc_sockaddr_totext(&query->sockaddr, b);
+               check_result(result, "isc_sockaddr_totext");
+               printf("Address: %.*s\n",
+                      (int)isc_buffer_usedlength(b),
+                      (char*)isc_buffer_base(b));
+               isc_buffer_free(&b);
+               printf("Aliases: \n\n");
+       }
 
        if (msg->rcode != 0) {
                result = isc_buffer_allocate(mctx, &b, MXNAME);
@@ -508,21 +506,8 @@ printmessage(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers) {
                                      ISC_TF(!short_form), query);
                if (result != ISC_R_SUCCESS)
                        return (result);
-       } else {
-               if ((short_form) && (listed_server)) {
-                        printf("Using domain server:\n");
-                        printf("Name: %s\n", query->servname);
-                        result = isc_buffer_allocate(mctx, &b, MXNAME);
-                        check_result(result, "isc_buffer_allocate");
-                        result = isc_sockaddr_totext(&query->sockaddr, b);
-                        check_result(result, "isc_sockaddr_totext");
-                        printf("Address: %.*s\n",
-                               (int)isc_buffer_usedlength(b),
-                               (char*)isc_buffer_base(b));
-                        isc_buffer_free(&b);
-                        printf("Aliases: \n\n");
-                }
        }
+
        if (! ISC_LIST_EMPTY(msg->sections[DNS_SECTION_AUTHORITY]) &&
            !short_form) {
                printf("\n");
@@ -554,42 +539,65 @@ printmessage(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers) {
 
 static void
 parse_args(isc_boolean_t is_batchfile, int argc, char **argv) {
-       isc_boolean_t recursion = ISC_TRUE;
        char hostname[MXNAME];
-       char querytype[32] = "";
-       char queryclass[32] = "";
        dig_server_t *srv;
        dig_lookup_t *lookup;
        int i, c, n, adrs[4];
        char store[MXNAME];
+       isc_textregion_t tr;
+       isc_result_t result;
+       dns_rdatatype_t rdtype;
+       dns_rdataclass_t rdclass;
 
        UNUSED(is_batchfile);
 
-       while ((c = isc_commandline_parse(argc, argv, "lvwrdt:c:aTCN:R:W:D"))
+       lookup = make_empty_lookup();
+
+       while ((c = isc_commandline_parse(argc, argv, "lvwrdt:c:aTCN:R:W:Dn"))
               != EOF) {
                switch (c) {
                case 'l':
-                       tcpmode = ISC_TRUE;
-                       filter = ISC_TRUE;
-                       strcpy(querytype, "axfr");
+                       lookup->tcp_mode = ISC_TRUE;
+                       lookup->rdtype = dns_rdatatype_axfr;
                        break;
                case 'v':
                case 'd':
                        short_form = ISC_FALSE;
                        break;
                case 'r':
-                       recursion = ISC_FALSE;
+                       lookup->recurse = ISC_FALSE;
                        break;
                case 't':
-                       strncpy (querytype, isc_commandline_argument, 32);
+                       tr.base = isc_commandline_argument;
+                       tr.length = strlen(isc_commandline_argument);
+                       result = dns_rdatatype_fromtext(&rdtype,
+                                                  (isc_textregion_t *)&tr);
+
+                       if (result != ISC_R_SUCCESS)
+                               fprintf (stderr,"Warning: invalid type: %s\n",
+                                        isc_commandline_argument);
+                       else
+                               lookup->rdtype = rdtype;
                        break;
                case 'c':
-                       strncpy (queryclass, isc_commandline_argument, 32);
+                       tr.base = isc_commandline_argument;
+                       tr.length = strlen(isc_commandline_argument);
+                       result = dns_rdataclass_fromtext(&rdclass,
+                                                  (isc_textregion_t *)&tr);
+
+                       if (result != ISC_R_SUCCESS)
+                               fprintf (stderr,"Warning: invalid class: %s\n",
+                                        isc_commandline_argument);
+                       else
+                               lookup->rdclass = rdclass;
                        break;
                case 'a':
-                       strcpy (querytype, "any");
+                       lookup->rdtype = dns_rdatatype_any;
                        short_form = ISC_FALSE;
                        break;
+               case 'n':
+                       lookup->nibble = ISC_TRUE;
+                       break;
                case 'w':
                        /*
                         * The timer routines are coded such that
@@ -608,19 +616,17 @@ parse_args(isc_boolean_t is_batchfile, int argc, char **argv) {
                                tries = 1;
                        break;
                case 'T':
-                       tcpmode = ISC_TRUE;
+                       lookup->tcp_mode = ISC_TRUE;
                        break;
                case 'C':
                        debug("showing all SOAs");
-                       if (querytype[0] == 0)
-                               strcpy(querytype, "soa");
-                       if (queryclass[0] == 0)
-                               strcpy(queryclass, "in");
-                       showallsoa = ISC_TRUE;
-                       show_details = ISC_TRUE;
+                       lookup->rdtype = dns_rdatatype_soa;
+                       lookup->rdclass = dns_rdataclass_in;
+                       lookup->ns_search_only = ISC_TRUE;
+                       lookup->trace_root = ISC_TRUE;
                        break;
                case 'N':
-                       debug("setting NDOTS to %s", 
+                       debug("setting NDOTS to %s",
                              isc_commandline_argument);
                        ndots = atoi(isc_commandline_argument);
                        break;
@@ -632,33 +638,22 @@ parse_args(isc_boolean_t is_batchfile, int argc, char **argv) {
        if (isc_commandline_index >= argc) {
                show_usage();
        }
-       strncpy(hostname, argv[isc_commandline_index], MXNAME);
+       strncpy(hostname, argv[isc_commandline_index], sizeof(hostname));
+       hostname[sizeof(hostname)-1]=0;
        if (argc > isc_commandline_index + 1) {
-               srv = isc_mem_allocate(mctx, sizeof(struct dig_server));
-               if (srv == NULL)
-                       fatal("Memory allocation failure.");
-               strncpy(srv->servername,
-                       argv[isc_commandline_index+1], MXNAME-1);
+               srv = make_server(argv[isc_commandline_index+1]);
                debug("server is %s", srv->servername);
                ISC_LIST_APPEND(server_list, srv, link);
                listed_server = ISC_TRUE;
        }
-       
-       lookup = isc_mem_allocate(mctx, sizeof(struct dig_lookup));
-       if (lookup == NULL)     
-               fatal("Memory allocation failure.");
+
        lookup->pending = ISC_FALSE;
-       /* 
-        * XXXMWS Add IPv6 translation here, probably using inet_pton
-        * to extract the formatted text.
-        */
        if (strspn(hostname, "0123456789.") == strlen(hostname)) {
                lookup->textname[0] = 0;
                n = sscanf(hostname, "%d.%d.%d.%d", &adrs[0], &adrs[1],
                                   &adrs[2], &adrs[3]);
                if (n == 0) {
                        show_usage();
-                       exit(exitcode);
                }
                for (i = n - 1; i >= 0; i--) {
                        snprintf(store, MXNAME/8, "%d.",
@@ -666,47 +661,36 @@ parse_args(isc_boolean_t is_batchfile, int argc, char **argv) {
                        strncat(lookup->textname, store, MXNAME);
                }
                strncat(lookup->textname, "in-addr.arpa.", MXNAME);
-               if (querytype[0] == 0)
-                       strcpy(querytype, "ptr");
+               lookup->rdtype = dns_rdatatype_ptr;
+       } else if (strspn(hostname, "0123456789abcdef.:") == strlen(hostname))
+       {
+               isc_netaddr_t addr;
+               dns_fixedname_t fname;
+               isc_buffer_t b;
+
+               addr.family = AF_INET6;
+               n = inet_pton(AF_INET6, hostname, &addr.type.in6);
+               if (n <= 0)
+                       goto notv6;
+               dns_fixedname_init(&fname);
+               result = dns_byaddr_createptrname(&addr, lookup->nibble,
+                                                 dns_fixedname_name(&fname));
+               if (result != ISC_R_SUCCESS)
+                       show_usage();
+               isc_buffer_init(&b, lookup->textname, sizeof lookup->textname);
+               result = dns_name_totext(dns_fixedname_name(&fname),
+                                        ISC_FALSE, &b);
+               isc_buffer_putuint8(&b, 0);
+               if (result != ISC_R_SUCCESS)
+                       show_usage();
+               lookup->rdtype = dns_rdatatype_ptr;
        } else {
-               strncpy(lookup->textname, hostname, MXNAME);
+ notv6:
+               strncpy(lookup->textname, hostname, sizeof(lookup->textname));
+               lookup->textname[sizeof(lookup->textname)-1]=0;
        }
-       if (querytype[0] == 0)
-               strcpy(querytype, "a");
-       if (queryclass[0] == 0)
-               strcpy(queryclass, "in");
-       strncpy(lookup->rttext, querytype, 32);
-       strncpy(lookup->rctext, queryclass, 32);
-       lookup->namespace[0] = 0;
-       lookup->sendspace = NULL;
-       lookup->sendmsg = NULL;
-       lookup->name = NULL;
-       lookup->oname = NULL;
-       lookup->timer = NULL;
-       lookup->xfr_q = NULL;
-       lookup->origin = NULL;
-       lookup->querysig = NULL;
-       lookup->doing_xfr = ISC_FALSE;
-       lookup->ixfr_serial = 0;
-       lookup->defname = ISC_FALSE;
-       lookup->identify = ISC_FALSE;
-       lookup->recurse = recursion;
-       lookup->ns_search_only = showallsoa;
-       lookup->use_my_server_list = ISC_FALSE;
-       lookup->retries = tries;
-       lookup->udpsize = 0;
-       lookup->nsfound = 0;
-       lookup->trace = ISC_FALSE;
-       lookup->trace_root = showallsoa;
-       lookup->tcp_mode = tcpmode;
        lookup->new_search = ISC_TRUE;
-       lookup->aaonly = ISC_FALSE;
-       lookup->adflag = ISC_FALSE;
-       lookup->cdflag = ISC_FALSE;
-       ISC_LIST_INIT(lookup->q);
        ISC_LIST_APPEND(lookup_list, lookup, link);
-       lookup->origin = NULL;
-       ISC_LIST_INIT(lookup->my_server_list);
 
        usesearch = ISC_TRUE;
 }
@@ -721,28 +705,17 @@ main(int argc, char **argv) {
 
        debug("main()");
        progname = argv[0];
+       result = isc_app_start();
+       check_result(result, "isc_app_start");
        setup_libs();
        parse_args(ISC_FALSE, argc, argv);
        setup_system();
        result = isc_app_onrun(mctx, global_task, onrun_callback, NULL);
        check_result(result, "isc_app_onrun");
        isc_app_run();
-       /*
-        * XXXMWS This code should really NOT be bypassed.  However,
-        * until the proper code can be added to handle SIGTERM/INT
-        * correctly, just exit out "hard" and deal as best we can.
-        */
-#if 0
-       if (taskmgr != NULL) {
-               debug("freeing taskmgr");
-               isc_taskmgr_destroy(&taskmgr);
-        }
-       if (isc_mem_debugging)
-               isc_mem_stats(mctx, stderr);
+       cancel_all();
+       destroy_libs();
        isc_app_finish();
-       if (mctx != NULL)
-               isc_mem_destroy(&mctx); 
-#endif
        return (0);
 }
 
index fa85a9c7c6cfe4044e877d4a713a39d0b612d63b..fe409b1e8550c4e2c648b99a9a21f3cf5d279ceb 100644 (file)
@@ -1,21 +1,21 @@
 /*
  * Copyright (C) 2000  Internet Software Consortium.
- * 
+ *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
- * 
- * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
- * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
- * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
- * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
- * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
- * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
- * SOFTWARE.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+ * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: dig.h,v 1.25.2.4 2000/08/07 23:50:17 gson Exp $ */
+/* $Id: dig.h,v 1.25.2.5 2000/10/06 19:08:08 mws Exp $ */
 
 #ifndef DIG_H
 #define DIG_H
@@ -32,8 +32,8 @@
 #include <isc/sockaddr.h>
 #include <isc/socket.h>
 
-#define MXSERV 4
-#define MXNAME 1005
+#define MXSERV 6
+#define MXNAME (1024)
 #define MXRD 32
 #define BUFSIZE 512
 #define COMMSIZE 0xffff
 /*
  * Default timeout values
  */
-#define TCP_TIMEOUT 60
-#define UDP_TIMEOUT 30
+#define TCP_TIMEOUT 10
+#define UDP_TIMEOUT 5
+
+#define SERVER_TIMEOUT 1
 
 #define LOOKUP_LIMIT 64
 /*
@@ -66,6 +68,7 @@ ISC_LANG_BEGINDECLS
 typedef struct dig_lookup dig_lookup_t;
 typedef struct dig_query dig_query_t;
 typedef struct dig_server dig_server_t;
+typedef ISC_LIST(dig_server_t) dig_serverlist_t;
 typedef struct dig_searchlist dig_searchlist_t;
 
 struct dig_lookup {
@@ -74,8 +77,8 @@ struct dig_lookup {
                waiting_connect,
                doing_xfr,
                ns_search_only,
-               use_my_server_list,
                identify,
+               ignore,
                recurse,
                aaonly,
                adflag,
@@ -84,16 +87,19 @@ struct dig_lookup {
                trace_root,
                defname,
                tcp_mode,
+               nibble,
                comments,
                stats,
                section_question,
                section_answer,
                section_authority,
                section_additional,
+               servfail_stops,
                new_search;
        char textname[MXNAME]; /* Name we're going to be looking up */
-       char rttext[MXRD]; /* rdata type text */
-       char rctext[MXRD]; /* rdata class text */
+       char cmdline[MXNAME];
+       dns_rdatatype_t rdtype;
+       dns_rdataclass_t rdclass;
        char namespace[BUFSIZE];
        char onamespace[BUFSIZE];
        isc_buffer_t namebuf;
@@ -107,7 +113,8 @@ struct dig_lookup {
        dns_name_t *oname;
        ISC_LINK(dig_lookup_t) link;
        ISC_LIST(dig_query_t) q;
-       ISC_LIST(dig_server_t) my_server_list;
+       dig_query_t *current_query;
+       dig_serverlist_t my_server_list;
        dig_searchlist_t *origin;
        dig_query_t *xfr_q;
        int retries;
@@ -123,15 +130,16 @@ struct dig_lookup {
 
 struct dig_query {
        dig_lookup_t *lookup;
-       isc_boolean_t working,
-               waiting_connect,
+       isc_boolean_t waiting_connect,
                first_pass,
                first_soa_rcvd,
                second_rr_rcvd,
-               first_repeat_rcvd;
+               first_repeat_rcvd,
+               recv_made;
        isc_uint32_t first_rr_serial;
        isc_uint32_t second_rr_serial;
-       int retries;
+       isc_uint32_t rr_count;
+       isc_uint32_t name_count;
        char *servname;
        isc_bufferlist_t sendlist,
                recvlist,
@@ -173,12 +181,6 @@ debug(const char *format, ...);
 void
 check_result(isc_result_t result, const char *msg);
 
-isc_boolean_t
-isclass(char *text);
-
-isc_boolean_t
-istype(char *text);
-
 void
 setup_lookup(dig_lookup_t *lookup);
 
@@ -191,9 +193,6 @@ start_lookup(void);
 void
 onrun_callback(isc_task_t *task, isc_event_t *event);
 
-void
-send_udp(dig_lookup_t *lookup);
-
 int
 dhmain(int argc, char **argv);
 
@@ -203,15 +202,28 @@ setup_libs(void);
 void
 setup_system(void);
 
-void
-free_lists(void);
-
 dig_lookup_t *
 requeue_lookup(dig_lookup_t *lookold, isc_boolean_t servers);
 
+dig_lookup_t *
+make_empty_lookup(void);
+
+dig_lookup_t *
+clone_lookup(dig_lookup_t *lookold, isc_boolean_t servers);
+
 dig_server_t *
 make_server(const char *servname);
 
+void
+clone_server_list(dig_serverlist_t src,
+                 dig_serverlist_t *dest);
+
+void
+cancel_all(void);
+
+void
+destroy_libs(void);
+
 /*
  * Routines needed in dig.c and host.c.
  */
@@ -227,6 +239,9 @@ trying(int frmsize, char *frm, dig_lookup_t *lookup);
 void
 dighost_shutdown(void);
 
+char *
+next_token(char **stringp, const char *delim);
+
 ISC_LANG_ENDDECLS
 
 #endif
index f00fc3a0f715be73cc29640c57484bbb98ba2765..fd9a0da6ae30768b4d42cb63d28d7850ea438430 100644 (file)
@@ -1,21 +1,21 @@
 /*
  * Copyright (C) 2000  Internet Software Consortium.
- * 
+ *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * copyright notice and this permission notice appear in all copies.
- * 
- * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
- * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
- * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
- * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
- * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
- * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
- * SOFTWARE.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+ * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+ * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: nslookup.c,v 1.20.2.8 2000/09/15 22:56:15 gson Exp $ */
+/* $Id: nslookup.c,v 1.20.2.9 2000/10/06 19:08:06 mws Exp $ */
 
 #include <config.h>
 
 
 extern int h_errno;
 
-#include <dns/message.h>
-#include <dns/name.h>
-#include <dns/rdata.h>
-#include <dns/rdataset.h>
-#include <dns/rdatatype.h>
-#include <dns/rdataclass.h>
 #include <isc/app.h>
 #include <isc/buffer.h>
 #include <isc/commandline.h>
-#include <isc/condition.h>
-#include <isc/mutex.h>
+#include <isc/event.h>
 #include <isc/string.h>
 #include <isc/timer.h>
 #include <isc/util.h>
 #include <isc/task.h>
+#include <isc/netaddr.h>
+
+#include <dns/message.h>
+#include <dns/name.h>
+#include <dns/fixedname.h>
+#include <dns/rdata.h>
+#include <dns/rdataclass.h>
+#include <dns/rdataset.h>
+#include <dns/rdatatype.h>
+#include <dns/byaddr.h>
 
 #include <dig/dig.h>
 
@@ -45,8 +48,8 @@ extern ISC_LIST(dig_lookup_t) lookup_list;
 extern ISC_LIST(dig_server_t) server_list;
 extern ISC_LIST(dig_searchlist_t) search_list;
 
-extern isc_boolean_t have_ipv6, show_details,
-       usesearch, trace, qr, debugging;
+extern isc_boolean_t have_ipv6,
+       usesearch, trace, qr, debugging, is_blocking;
 extern in_port_t port;
 extern unsigned int timeout;
 extern isc_mem_t *mctx;
@@ -60,7 +63,7 @@ extern int lookup_counter;
 extern char fixeddomain[MXNAME];
 extern int exitcode;
 extern isc_taskmgr_t *taskmgr;
-extern isc_mempool_t *commctx;
+extern isc_task_t *global_task;
 extern char *progname;
 
 isc_boolean_t short_form = ISC_TRUE, printcmd = ISC_TRUE,
@@ -75,11 +78,10 @@ isc_boolean_t identify = ISC_FALSE,
        section_answer = ISC_TRUE, section_authority = ISC_TRUE,
        section_additional = ISC_TRUE, recurse = ISC_TRUE,
        defname = ISC_TRUE, aaonly = ISC_FALSE;
-isc_mutex_t lock;
-isc_condition_t cond;
 isc_boolean_t busy = ISC_FALSE, in_use = ISC_FALSE;
 char defclass[MXRD] = "IN";
 char deftype[MXRD] = "A";
+isc_event_t *global_event = NULL;
 
 static const char *rcodetext[] = {
        "NOERROR",
@@ -109,7 +111,7 @@ static const char *rtypetext[] = {
        "mf = ",                        /* 4 */
        "canonical name = ",            /* 5 */
        "soa = ",                       /* 6 */
-       "mb = ",                        /* 7 */ 
+       "mb = ",                        /* 7 */
        "mg = ",                        /* 8 */
        "mr = ",                        /* 9 */
        "rtype_10 = ",                  /* 10 */
@@ -146,41 +148,45 @@ static const char *rtypetext[] = {
        "optional = "};                 /* 41 */
 
 
+static void flush_lookup_list(void);
+static void getinput(isc_task_t *task, isc_event_t *event);
+
 static void
 show_usage(void) {
-       fputs (
-"Usage:\n"
-, stderr);
-}                              
+       fputs("Usage:\n", stderr);
+}
 
 void
 dighost_shutdown(void) {
+       isc_event_t *event = global_event;
 
-       debug ("dighost_dhutdown()");
-       isc_mutex_lock(&lock);
-       busy = ISC_FALSE;
-       debug ("signalling out");
-       isc_condition_signal(&cond);
-       isc_mutex_unlock(&lock);
+       flush_lookup_list();
+       debug("dighost_shutdown()");
+
+       if (!in_use) {
+               isc_app_shutdown();
+               return;
+       }
 
+       isc_task_send(global_task, &event);
 }
+
 void
 received(int bytes, int frmsize, char *frm, dig_query_t *query) {
-       UNUSED (bytes);
-       UNUSED (frmsize);
-       UNUSED (frm);
-       UNUSED (query);
+       UNUSED(bytes);
+       UNUSED(frmsize);
+       UNUSED(frm);
+       UNUSED(query);
 }
 
 void
 trying(int frmsize, char *frm, dig_lookup_t *lookup) {
-       UNUSED (frmsize);
-       UNUSED (frm);
-       UNUSED (lookup);
+       UNUSED(frmsize);
+       UNUSED(frm);
+       UNUSED(lookup);
 
 }
 
-
 static isc_result_t
 printsection(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers,
             dns_section_t section) {
@@ -190,20 +196,13 @@ printsection(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers,
        dns_rdataset_t *rdataset = NULL;
        dns_rdata_t rdata;
        char *ptr;
+       char *input;
 
-       UNUSED (query);
-       UNUSED (headers);
+       UNUSED(query);
+       UNUSED(headers);
 
        debug("printsection()");
 
-       /*
-        * Exitcode 9 means we timed out, but if we're printing a message,
-        * we much have recovered.  Go ahead and reset it to code 0, and
-        * call this a success.
-        */
-       if (exitcode == 9)
-               exitcode = 0;
-
        result = dns_message_firstname(msg, section);
        if (result == ISC_R_NOMORE)
                return (ISC_R_SUCCESS);
@@ -213,7 +212,7 @@ printsection(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers,
        check_result(result, "isc_buffer_allocate");
        for (;;) {
                name = NULL;
-               dns_message_currentname(msg, section, 
+               dns_message_currentname(msg, section,
                                        &name);
                for (rdataset = ISC_LIST_HEAD(name->list);
                     rdataset != NULL;
@@ -261,41 +260,41 @@ printsection(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers,
                                        check_result(result,
                                                     "dns_rdata_totext");
                                        ((char *)isc_buffer_used(b))[0]=0;
-                                       ptr = strtok(isc_buffer_base(b),
-                                                    " \t\r\n");
+                                       input = isc_buffer_base(b);
+                                       ptr = next_token(&input, " \t\r\n");
                                        if (ptr == NULL)
                                                break;
                                        printf("\torigin = %s\n",
                                               ptr);
-                                       ptr = strtok(NULL, " \t\r\n");
+                                       ptr = next_token(&input, " \t\r\n");
                                        if (ptr == NULL)
                                                break;
                                        printf("\tmail addr = %s\n",
                                               ptr);
-                                       ptr = strtok(NULL, " \t\r\n");
+                                       ptr = next_token(&input, " \t\r\n");
                                        if (ptr == NULL)
                                                break;
-                                       ptr = strtok(NULL, " \t\r\n");
+                                       ptr = next_token(&input, " \t\r\n");
                                        if (ptr == NULL)
                                                break;
                                        printf("\tserial = %s\n",
                                               ptr);
-                                       ptr = strtok(NULL, " \t\r\n");
+                                       ptr = next_token(&input, " \t\r\n");
                                        if (ptr == NULL)
                                                break;
                                        printf("\trefresh = %s\n",
                                               ptr);
-                                       ptr = strtok(NULL, " \t\r\n");
+                                       ptr = next_token(&input, " \t\r\n");
                                        if (ptr == NULL)
                                                break;
                                        printf("\tretry = %s\n",
                                               ptr);
-                                       ptr = strtok(NULL, " \t\r\n");
+                                       ptr = next_token(&input, " \t\r\n");
                                        if (ptr == NULL)
                                                break;
                                        printf("\texpire = %s\n",
                                               ptr);
-                                       ptr = strtok(NULL, " \t\r\n");
+                                       ptr = next_token(&input, " \t\r\n");
                                        if (ptr == NULL)
                                                break;
                                        printf("\tminimum = %s\n",
@@ -310,24 +309,24 @@ printsection(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers,
                                        check_result(result,
                                                     "dns_name_totext");
                                        if (rdata.type <= 41)
-                                               printf ("%.*s\t%s",
+                                               printf("%.*s\t%s",
                                                (int)isc_buffer_usedlength(b),
                                                (char*)isc_buffer_base(b),
                                                rtypetext[rdata.type]);
                                        else
-                                               printf ("%.*s\trdata_%d = ",
+                                               printf("%.*s\trdata_%d = ",
                                                (int)isc_buffer_usedlength(b),
                                                (char*)isc_buffer_base(b),
                                                 rdata.type);
                                        isc_buffer_clear(b);
-                                       result = dns_rdata_totext(&rdata, 
+                                       result = dns_rdata_totext(&rdata,
                                                                  NULL, b);
                                        check_result(result,
                                                     "dns_rdata_totext");
                                        printf("%.*s\n",
                                               (int)isc_buffer_usedlength(b),
                                               (char*)isc_buffer_base(b));
-                               }       
+                               }
                                loopresult = dns_rdataset_next(rdataset);
                        }
                }
@@ -352,32 +351,25 @@ detailsection(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers,
        dns_rdataset_t *rdataset = NULL;
        dns_rdata_t rdata;
        char *ptr;
+       char *input;
 
-       UNUSED (query);
+       UNUSED(query);
 
        debug("detailsection()");
 
-       /*
-        * Exitcode 9 means we timed out, but if we're printing a message,
-        * we much have recovered.  Go ahead and reset it to code 0, and
-        * call this a success.
-        */
-       if (exitcode == 9)
-               exitcode = 0;
-
        if (headers) {
                switch (section) {
                case DNS_SECTION_QUESTION:
-                       puts ("    QUESTIONS:");
+                       puts("    QUESTIONS:");
                        break;
                case DNS_SECTION_ANSWER:
-                       puts ("    ANSWERS:");
+                       puts("    ANSWERS:");
                        break;
                case DNS_SECTION_AUTHORITY:
-                       puts ("    AUTHORITY RECORDS:");
+                       puts("    AUTHORITY RECORDS:");
                        break;
                case DNS_SECTION_ADDITIONAL:
-                       puts ("    ADDITIONAL RECORDS:");
+                       puts("    ADDITIONAL RECORDS:");
                        break;
                }
        }
@@ -391,7 +383,7 @@ detailsection(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers,
        check_result(result, "isc_buffer_allocate");
        for (;;) {
                name = NULL;
-               dns_message_currentname(msg, section, 
+               dns_message_currentname(msg, section,
                                        &name);
                for (rdataset = ISC_LIST_HEAD(name->list);
                     rdataset != NULL;
@@ -417,41 +409,41 @@ detailsection(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers,
                                        check_result(result,
                                                     "dns_rdata_totext");
                                        ((char *)isc_buffer_used(b))[0]=0;
-                                       ptr = strtok(isc_buffer_base(b),
-                                                    " \t\r\n");
+                                       input = isc_buffer_base(b);
+                                       ptr = next_token(&input, " \t\r\n");
                                        if (ptr == NULL)
                                                break;
                                        printf("\torigin = %s\n",
                                               ptr);
-                                       ptr = strtok(NULL, " \t\r\n");
+                                       ptr = next_token(&input, " \t\r\n");
                                        if (ptr == NULL)
                                                break;
                                        printf("\tmail addr = %s\n",
                                               ptr);
-                                       ptr = strtok(NULL, " \t\r\n");
+                                       ptr = next_token(&input, " \t\r\n");
                                        if (ptr == NULL)
                                                break;
-                                       ptr = strtok(NULL, " \t\r\n");
+                                       ptr = next_token(&input, " \t\r\n");
                                        if (ptr == NULL)
                                                break;
                                        printf("\tserial = %s\n",
                                               ptr);
-                                       ptr = strtok(NULL, " \t\r\n");
+                                       ptr = next_token(&input, " \t\r\n");
                                        if (ptr == NULL)
                                                break;
                                        printf("\trefresh = %s\n",
                                               ptr);
-                                       ptr = strtok(NULL, " \t\r\n");
+                                       ptr = next_token(&input, " \t\r\n");
                                        if (ptr == NULL)
                                                break;
                                        printf("\tretry = %s\n",
                                               ptr);
-                                       ptr = strtok(NULL, " \t\r\n");
+                                       ptr = next_token(&input, " \t\r\n");
                                        if (ptr == NULL)
                                                break;
                                        printf("\texpire = %s\n",
                                               ptr);
-                                       ptr = strtok(NULL, " \t\r\n");
+                                       ptr = next_token(&input, " \t\r\n");
                                        if (ptr == NULL)
                                                break;
                                        printf("\tminimum = %s\n",
@@ -460,20 +452,20 @@ detailsection(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers,
                                default:
                                        isc_buffer_clear(b);
                                        if (rdata.type <= 41)
-                                               printf ("\t%s",
+                                               printf("\t%s",
                                                rtypetext[rdata.type]);
                                        else
-                                               printf ("\trdata_%d = ",
+                                               printf("\trdata_%d = ",
                                                 rdata.type);
                                        isc_buffer_clear(b);
-                                       result = dns_rdata_totext(&rdata, 
+                                       result = dns_rdata_totext(&rdata,
                                                                  NULL, b);
                                        check_result(result,
                                                     "dns_rdata_totext");
                                        printf("%.*s\n",
                                               (int)isc_buffer_usedlength(b),
                                               (char*)isc_buffer_base(b));
-                               }       
+                               }
                                loopresult = dns_rdataset_next(rdataset);
                        }
                }
@@ -495,9 +487,8 @@ printmessage(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers) {
        isc_region_t r;
        isc_result_t result;
 
-       debug ("printmessage()");
-
-       debug ("continuing on with rcode != 0");
+       debug("printmessage()");
+       debug("continuing on with rcode != 0");
        result = isc_buffer_allocate(mctx, &b, MXNAME);
        check_result(result, "isc_buffer_allocate");
        printf("Server:\t\t%s\n", query->servname);
@@ -507,6 +498,7 @@ printmessage(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers) {
               (char*)isc_buffer_base(b));
        isc_buffer_free(&b);
        puts("");
+
        if (msg->rcode != 0) {
                result = isc_buffer_allocate(mctx, &b, MXNAME);
                check_result(result, "isc_buffer_allocate");
@@ -518,26 +510,30 @@ printmessage(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers) {
                       (int)r.length, (char*)r.base,
                       rcodetext[msg->rcode]);
                isc_buffer_free(&b);
-               debug ("returning with rcode == 0");
+               debug("returning with rcode == 0");
                return (ISC_R_SUCCESS);
        }
        if (!short_form){
-               puts ("------------");
+               puts("------------");
                /*              detailheader(query, msg);*/
                detailsection(query, msg, headers, DNS_SECTION_QUESTION);
                detailsection(query, msg, headers, DNS_SECTION_ANSWER);
                detailsection(query, msg, headers, DNS_SECTION_AUTHORITY);
                detailsection(query, msg, headers, DNS_SECTION_ADDITIONAL);
-               puts ("------------");
+               puts("------------");
        }
-       
+
        if ((msg->flags & DNS_MESSAGEFLAG_AA) == 0)
-               puts ("Non-authorative answer:");
-       printsection(query, msg, headers, DNS_SECTION_ANSWER);
-       
+               puts("Non-authorative answer:");
+       if (!ISC_LIST_EMPTY(msg->sections[DNS_SECTION_ANSWER]))
+               printsection(query, msg, headers, DNS_SECTION_ANSWER);
+       else
+               printf("*** Can't find %s: No answer\n",
+                      query->lookup->textname);
+
        if (((msg->flags & DNS_MESSAGEFLAG_AA) == 0) &&
-           (strcasecmp(query->lookup->rttext,"a") != 0)) {
-               puts ("\nAuthorative answers can be found from:");
+           (query->lookup->rdtype != dns_rdatatype_a)) {
+               puts("\nAuthorative answers can be found from:");
                printsection(query, msg, headers,
                             DNS_SECTION_AUTHORITY);
                printsection(query, msg, headers,
@@ -547,21 +543,21 @@ printmessage(dig_query_t *query, dns_message_t *msg, isc_boolean_t headers) {
 }
 
 static void
-show_settings(isc_boolean_t full) {
+show_settings(isc_boolean_t full, isc_boolean_t serv_only) {
        dig_server_t *srv;
        isc_sockaddr_t sockaddr;
        isc_buffer_t *b = NULL;
        isc_result_t result;
-       
+
        srv = ISC_LIST_HEAD(server_list);
 
        while (srv != NULL) {
                result = isc_buffer_allocate(mctx, &b, MXNAME);
                check_result(result, "isc_buffer_allocate");
-               get_address(srv->servername, 53, &sockaddr);
+               get_address(srv->servername, port, &sockaddr);
                result = isc_sockaddr_totext(&sockaddr, b);
                check_result(result, "isc_sockaddr_totext");
-               printf ("Default server: %s\nAddress: %.*s\n",
+               printf("Default server: %s\nAddress: %.*s\n",
                        srv->servername, (int)isc_buffer_usedlength(b),
                        (char*)isc_buffer_base(b));
                isc_buffer_free(&b);
@@ -569,19 +565,21 @@ show_settings(isc_boolean_t full) {
                        return;
                srv = ISC_LIST_NEXT(srv, link);
        }
-       printf ("\n\tSet options:\n");
-       printf ("\t  %s\t\t\t%s\t\t%s\n",
+       if (serv_only)
+               return;
+       printf("\n\tSet options:\n");
+       printf("\t  %s\t\t\t%s\t\t%s\n",
                tcpmode?"vc":"novc", short_form?"nodebug":"debug",
                debugging?"d2":"nod2");
-       printf ("\t  %s\t\t%s\t\t%s\n",
+       printf("\t  %s\t\t%s\t%s\n",
                defname?"defname":"nodefname",
-               usesearch?"search":"nosearch",
+               usesearch?"search  ":"nosearch",
                recurse?"recurse":"norecurse");
-       printf ("\t  timeout = %d\t\tretry = %d\tport = %d\n",
+       printf("\t  timeout = %d\t\tretry = %d\tport = %d\n",
                timeout, tries, port);
-       printf ("\t  querytype = %-8s\tclass=%s\n",deftype, defclass);
+       printf("\t  querytype = %-8s\tclass = %s\n", deftype, defclass);
 #if 0
-       printf ("\t  domain = %s\n", fixeddomain);
+       printf("\t  domain = %s\n", fixeddomain);
 #endif
 
 }
@@ -591,7 +589,7 @@ testtype(char *typetext) {
        isc_result_t result;
        isc_textregion_t tr;
        dns_rdatatype_t rdtype;
-       
+
        tr.base = typetext;
        tr.length = strlen(typetext);
        result = dns_rdatatype_fromtext(&rdtype, &tr);
@@ -620,40 +618,44 @@ testclass(char *typetext) {
        }
 }
 
+static void
+safecpy(char *dest, char *src, int size) {
+       strncpy(dest, src, size);
+       dest[size-1]=0;
+}
+       
 
 static void
 setoption(char *opt) {
-       dig_server_t *srv;
-
-       if (strncasecmp(opt,"all",4) == 0) {
-               show_settings(ISC_TRUE);
+       if (strncasecmp(opt, "all", 4) == 0) {
+               show_settings(ISC_TRUE, ISC_FALSE);
        } else if (strncasecmp(opt, "class=", 6) == 0) {
                if (testclass(&opt[6]))
-                       strncpy(defclass, &opt[6], MXRD);
+                       safecpy(defclass, &opt[6], MXRD);
        } else if (strncasecmp(opt, "cl=", 3) == 0) {
                if (testclass(&opt[3]))
-                       strncpy(defclass, &opt[3], MXRD);
+                       safecpy(defclass, &opt[3], MXRD);
        } else if (strncasecmp(opt, "type=", 5) == 0) {
                if (testtype(&opt[5]))
-                       strncpy(deftype, &opt[5], MXRD);
+                       safecpy(deftype, &opt[3], MXRD);
        } else if (strncasecmp(opt, "ty=", 3) == 0) {
                if (testtype(&opt[3]))
-                       strncpy(deftype, &opt[3], MXRD);
+                       safecpy(deftype, &opt[3], MXRD);
        } else if (strncasecmp(opt, "querytype=", 10) == 0) {
                if (testtype(&opt[10]))
-                       strncpy(deftype, &opt[10], MXRD);
+                       safecpy(deftype, &opt[10], MXRD);
        } else if (strncasecmp(opt, "query=", 6) == 0) {
                if (testtype(&opt[6]))
-                       strncpy(deftype, &opt[6], MXRD);
+                       safecpy(deftype, &opt[6], MXRD);
        } else if (strncasecmp(opt, "qu=", 3) == 0) {
                if (testtype(&opt[3]))
-                       strncpy(deftype, &opt[3], MXRD);
+                       safecpy(deftype, &opt[3], MXRD);
 #if 0
                /* XXXMWS domain= doesn't work now. */
        } else if (strncasecmp(opt, "domain=", 7) == 0) {
-               strncpy(fixeddomain, &opt[7], MXNAME);
+               safecpy(fixeddomain, &opt[7], MXNAME);
        } else if (strncasecmp(opt, "do=", 3) == 0) {
-               strncpy(fixeddomain, &opt[3], MXNAME);
+               safecpy(fixeddomain, &opt[3], MXNAME);
 #endif
        } else if (strncasecmp(opt, "port=", 5) == 0) {
                port = atoi(&opt[5]);
@@ -679,61 +681,95 @@ setoption(char *opt) {
                debugging = ISC_TRUE;
        } else if (strncasecmp(opt, "nod2", 4) == 0) {
                debugging = ISC_FALSE;
+       } else if (strncasecmp(opt, "search",3) == 0) {
+               usesearch = ISC_TRUE;
+       } else if (strncasecmp(opt, "nosearch",5) == 0) {
+               usesearch = ISC_FALSE;
        } else if (strncasecmp(opt, "sil",3) == 0) {
                deprecation_msg = ISC_FALSE;
        } else {
-               srv = make_server(opt);
-               debug("server is %s", srv->servername);
-               ISC_LIST_APPEND(server_list, srv, link);
+               printf("*** Invalid option: %s\n", opt);        
        }
 }
 
 static dig_lookup_t*
 addlookup(char *opt) {
        dig_lookup_t *lookup;
+       isc_result_t result;
+       isc_textregion_t tr;
+       dns_rdatatype_t rdtype;
+       dns_rdataclass_t rdclass;
 
-       debug ("addlookup()");
-       lookup = isc_mem_allocate(mctx, sizeof(struct dig_lookup));
-       if (lookup == NULL)
-               fatal("Memory allocation failure.");
-       lookup->pending = ISC_FALSE;
-       strncpy(lookup->textname, opt, MXNAME-1);
-       if (istype(deftype))
-               strncpy(lookup->rttext, deftype, MXNAME);
-       else {
-               strcpy(lookup->rttext, "a");
-               printf ("unknown query type: %s\n",deftype);
+       debug("addlookup()");
+       tr.base = deftype;
+       tr.length = strlen(deftype);
+       result = dns_rdatatype_fromtext(&rdtype, &tr);
+       if (result != ISC_R_SUCCESS) {
+               printf("unknown query type: %s\n", deftype);
+               rdclass = dns_rdatatype_a;
        }
-       if (isclass(defclass))
-               strncpy(lookup->rctext, defclass, MXNAME);
-       else {
-               strcpy(lookup->rctext, "in");
-               printf ("unknown query class: %s\n",defclass);
+       tr.base = defclass;
+       tr.length = strlen(defclass);
+       result = dns_rdataclass_fromtext(&rdclass, &tr);
+       if (result != ISC_R_SUCCESS) {
+               printf("unknown query class: %s\n", defclass);
+               rdclass = dns_rdataclass_in;
        }
-       lookup->namespace[0]=0;
-       lookup->sendspace = NULL;
-       lookup->sendmsg=NULL;
-       lookup->name=NULL;
-       lookup->oname=NULL;
-       lookup->timer = NULL;
-       lookup->xfr_q = NULL;
-       lookup->origin = NULL;
-       lookup->querysig = NULL;
-       lookup->use_my_server_list = ISC_FALSE;
-       lookup->doing_xfr = ISC_FALSE;
-       lookup->ixfr_serial = 0;
-       lookup->defname = ISC_FALSE;
+       lookup = make_empty_lookup();
+       if (strspn(opt, "0123456789.") == strlen(opt)) {
+               int n, i, adrs[4];
+               char store[MXNAME];
+
+               lookup->textname[0] = 0;
+               n = sscanf(opt, "%d.%d.%d.%d", &adrs[0], &adrs[1],
+                                  &adrs[2], &adrs[3]);
+               if (n == 0) {
+                       show_usage();
+               }
+               for (i = n - 1; i >= 0; i--) {
+                       snprintf(store, MXNAME/8, "%d.",
+                                adrs[i]);
+                       strncat(lookup->textname, store, MXNAME);
+               }
+               strncat(lookup->textname, "in-addr.arpa.", MXNAME);
+               lookup->rdtype = dns_rdatatype_ptr;
+       } else if (strspn(opt, "0123456789abcdef.:") == strlen(opt))
+       {
+               isc_netaddr_t addr;
+               dns_fixedname_t fname;
+               isc_buffer_t b;
+               int n;
+
+               addr.family = AF_INET6;
+               n = inet_pton(AF_INET6, opt, &addr.type.in6);
+               if (n <= 0)
+                       goto notv6;
+               dns_fixedname_init(&fname);
+               result = dns_byaddr_createptrname(&addr, lookup->nibble,
+                                                 dns_fixedname_name(&fname));
+               if (result != ISC_R_SUCCESS)
+                       show_usage();
+               isc_buffer_init(&b, lookup->textname, sizeof lookup->textname);
+               result = dns_name_totext(dns_fixedname_name(&fname),
+                                        ISC_FALSE, &b);
+               isc_buffer_putuint8(&b, 0);
+               if (result != ISC_R_SUCCESS)
+                       show_usage();
+               lookup->rdtype = dns_rdatatype_ptr;
+       } else {
+       notv6:
+               safecpy(lookup->textname, opt, MXNAME-1);
+               lookup->rdtype = rdtype;
+       }
+       lookup->rdclass = rdclass;
        lookup->trace = ISC_TF(trace || ns_search_only);
        lookup->trace_root = trace;
        lookup->ns_search_only = ns_search_only;
        lookup->identify = identify;
        lookup->recurse = recurse;
        lookup->aaonly = aaonly;
-       lookup->adflag = ISC_FALSE;
-       lookup->cdflag = ISC_FALSE;
        lookup->retries = tries;
        lookup->udpsize = bufsize;
-       lookup->nsfound = 0;
        lookup->comments = comments;
        lookup->tcp_mode = tcpmode;
        lookup->stats = stats;
@@ -754,7 +790,7 @@ static void
 flush_server_list(void) {
        dig_server_t *s, *ps;
 
-       debug ("flush_lookup_list()");
+       debug("flush_server_list()");
        s = ISC_LIST_HEAD(server_list);
        while (s != NULL) {
                ps = s;
@@ -762,10 +798,9 @@ flush_server_list(void) {
                ISC_LIST_DEQUEUE(server_list, ps, link);
                isc_mem_free(mctx, ps);
        }
-
 }
 
-/* 
+/*
  * This works on the global server list, instead of on a per-lookup
  * server list, since the change is persistent.
  */
@@ -773,53 +808,65 @@ static void
 setsrv(char *opt) {
        dig_server_t *srv;
 
+       if (opt == NULL) {
+               return;
+       }
        flush_server_list();
        srv=isc_mem_allocate(mctx, sizeof(struct dig_server));
        if (srv == NULL)
                fatal("Memory allocation failure.");
-       strncpy(srv->servername, opt, MXNAME-1);
+       safecpy(srv->servername, opt, MXNAME-1);
        ISC_LIST_APPEND(server_list, srv, link);
 }
 
 static void
 get_next_command(void) {
-       char input[COMMSIZE];
+       char *buf;
        char *ptr, *arg;
+       char *input;
 
+       buf = isc_mem_allocate(mctx, COMMSIZE);
+       if (buf == NULL)
+               fatal("Memory allocation failure.");
        fputs("> ", stderr);
-       ptr = fgets(input, COMMSIZE, stdin);
+       is_blocking = ISC_TRUE;
+       ptr = fgets(buf, COMMSIZE, stdin);
+       is_blocking = ISC_FALSE;
        if (ptr == NULL) {
                in_use = ISC_FALSE;
-               return;
+               goto cleanup;
        }
-       ptr = strtok(input, " \t\r\n");
+       input = buf;
+       ptr = next_token(&input, " \t\r\n");
        if (ptr == NULL)
-               return;
-       arg = strtok(NULL, " \t\r\n");
+               goto cleanup;
+       arg = next_token(&input, " \t\r\n");
        if ((strcasecmp(ptr, "set") == 0) &&
            (arg != NULL))
                setoption(arg);
        else if ((strcasecmp(ptr, "server") == 0) ||
                 (strcasecmp(ptr, "lserver") == 0)) {
-               printf("Server:\t%s\n", arg); 
                setsrv(arg);
+               show_settings(ISC_TRUE, ISC_TRUE);
        } else if (strcasecmp(ptr, "exit") == 0) {
                in_use = ISC_FALSE;
-               return;
+               goto cleanup;
        } else if (strcasecmp(ptr, "help") == 0 ||
                   strcasecmp(ptr, "?") == 0)
        {
                printf("The '%s' command is not yet implemented.\n", ptr);
-               return;
+               goto cleanup;
        } else if (strcasecmp(ptr, "finger") == 0 ||
                   strcasecmp(ptr, "root") == 0 ||
                   strcasecmp(ptr, "ls") == 0 ||
                   strcasecmp(ptr, "view") == 0)
        {
                printf("The '%s' command is not implemented.\n", ptr);
-               return;
-       } else 
+               goto cleanup;
+       } else
                addlookup(ptr);
+ cleanup:
+       isc_mem_free(mctx, buf);
 }
 
 static void
@@ -828,7 +875,7 @@ parse_args(int argc, char **argv) {
        isc_boolean_t have_lookup = ISC_FALSE;
 
        for (argc--, argv++; argc > 0; argc--, argv++) {
-               debug ("main parsing %s", argv[0]);
+               debug("main parsing %s", argv[0]);
                if (argv[0][0] == '-') {
                        if ((argv[0][1] == 'h') &&
                            (argv[0][2] == 0)) {
@@ -873,8 +920,6 @@ flush_lookup_list(void) {
                        if (ISC_LINK_LINKED(&q->lengthbuf, link))
                                ISC_LIST_DEQUEUE(q->lengthlist, &q->lengthbuf,
                                                 link);
-                       INSIST(q->recvspace != NULL);
-                       isc_mempool_put(commctx, q->recvspace);
                        isc_buffer_invalidate(&q->recvbuf);
                        isc_buffer_invalidate(&q->lengthbuf);
                        qp = q;
@@ -882,20 +927,16 @@ flush_lookup_list(void) {
                        ISC_LIST_DEQUEUE(l->q, qp, link);
                        isc_mem_free(mctx, qp);
                }
-               if (l->use_my_server_list) {
-                       s = ISC_LIST_HEAD(l->my_server_list);
-                       while (s != NULL) {
-                               sp = s;
-                               s = ISC_LIST_NEXT(s, link);
-                               ISC_LIST_DEQUEUE(l->my_server_list, sp, link);
-                               isc_mem_free(mctx, sp);
+               s = ISC_LIST_HEAD(l->my_server_list);
+               while (s != NULL) {
+                       sp = s;
+                       s = ISC_LIST_NEXT(s, link);
+                       ISC_LIST_DEQUEUE(l->my_server_list, sp, link);
+                       isc_mem_free(mctx, sp);
 
-                       }
                }
                if (l->sendmsg != NULL)
                        dns_message_destroy(&l->sendmsg);
-               if (l->sendspace != NULL)
-                       isc_mempool_put(commctx, l->sendspace);
                if (l->timer != NULL)
                        isc_timer_detach(&l->timer);
                lp = l;
@@ -903,7 +944,22 @@ flush_lookup_list(void) {
                ISC_LIST_DEQUEUE(lookup_list, lp, link);
                isc_mem_free(mctx, lp);
        }
-}      
+}
+
+static void
+getinput(isc_task_t *task, isc_event_t *event) {
+       UNUSED(task);
+       if (global_event == NULL)
+               global_event = event;
+       while (in_use) {
+               get_next_command();
+               if (ISC_LIST_HEAD(lookup_list) != NULL) {
+                       start_lookup();
+                       return;
+               }
+       }
+       isc_app_shutdown();
+}
 
 int
 main(int argc, char **argv) {
@@ -913,68 +969,38 @@ main(int argc, char **argv) {
        ISC_LIST_INIT(server_list);
        ISC_LIST_INIT(search_list);
 
+       result = isc_app_start();
+       check_result(result, "isc_app_start");
+
        setup_libs();
        progname = argv[0];
-       result = isc_mutex_init(&lock);
-       check_result(result, "isc_mutex_init");
-       result = isc_condition_init(&cond);
-       check_result(result, "isc_condition_init");
-       result = isc_mutex_trylock(&lock);
-       check_result(result, "isc_mutex_trylock");
 
        parse_args(argc, argv);
 
        if (deprecation_msg) {
-               puts (
+               fputs(
 "Note:  nslookup is deprecated and may be removed from future releases.\n"
 "Consider using the `dig' or `host' programs instead.  Run nslookup with\n"
-"the `-sil[ent]' option to prevent this message from appearing.\n");
+"the `-sil[ent]' option to prevent this message from appearing.\n", stderr);
        }
        setup_system();
 
-       if (in_use) {
-               busy = ISC_TRUE;
-               start_lookup();
-               while (busy) {
-                       result = isc_condition_wait(&cond, &lock);
-                       check_result(result, "isc_condition_wait");
-               }
-               flush_lookup_list();
-               in_use = ISC_FALSE;
-       } else {
-               show_settings(ISC_FALSE);
-               in_use = ISC_TRUE;
-       }
+       if (in_use)
+               result = isc_app_onrun(mctx, global_task, onrun_callback,
+                                      NULL);
+       else
+               result = isc_app_onrun(mctx, global_task, getinput, NULL);
+       check_result(result, "isc_app_onrun");
+       in_use = ISC_TF(!in_use);
 
-       while (in_use) {
-               get_next_command();
-               if (ISC_LIST_HEAD(lookup_list) != NULL) {
-                       busy = ISC_TRUE;
-                       start_lookup();
-                       while (busy) {
-                               result = isc_condition_wait(&cond, &lock);
-                               check_result(result, "isc_condition_wait");
-                       }
-                       debug ("out of the condition wait");
-                       flush_lookup_list();
-               }
-       }
+       (void)isc_app_run();
 
-       puts ("");
-       debug ("done, and starting to shut down");
-       free_lists();
-       isc_mutex_destroy(&lock);
-       isc_condition_destroy(&cond);
-       if (taskmgr != NULL) {
-               debug ("freeing taskmgr");
-               isc_taskmgr_destroy(&taskmgr);
-        }
-       if (isc_mem_debugging)
-               isc_mem_stats(mctx, stderr);
+       puts("");
+       debug("done, and starting to shut down");
+       if (global_event != NULL)
+               isc_event_free(&global_event);
+       destroy_libs();
        isc_app_finish();
-       if (mctx != NULL)
-               isc_mem_destroy(&mctx); 
-       
+
        return (0);
 }
-