]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Split off the generic dns query functionality from the byaddr module into
authorBrian Wellington <source@isc.org>
Tue, 17 Oct 2000 01:57:42 +0000 (01:57 +0000)
committerBrian Wellington <source@isc.org>
Tue, 17 Oct 2000 01:57:42 +0000 (01:57 +0000)
the lookup module, and make byaddr use lookup.

lib/dns/Makefile.in
lib/dns/byaddr.c
lib/dns/include/dns/lookup.h [new file with mode: 0644]
lib/dns/lookup.c [new file with mode: 0644]

index acb63575e5924e16bce4dfe3723d5a2304d799ff..b25425ffefe59b8b9b5abf3aba973989e90dbfce 100644 (file)
@@ -13,7 +13,7 @@
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-# $Id: Makefile.in,v 1.108 2000/10/11 17:44:09 mws Exp $
+# $Id: Makefile.in,v 1.109 2000/10/17 01:57:40 bwelling Exp $
 
 srcdir =       @srcdir@
 VPATH =                @srcdir@
@@ -97,7 +97,7 @@ OBJS =                a6.@O@ acl.@O@ aclconf.@O@ adb.@O@ byaddr.@O@ \
                cache.@O@ callbacks.@O@ compress.@O@ \
                db.@O@ dbiterator.@O@ dbtable.@O@ dispatch.@O@ dnssec.@O@ \
                forward.@O@ journal.@O@ keytable.@O@ lib.@O@ log.@O@ \
-               master.@O@ masterdump.@O@ message.@O@ \
+               lookup.@O@ master.@O@ masterdump.@O@ message.@O@ \
                name.@O@ ncache.@O@ nxt.@O@ opt.@O@ peer.@O@ \
                rbt.@O@ rbtdb.@O@ rbtdb64.@O@ rdata.@O@ rdatalist.@O@ \
                rdataset.@O@ rdatasetiter.@O@ rdataslab.@O@ request.@O@ \
@@ -118,7 +118,7 @@ SRCS =              a6.c acl.c aclconf.c adb.c byaddr.c \
                cache.c callbacks.c compress.c \
                db.c dbiterator.c dbtable.c dispatch.c dnssec.c \
                forward.c journal.c keytable.c lib.c log.c \
-               master.c masterdump.c message.c \
+               lookup.c master.c masterdump.c message.c \
                name.c ncache.c nxt.c opt.c peer.c \
                rbt.c rbtdb.c rbtdb64.c rdata.c rdatalist.c \
                rdataset.c rdatasetiter.c rdataslab.c request.c \
index 4a6daf41862b8515ddedb06550ac7378b39236dc..2c277f8ef37b1ef3ea3d1eb7cc3c0f17df37f022 100644 (file)
@@ -15,7 +15,7 @@
  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: byaddr.c,v 1.22 2000/10/07 00:09:19 bwelling Exp $ */
+/* $Id: byaddr.c,v 1.23 2000/10/17 01:57:41 bwelling Exp $ */
 
 #include <config.h>
 
@@ -28,6 +28,7 @@
 #include <dns/byaddr.h>
 #include <dns/db.h>
 #include <dns/events.h>
+#include <dns/lookup.h>
 #include <dns/rdata.h>
 #include <dns/rdataset.h>
 #include <dns/rdatastruct.h>
@@ -47,13 +48,10 @@ struct dns_byaddr {
        dns_fixedname_t         name;
        /* Locked by lock. */
        unsigned int            options;
+       dns_lookup_t *          lookup;
        isc_task_t *            task;
-       dns_view_t *            view;
        dns_byaddrevent_t *     event;
-       dns_fetch_t *           fetch;
-       unsigned int            restarts;
        isc_boolean_t           canceled;
-       dns_rdataset_t          rdataset;
 };
 
 #define BYADDR_MAGIC                   0x42794164U     /* ByAd. */
@@ -62,8 +60,6 @@ struct dns_byaddr {
 
 #define MAX_RESTARTS 16
 
-static void byaddr_find(dns_byaddr_t *byaddr, dns_fetchevent_t *event);
-
 static char hex_digits[] = {
        '0', '1', '2', '3', '4', '5', '6', '7',
        '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
@@ -129,7 +125,7 @@ dns_byaddr_createptrname(isc_netaddr_t *address, isc_boolean_t nibble,
 }
 
 static inline isc_result_t
-copy_ptr_targets(dns_byaddr_t *byaddr) {
+copy_ptr_targets(dns_byaddr_t *byaddr, dns_rdataset_t *rdataset) {
        isc_result_t result;
        dns_name_t *name;
        dns_rdata_t rdata;
@@ -138,10 +134,10 @@ copy_ptr_targets(dns_byaddr_t *byaddr) {
         * The caller must be holding the byaddr's lock.
         */
 
-       result = dns_rdataset_first(&byaddr->rdataset);
+       result = dns_rdataset_first(rdataset);
        while (result == ISC_R_SUCCESS) {
                dns_rdata_ptr_t ptr;
-               dns_rdataset_current(&byaddr->rdataset, &rdata);
+               dns_rdataset_current(rdataset, &rdata);
                result = dns_rdata_tostruct(&rdata, &ptr, NULL);
                if (result != ISC_R_SUCCESS)
                        return (result);
@@ -158,7 +154,7 @@ copy_ptr_targets(dns_byaddr_t *byaddr) {
                        return (ISC_R_NOMEMORY);
                }
                ISC_LIST_APPEND(byaddr->event->names, name, link);
-               result = dns_rdataset_next(&byaddr->rdataset);
+               result = dns_rdataset_next(rdataset);
        }
        if (result == ISC_R_NOMORE)
                result = ISC_R_SUCCESS;
@@ -167,196 +163,22 @@ copy_ptr_targets(dns_byaddr_t *byaddr) {
 }
 
 static void
-fetch_done(isc_task_t *task, isc_event_t *event) {
+lookup_done(isc_task_t *task, isc_event_t *event) {
        dns_byaddr_t *byaddr = event->ev_arg;
-       dns_fetchevent_t *fevent;
+       dns_lookupevent_t *levent;
+       isc_result_t result;
 
        UNUSED(task);
-       REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE);
+       REQUIRE(event->ev_type == DNS_EVENT_LOOKUPDONE);
        REQUIRE(VALID_BYADDR(byaddr));
        REQUIRE(byaddr->task == task);
-       fevent = (dns_fetchevent_t *)event;
-       REQUIRE(fevent->fetch == byaddr->fetch);
-
-       byaddr_find(byaddr, fevent);
-}
-
-static inline isc_result_t
-start_fetch(dns_byaddr_t *byaddr) {
-       isc_result_t result;
-
-       /*
-        * The caller must be holding the byaddr's lock.
-        */
-
-       REQUIRE(byaddr->fetch == NULL);
-
-       result = dns_resolver_createfetch(byaddr->view->resolver,
-                                         dns_fixedname_name(&byaddr->name),
-                                         dns_rdatatype_ptr,
-                                         NULL, NULL, NULL, 0,
-                                         byaddr->task, fetch_done, byaddr,
-                                         &byaddr->rdataset, NULL,
-                                         &byaddr->fetch);
-
-       return (result);
-}
-
-static void
-byaddr_find(dns_byaddr_t *byaddr, dns_fetchevent_t *event) {
-       isc_result_t result;
-       isc_boolean_t want_restart;
-       isc_boolean_t send_event = ISC_FALSE;
-       isc_event_t *ievent;
-       dns_name_t *name, *fname, *prefix;
-       dns_fixedname_t foundname, fixed;
-       dns_rdata_t rdata;
-       unsigned int nlabels, nbits;
-       int order;
-       dns_namereln_t namereln;
-       dns_rdata_cname_t cname;
-       dns_rdata_dname_t dname;
-
-       REQUIRE(VALID_BYADDR(byaddr));
+       levent = (dns_lookupevent_t *)event;
 
-       LOCK(&byaddr->lock);
-
-       result = ISC_R_SUCCESS;
-       name = dns_fixedname_name(&byaddr->name);
-
-       do {
-               byaddr->restarts++;
-               want_restart = ISC_FALSE;
-
-               if (event == NULL && !byaddr->canceled) {
-                       dns_fixedname_init(&foundname);
-                       fname = dns_fixedname_name(&foundname);
-                       INSIST(!dns_rdataset_isassociated(&byaddr->rdataset));
-                       result = dns_view_find(byaddr->view, name,
-                                              dns_rdatatype_ptr, 0, 0,
-                                              ISC_FALSE, fname,
-                                              &byaddr->rdataset, NULL);
-                       if (result == ISC_R_NOTFOUND) {
-                               /*
-                                * We don't know anything about the name.
-                                * Launch a fetch.
-                                */
-                               result = start_fetch(byaddr);
-                               if (result != ISC_R_SUCCESS)
-                                       send_event = ISC_TRUE;
-                               goto done;
-                       }
-               } else {
-                       result = event->result;
-                       fname = dns_fixedname_name(&event->foundname);
-                       dns_resolver_destroyfetch(&byaddr->fetch);
-                       INSIST(event->rdataset == &byaddr->rdataset);
-                       INSIST(event->sigrdataset == NULL);
-                       /*
-                        * Detach (if necessary) from things we know we
-                        * don't care about.
-                        */
-                       if (event->node != NULL)
-                               dns_db_detachnode(event->db, &event->node);
-                       if (event->db != NULL)
-                               dns_db_detach(&event->db);
-               }
-
-               /*
-                * If we've been canceled, forget about the result.
-                */
-               if (byaddr->canceled)
-                       result = ISC_R_CANCELED;
-
-               switch (result) {
-               case ISC_R_SUCCESS:
-                       result = copy_ptr_targets(byaddr);
-                       send_event = ISC_TRUE;
-                       break;
-               case DNS_R_CNAME:
-                       /*
-                        * Copy the CNAME's target into the byaddr's
-                        * query name and start over.
-                        */
-                       result = dns_rdataset_first(&byaddr->rdataset);
-                       if (result != ISC_R_SUCCESS)
-                               break;
-                       dns_rdataset_current(&byaddr->rdataset, &rdata);
-                       result = dns_rdata_tostruct(&rdata, &cname, NULL);
-                       if (result != ISC_R_SUCCESS)
-                               break;
-                       result = dns_name_concatenate(&cname.cname, NULL, name,
-                                                     NULL);
-                       dns_rdata_freestruct(&cname);
-                       if (result == ISC_R_SUCCESS)
-                               want_restart = ISC_TRUE;
-                       break;
-               case DNS_R_DNAME:
-                       namereln = dns_name_fullcompare(name, fname, &order,
-                                                       &nlabels, &nbits);
-                       INSIST(namereln == dns_namereln_subdomain);
-                       /*
-                        * Get the target name of the DNAME.
-                        */
-                       result = dns_rdataset_first(&byaddr->rdataset);
-                       if (result != ISC_R_SUCCESS)
-                               break;
-                       dns_rdataset_current(&byaddr->rdataset, &rdata);
-                       result = dns_rdata_tostruct(&rdata, &dname, NULL);
-                       if (result != ISC_R_SUCCESS)
-                               break;
-                       /*
-                        * Construct the new query name and start over.
-                        */
-                       dns_fixedname_init(&fixed);
-                       prefix = dns_fixedname_name(&fixed);
-                       result = dns_name_split(name, nlabels, nbits, prefix,
-                                               NULL);
-                       if (result != ISC_R_SUCCESS) {
-                               dns_rdata_freestruct(&dname);
-                               break;
-                       }
-                       result = dns_name_concatenate(prefix, &dname.dname,
-                                                     name, NULL);
-                       dns_rdata_freestruct(&dname);
-                       if (result == ISC_R_SUCCESS)
-                               want_restart = ISC_TRUE;
-                       break;
-               default:
-                       send_event = ISC_TRUE;
-               }
-
-       done:
-               if (dns_rdataset_isassociated(&byaddr->rdataset))
-                       dns_rdataset_disassociate(&byaddr->rdataset);
-
-               if (event != NULL) {
-                       ievent = (isc_event_t *)event;
-                       isc_event_free(&ievent);
-                       event = NULL;
-               }
-
-               /*
-                * Limit the number of restarts.
-                */
-               if (want_restart && byaddr->restarts == MAX_RESTARTS) {
-                       want_restart = ISC_FALSE;
-                       result = ISC_R_QUOTA;
-                       send_event = ISC_TRUE;
-               }
-
-       } while (want_restart);
-
-       if (send_event) {
-               byaddr->event->result = result;
-               byaddr->event->ev_sender = byaddr;
-               ievent = (isc_event_t *)byaddr->event;
-               byaddr->event = NULL;
-               isc_task_sendanddetach(&byaddr->task, &ievent);
-               dns_view_detach(&byaddr->view);
-       }
-
-       UNLOCK(&byaddr->lock);
+       if (levent->result == ISC_R_SUCCESS)
+               result = copy_ptr_targets(byaddr, levent->rdataset);
+       byaddr->event->result = levent->result;
+       isc_event_free(&event);
+       isc_task_sendanddetach(&byaddr->task, (isc_event_t **)&byaddr->event);
 }
 
 static void
@@ -420,18 +242,18 @@ dns_byaddr_create(isc_mem_t *mctx, isc_netaddr_t *address, dns_view_t *view,
        if (result != ISC_R_SUCCESS)
                goto cleanup_lock;
 
-       byaddr->view = NULL;
-       dns_view_attach(view, &byaddr->view);
-       byaddr->fetch = NULL;
-       byaddr->restarts = 0;
+       byaddr->lookup = NULL;
+       result = dns_lookup_create(mctx, dns_fixedname_name(&byaddr->name),
+                                  dns_rdatatype_ptr, view, 0, task,
+                                  lookup_done, byaddr, &byaddr->lookup);
+       if (result != ISC_R_SUCCESS)
+               goto cleanup_lock;
+
        byaddr->canceled = ISC_FALSE;
-       dns_rdataset_init(&byaddr->rdataset);
        byaddr->magic = BYADDR_MAGIC;
 
        *byaddrp = byaddr;
 
-       byaddr_find(byaddr, NULL);
-
        return (ISC_R_SUCCESS);
 
  cleanup_lock:
@@ -458,10 +280,8 @@ dns_byaddr_cancel(dns_byaddr_t *byaddr) {
 
        if (!byaddr->canceled) {
                byaddr->canceled = ISC_TRUE;
-               if (byaddr->fetch != NULL) {
-                       INSIST(byaddr->view != NULL);
-                       dns_resolver_cancelfetch(byaddr->fetch);
-               }
+               if (byaddr->lookup != NULL)
+                       dns_lookup_cancel(byaddr->lookup);
        }
 
        UNLOCK(&byaddr->lock);
@@ -476,7 +296,7 @@ dns_byaddr_destroy(dns_byaddr_t **byaddrp) {
        REQUIRE(VALID_BYADDR(byaddr));
        REQUIRE(byaddr->event == NULL);
        REQUIRE(byaddr->task == NULL);
-       REQUIRE(byaddr->view == NULL);
+       dns_lookup_destroy(&byaddr->lookup);
 
        DESTROYLOCK(&byaddr->lock);
        byaddr->magic = 0;
diff --git a/lib/dns/include/dns/lookup.h b/lib/dns/include/dns/lookup.h
new file mode 100644 (file)
index 0000000..969fb9a
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * 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: lookup.h,v 1.1 2000/10/17 01:57:42 bwelling Exp $ */
+
+#ifndef DNS_LOOKUP_H
+#define DNS_LOOKUP_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*
+ * DNS Lookup
+ *
+ * The lookup module performs simple dns lookups.
+ *
+ * MP:
+ *     The module ensures appropriate synchronization of data structures it
+ *     creates and manipulates.
+ *
+ * Reliability:
+ *     No anticipated impact.
+ *
+ * Resources:
+ *     <TBS>
+ *
+ * Security:
+ *     No anticipated impact.
+ *
+ * Standards:
+ *     RFCs:   1034, 1035, 2181, <TBS>
+ *     Drafts: <TBS>
+ */
+
+#include <isc/lang.h>
+#include <isc/event.h>
+
+#include <dns/types.h>
+
+ISC_LANG_BEGINDECLS
+
+/*
+ * A 'dns_lookupevent_t' is returned when a lookup completes.
+ * The sender field will be set to the lookup that completed.  If 'result'
+ * is ISC_R_SUCCESS, then 'names' will contain a list of names associated
+ * with the address.  The recipient of the event must not change the list
+ * and must not refer to any of the name data after the event is freed.
+ */
+typedef struct dns_lookupevent {
+       ISC_EVENT_COMMON(struct dns_lookupevent);
+       isc_result_t                    result;
+       dns_name_t                      *name;
+       dns_rdataset_t                  *rdataset;
+} dns_lookupevent_t;
+
+isc_result_t
+dns_lookup_create(isc_mem_t *mctx, dns_name_t *name, dns_rdatatype_t type,
+                 dns_view_t *view, unsigned int options, isc_task_t *task,
+                 isc_taskaction_t action, void *arg, dns_lookup_t **lookupp);
+/*
+ * Finds the rrsets matching 'name' and 'type'.
+ *
+ * Requires:
+ *
+ *     'mctx' is a valid mctx.
+ *
+ *     'name' is a valid name.
+ *
+ *     'view' is a valid view which has a resolver.
+ *
+ *     'task' is a valid task.
+ *
+ *     lookupp != NULL && *lookupp == NULL
+ *
+ * Returns:
+ *
+ *     ISC_R_SUCCESS
+ *     ISC_R_NOMEMORY
+ *
+ *     Any resolver-related error (e.g. ISC_R_SHUTTINGDOWN) may also be
+ *     returned.
+ */
+
+void
+dns_lookup_cancel(dns_lookup_t *lookup);
+/*
+ * Cancel 'lookup'.
+ *
+ * Notes:
+ *
+ *     If 'lookup' has not completed, post its LOOKUPDONE event with a
+ *     result code of ISC_R_CANCELED.
+ *
+ * Requires:
+ *
+ *     'lookup' is a valid lookup.
+ */
+
+void
+dns_lookup_destroy(dns_lookup_t **lookupp);
+/*
+ * Destroy 'lookup'.
+ *
+ * Requires:
+ *
+ *     '*lookupp' is a valid lookup.
+ *
+ *     The caller has received the LOOKUPDONE event (either because the
+ *     lookup completed or because dns_lookup_cancel() was called).
+ *
+ * Ensures:
+ *
+ *     *lookupp == NULL.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DNS_LOOKUP_H */
diff --git a/lib/dns/lookup.c b/lib/dns/lookup.c
new file mode 100644 (file)
index 0000000..575ab0e
--- /dev/null
@@ -0,0 +1,425 @@
+/*
+ * 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: lookup.c,v 1.1 2000/10/17 01:57:42 bwelling Exp $ */
+
+#include <config.h>
+
+#include <isc/mem.h>
+#include <isc/netaddr.h>
+#include <isc/string.h>                /* Required for HP/UX (and others?) */
+#include <isc/task.h>
+#include <isc/util.h>
+
+#include <dns/db.h>
+#include <dns/events.h>
+#include <dns/lookup.h>
+#include <dns/rdata.h>
+#include <dns/rdataset.h>
+#include <dns/rdatastruct.h>
+#include <dns/resolver.h>
+#include <dns/result.h>
+#include <dns/view.h>
+
+struct dns_lookup {
+       /* Unlocked. */
+       unsigned int            magic;
+       isc_mem_t *             mctx;
+       isc_mutex_t             lock;
+       dns_rdatatype_t         type;
+       dns_fixedname_t         name;
+       /* Locked by lock. */
+       unsigned int            options;
+       isc_task_t *            task;
+       dns_view_t *            view;
+       dns_lookupevent_t *     event;
+       dns_fetch_t *           fetch;
+       unsigned int            restarts;
+       isc_boolean_t           canceled;
+       dns_rdataset_t          rdataset;
+};
+
+#define LOOKUP_MAGIC                   ISC_MAGIC('l', 'o', 'o', 'k')
+#define VALID_LOOKUP(l)                        ISC_MAGIC_VALID((l), LOOKUP_MAGIC)
+
+#define MAX_RESTARTS 16
+
+static void lookup_find(dns_lookup_t *lookup, dns_fetchevent_t *event);
+
+static void
+fetch_done(isc_task_t *task, isc_event_t *event) {
+       dns_lookup_t *lookup = event->ev_arg;
+       dns_fetchevent_t *fevent;
+
+       UNUSED(task);
+       REQUIRE(event->ev_type == DNS_EVENT_FETCHDONE);
+       REQUIRE(VALID_LOOKUP(lookup));
+       REQUIRE(lookup->task == task);
+       fevent = (dns_fetchevent_t *)event;
+       REQUIRE(fevent->fetch == lookup->fetch);
+
+       lookup_find(lookup, fevent);
+}
+
+static inline isc_result_t
+start_fetch(dns_lookup_t *lookup) {
+       isc_result_t result;
+
+       /*
+        * The caller must be holding the lookup's lock.
+        */
+
+       REQUIRE(lookup->fetch == NULL);
+
+       result = dns_resolver_createfetch(lookup->view->resolver,
+                                         dns_fixedname_name(&lookup->name),
+                                         lookup->type,
+                                         NULL, NULL, NULL, 0,
+                                         lookup->task, fetch_done, lookup,
+                                         &lookup->rdataset, NULL,
+                                         &lookup->fetch);
+
+       return (result);
+}
+
+static isc_result_t
+build_event(dns_lookup_t *lookup) {
+       dns_name_t *name = NULL;
+       dns_rdataset_t *rdataset = NULL;
+       isc_result_t result;
+
+       name = isc_mem_get(lookup->mctx, sizeof(dns_name_t));
+       if (name == NULL) {
+               result = ISC_R_NOMEMORY;
+               goto fail;
+       }
+       dns_name_init(name, NULL);
+       result = dns_name_dup(dns_fixedname_name(&lookup->name),
+                             lookup->mctx, name);
+       if (result != ISC_R_SUCCESS)
+               goto fail;
+
+       rdataset = isc_mem_get(lookup->mctx, sizeof(dns_rdataset_t));
+       if (rdataset == NULL) {
+               result = ISC_R_NOMEMORY;
+               goto fail;
+       }
+       dns_rdataset_init(rdataset);
+       dns_rdataset_clone(&lookup->rdataset, rdataset);
+
+       lookup->event->name = name;
+       lookup->event->rdataset = rdataset;
+
+       return (ISC_R_SUCCESS);
+
+ fail:
+       if (name != NULL) {
+               if (dns_name_dynamic(name))
+                       dns_name_free(name, lookup->mctx);
+               isc_mem_put(lookup->mctx, name, sizeof(dns_name_t));
+       }
+       if (rdataset != NULL) {
+               if (dns_rdataset_isassociated(rdataset))
+                       dns_rdataset_disassociate(rdataset);
+               isc_mem_put(lookup->mctx, rdataset, sizeof(dns_rdataset_t));
+       }
+       return (result);
+}
+
+static void
+lookup_find(dns_lookup_t *lookup, dns_fetchevent_t *event) {
+       isc_result_t result;
+       isc_boolean_t want_restart;
+       isc_boolean_t send_event = ISC_FALSE;
+       dns_name_t *name, *fname, *prefix;
+       dns_fixedname_t foundname, fixed;
+       dns_rdata_t rdata;
+       unsigned int nlabels, nbits;
+       int order;
+       dns_namereln_t namereln;
+       dns_rdata_cname_t cname;
+       dns_rdata_dname_t dname;
+
+       REQUIRE(VALID_LOOKUP(lookup));
+
+       LOCK(&lookup->lock);
+
+       result = ISC_R_SUCCESS;
+       name = dns_fixedname_name(&lookup->name);
+
+       do {
+               lookup->restarts++;
+               want_restart = ISC_FALSE;
+
+               if (event == NULL && !lookup->canceled) {
+                       dns_fixedname_init(&foundname);
+                       fname = dns_fixedname_name(&foundname);
+                       INSIST(!dns_rdataset_isassociated(&lookup->rdataset));
+                       result = dns_view_find(lookup->view, name,
+                                              lookup->type, 0, 0,
+                                              ISC_FALSE, fname,
+                                              &lookup->rdataset, NULL);
+                       if (result == ISC_R_NOTFOUND) {
+                               /*
+                                * We don't know anything about the name.
+                                * Launch a fetch.
+                                */
+                               result = start_fetch(lookup);
+                               if (result != ISC_R_SUCCESS)
+                                       send_event = ISC_TRUE;
+                               goto done;
+                       }
+               } else {
+                       result = event->result;
+                       fname = dns_fixedname_name(&event->foundname);
+                       dns_resolver_destroyfetch(&lookup->fetch);
+                       INSIST(event->rdataset == &lookup->rdataset);
+                       INSIST(event->sigrdataset == NULL);
+                       /*
+                        * Detach (if necessary) from things we know we
+                        * don't care about.
+                        */
+                       if (event->node != NULL)
+                               dns_db_detachnode(event->db, &event->node);
+                       if (event->db != NULL)
+                               dns_db_detach(&event->db);
+               }
+
+               /*
+                * If we've been canceled, forget about the result.
+                */
+               if (lookup->canceled)
+                       result = ISC_R_CANCELED;
+
+               switch (result) {
+               case ISC_R_SUCCESS:
+                       result = build_event(lookup);
+                       send_event = ISC_TRUE;
+                       break;
+               case DNS_R_CNAME:
+                       /*
+                        * Copy the CNAME's target into the lookup's
+                        * query name and start over.
+                        */
+                       result = dns_rdataset_first(&lookup->rdataset);
+                       if (result != ISC_R_SUCCESS)
+                               break;
+                       dns_rdataset_current(&lookup->rdataset, &rdata);
+                       result = dns_rdata_tostruct(&rdata, &cname, NULL);
+                       if (result != ISC_R_SUCCESS)
+                               break;
+                       result = dns_name_concatenate(&cname.cname, NULL, name,
+                                                     NULL);
+                       dns_rdata_freestruct(&cname);
+                       if (result == ISC_R_SUCCESS)
+                               want_restart = ISC_TRUE;
+                       break;
+               case DNS_R_DNAME:
+                       namereln = dns_name_fullcompare(name, fname, &order,
+                                                       &nlabels, &nbits);
+                       INSIST(namereln == dns_namereln_subdomain);
+                       /*
+                        * Get the target name of the DNAME.
+                        */
+                       result = dns_rdataset_first(&lookup->rdataset);
+                       if (result != ISC_R_SUCCESS)
+                               break;
+                       dns_rdataset_current(&lookup->rdataset, &rdata);
+                       result = dns_rdata_tostruct(&rdata, &dname, NULL);
+                       if (result != ISC_R_SUCCESS)
+                               break;
+                       /*
+                        * Construct the new query name and start over.
+                        */
+                       dns_fixedname_init(&fixed);
+                       prefix = dns_fixedname_name(&fixed);
+                       result = dns_name_split(name, nlabels, nbits, prefix,
+                                               NULL);
+                       if (result != ISC_R_SUCCESS) {
+                               dns_rdata_freestruct(&dname);
+                               break;
+                       }
+                       result = dns_name_concatenate(prefix, &dname.dname,
+                                                     name, NULL);
+                       dns_rdata_freestruct(&dname);
+                       if (result == ISC_R_SUCCESS)
+                               want_restart = ISC_TRUE;
+                       break;
+               default:
+                       send_event = ISC_TRUE;
+               }
+
+       done:
+               if (dns_rdataset_isassociated(&lookup->rdataset))
+                       dns_rdataset_disassociate(&lookup->rdataset);
+
+               if (event != NULL)
+                       isc_event_free((isc_event_t **)&event);
+
+               /*
+                * Limit the number of restarts.
+                */
+               if (want_restart && lookup->restarts == MAX_RESTARTS) {
+                       want_restart = ISC_FALSE;
+                       result = ISC_R_QUOTA;
+                       send_event = ISC_TRUE;
+               }
+
+       } while (want_restart);
+
+       if (send_event) {
+               lookup->event->result = result;
+               lookup->event->ev_sender = lookup;
+               isc_task_sendanddetach(&lookup->task,
+                                      (isc_event_t **)&lookup->event);
+               dns_view_detach(&lookup->view);
+       }
+
+       UNLOCK(&lookup->lock);
+}
+
+static void
+levent_destroy(isc_event_t *event) {
+       dns_lookupevent_t *levent;
+       isc_mem_t *mctx;
+                                       
+       REQUIRE(event->ev_type == DNS_EVENT_LOOKUPDONE);
+       mctx = event->ev_destroy_arg;
+       levent = (dns_lookupevent_t *)event;
+
+       if (levent->name != NULL) {
+               if (dns_name_dynamic(levent->name))
+                       dns_name_free(levent->name, mctx);
+               isc_mem_put(mctx, levent->name, sizeof(dns_name_t));
+       }
+       if (levent->rdataset != NULL) {
+               dns_rdataset_disassociate(levent->rdataset);
+               isc_mem_put(mctx, levent->rdataset, sizeof(dns_rdataset_t));
+       }
+       isc_mem_put(mctx, event, event->ev_size);
+}
+
+
+isc_result_t
+dns_lookup_create(isc_mem_t *mctx, dns_name_t *name, dns_rdatatype_t type,
+                 dns_view_t *view, unsigned int options, isc_task_t *task,
+                 isc_taskaction_t action, void *arg, dns_lookup_t **lookupp)
+{
+       isc_result_t result;
+       dns_lookup_t *lookup;
+       isc_event_t *ievent;
+
+       lookup = isc_mem_get(mctx, sizeof *lookup);
+       if (lookup == NULL)
+               return (ISC_R_NOMEMORY);
+       lookup->mctx = mctx;
+       lookup->options = options;
+
+       ievent = isc_event_allocate(mctx, lookup, DNS_EVENT_LOOKUPDONE,
+                                   action, arg, sizeof *lookup->event);
+       if (ievent == NULL) {
+               result = ISC_R_NOMEMORY;
+               goto cleanup_lookup;
+       }
+       lookup->event = (dns_lookupevent_t *)ievent;
+       lookup->event->ev_destroy = levent_destroy;
+       lookup->event->ev_destroy_arg = mctx;
+       lookup->event->result = ISC_R_FAILURE;
+       lookup->event->name = NULL;
+       lookup->event->rdataset = NULL;
+
+       lookup->task = NULL;
+       isc_task_attach(task, &lookup->task);
+
+       result = isc_mutex_init(&lookup->lock);
+       if (result != ISC_R_SUCCESS)
+               goto cleanup_event;
+
+       dns_fixedname_init(&lookup->name);
+
+       result = dns_name_concatenate(name, NULL,
+                                     dns_fixedname_name(&lookup->name), NULL);
+       if (result != ISC_R_SUCCESS)
+               goto cleanup_lock;
+
+       lookup->type = type;
+       lookup->view = NULL;
+       dns_view_attach(view, &lookup->view);
+       lookup->fetch = NULL;
+       lookup->restarts = 0;
+       lookup->canceled = ISC_FALSE;
+       dns_rdataset_init(&lookup->rdataset);
+       lookup->magic = LOOKUP_MAGIC;
+
+       *lookupp = lookup;
+
+       lookup_find(lookup, NULL);
+
+       return (ISC_R_SUCCESS);
+
+ cleanup_lock:
+       DESTROYLOCK(&lookup->lock);
+
+ cleanup_event:
+       ievent = (isc_event_t *)lookup->event;
+       isc_event_free(&ievent);
+       lookup->event = NULL;
+
+       isc_task_detach(&lookup->task);
+
+ cleanup_lookup:
+       isc_mem_put(mctx, lookup, sizeof *lookup);
+
+       return (result);
+}
+
+void
+dns_lookup_cancel(dns_lookup_t *lookup) {
+       REQUIRE(VALID_LOOKUP(lookup));
+
+       LOCK(&lookup->lock);
+
+       if (!lookup->canceled) {
+               lookup->canceled = ISC_TRUE;
+               if (lookup->fetch != NULL) {
+                       INSIST(lookup->view != NULL);
+                       dns_resolver_cancelfetch(lookup->fetch);
+               }
+       }
+
+       UNLOCK(&lookup->lock);
+}
+
+void
+dns_lookup_destroy(dns_lookup_t **lookupp) {
+       dns_lookup_t *lookup;
+
+       REQUIRE(lookupp != NULL);
+       lookup = *lookupp;
+       REQUIRE(VALID_LOOKUP(lookup));
+       REQUIRE(lookup->event == NULL);
+       REQUIRE(lookup->task == NULL);
+       REQUIRE(lookup->view == NULL);
+       if (dns_rdataset_isassociated(&lookup->rdataset))
+               dns_rdataset_disassociate(&lookup->rdataset);
+
+       DESTROYLOCK(&lookup->lock);
+       lookup->magic = 0;
+       isc_mem_put(lookup->mctx, lookup, sizeof *lookup);
+
+       *lookupp = NULL;
+}