]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
1913. [func] Integrate contibuted DLZ code into named. [RT #11382]
authorMark Andrews <marka@isc.org>
Mon, 5 Sep 2005 00:12:29 +0000 (00:12 +0000)
committerMark Andrews <marka@isc.org>
Mon, 5 Sep 2005 00:12:29 +0000 (00:12 +0000)
52 files changed:
CHANGES
bin/check/check-tool.c
bin/named/Makefile.in
bin/named/main.c
bin/named/query.c
bin/named/server.c
bin/named/xfrout.c
bin/tests/system/conf.sh.in
configure
configure.in
contrib/dlz/bin/dlzbdb/.cvsignore [new file with mode: 0644]
contrib/dlz/bin/dlzbdb/Makefile.in [new file with mode: 0644]
contrib/dlz/bin/dlzbdb/dlzbdb.c [new file with mode: 0644]
contrib/dlz/config.dlz.in [new file with mode: 0644]
contrib/dlz/drivers/.cvsignore [new file with mode: 0644]
contrib/dlz/drivers/dlz_bdb_driver.c [new file with mode: 0644]
contrib/dlz/drivers/dlz_bdbhpt_driver.c [new file with mode: 0644]
contrib/dlz/drivers/dlz_drivers.c [new file with mode: 0644]
contrib/dlz/drivers/dlz_filesystem_driver.c [new file with mode: 0644]
contrib/dlz/drivers/dlz_ldap_driver.c [new file with mode: 0644]
contrib/dlz/drivers/dlz_mysql_driver.c [new file with mode: 0644]
contrib/dlz/drivers/dlz_odbc_driver.c [new file with mode: 0644]
contrib/dlz/drivers/dlz_postgres_driver.c [new file with mode: 0644]
contrib/dlz/drivers/dlz_stub_driver.c [new file with mode: 0644]
contrib/dlz/drivers/include/dlz/dlz_bdb_driver.h [new file with mode: 0644]
contrib/dlz/drivers/include/dlz/dlz_bdbhpt_driver.h [new file with mode: 0644]
contrib/dlz/drivers/include/dlz/dlz_drivers.h [new file with mode: 0644]
contrib/dlz/drivers/include/dlz/dlz_filesystem_driver.h [new file with mode: 0644]
contrib/dlz/drivers/include/dlz/dlz_ldap_driver.h [new file with mode: 0644]
contrib/dlz/drivers/include/dlz/dlz_mysql_driver.h [new file with mode: 0644]
contrib/dlz/drivers/include/dlz/dlz_odbc_driver.h [new file with mode: 0644]
contrib/dlz/drivers/include/dlz/dlz_postgres_driver.h [new file with mode: 0644]
contrib/dlz/drivers/include/dlz/dlz_stub_driver.h [new file with mode: 0644]
contrib/dlz/drivers/include/dlz/sdlz_helper.h [new file with mode: 0644]
contrib/dlz/drivers/rules.in [new file with mode: 0644]
contrib/dlz/drivers/sdlz_helper.c [new file with mode: 0644]
lib/dns/Makefile.in
lib/dns/dlz.c [new file with mode: 0644]
lib/dns/include/dns/dlz.h [new file with mode: 0644]
lib/dns/include/dns/log.h
lib/dns/include/dns/name.h
lib/dns/include/dns/sdlz.h [new file with mode: 0644]
lib/dns/include/dns/types.h
lib/dns/include/dns/view.h
lib/dns/log.c
lib/dns/sdlz.c [new file with mode: 0644]
lib/dns/view.c
lib/dns/win32/libdns.def
lib/isc/unix/dir.c
lib/isccfg/Makefile.in
lib/isccfg/namedconf.c
lib/isccfg/parser.c

diff --git a/CHANGES b/CHANGES
index e550400708c3d162f30fc67dff357de71c4c144f..89f5c6a6d5ff46fe698cec6b3c431e7c6bbe6942 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,5 @@
+1913.  [func]          Integrate contibuted DLZ code into named. [RT #11382]
+
 1912.  [port]          aix: atomic locking for powerpc. [RT #15020]
 
 1911.  [bug]           Update windows socket code. [RT #14965]
index 60dad979d76c6aa986310ce528643b3c37037de2..bbadf135bd3df6f0b5634453318020e6a20b6a5f 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: check-tool.c,v 1.19 2005/08/24 23:53:54 marka Exp $ */
+/* $Id: check-tool.c,v 1.20 2005/09/05 00:10:51 marka Exp $ */
 
 /*! \file */
 
@@ -435,7 +435,7 @@ load_zone(isc_mem_t *mctx, const char *zonename, const char *filename,
                dns_zone_setchecksrv(zone, checksrv);
 
        CHECK(dns_zone_load(zone));
-       if (zonep != NULL){
+       if (zonep != NULL) {
                *zonep = zone;
                zone = NULL;
        }
index 52e9cf8d651bd00c5a27cd06503baf7c74c6540e..37e1b12c49e69144f226a67790569a32d2e92a57 100644 (file)
@@ -13,7 +13,7 @@
 # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 # PERFORMANCE OF THIS SOFTWARE.
 
-# $Id: Makefile.in,v 1.86 2005/01/12 01:56:06 marka Exp $
+# $Id: Makefile.in,v 1.87 2005/09/05 00:10:51 marka Exp $
 
 srcdir =       @srcdir@
 VPATH =                @srcdir@
@@ -31,12 +31,20 @@ DBDRIVER_SRCS =
 DBDRIVER_INCLUDES =
 DBDRIVER_LIBS =
 
+DLZ_DRIVER_DIR =       ${top_srcdir}/contrib/dlz/drivers
+
+DLZDRIVER_OBJS =       @DLZ_DRIVER_OBJS@
+DLZDRIVER_SRCS =       @DLZ_DRIVER_SRCS@
+DLZDRIVER_INCLUDES =   @DLZ_DRIVER_INCLUDES@
+DLZDRIVER_LIBS =       @DLZ_DRIVER_LIBS@
+
 CINCLUDES =    -I${srcdir}/include -I${srcdir}/unix/include \
                ${LWRES_INCLUDES} ${DNS_INCLUDES} ${BIND9_INCLUDES} \
                ${ISCCFG_INCLUDES} ${ISCCC_INCLUDES} ${ISC_INCLUDES} \
-               ${DBDRIVER_INCLUDES}
+               ${DLZDRIVER_INCLUDES} ${DBDRIVER_INCLUDES}
+
+CDEFINES =      @USE_DLZ@
 
-CDEFINES =
 CWARNINGS =
 
 DNSLIBS =      ../../lib/dns/libdns.@A@ @DNS_CRYPTO_LIBS@
@@ -57,7 +65,8 @@ DEPLIBS =     ${LWRESDEPLIBS} ${DNSDEPLIBS} ${BIND9DEPLIBS} \
                ${ISCCFGDEPLIBS} ${ISCCCDEPLIBS} ${ISCDEPLIBS}
 
 LIBS =         ${LWRESLIBS} ${DNSLIBS} ${BIND9LIBS} \
-               ${ISCCFGLIBS} ${ISCCCLIBS} ${ISCLIBS} ${DBDRIVER_LIBS} @LIBS@
+               ${ISCCFGLIBS} ${ISCCCLIBS} ${ISCLIBS} \
+               ${DLZDRIVER_LIBS} ${DBDRIVER_LIBS} @LIBS@
 
 SUBDIRS =      unix
 
@@ -71,7 +80,7 @@ OBJS =                builtin.@O@ client.@O@ config.@O@ control.@O@ \
                zoneconf.@O@ \
                lwaddr.@O@ lwresd.@O@ lwdclient.@O@ lwderror.@O@ lwdgabn.@O@ \
                lwdgnba.@O@ lwdgrbn.@O@ lwdnoop.@O@ lwsearch.@O@ \
-               $(DBDRIVER_OBJS)
+               ${DLZDRIVER_OBJS} ${DBDRIVER_OBJS}
 
 UOBJS =                unix/os.@O@
 
@@ -83,7 +92,7 @@ SRCS =                builtin.c client.c config.c control.c \
                zoneconf.c \
                lwaddr.c lwresd.c lwdclient.c lwderror.c lwdgabn.c \
                lwdgnba.c lwdgrbn.c lwdnoop.c lwsearch.c \
-               $(DBDRIVER_SRCS)
+               ${DLZDRIVER_SRCS} ${DBDRIVER_SRCS}
 
 MANPAGES =     named.8 lwresd.8 named.conf.5
 
@@ -133,3 +142,4 @@ install:: named@EXEEXT@ lwresd@EXEEXT@ installdirs
        ${INSTALL_DATA} ${srcdir}/lwresd.8 ${DESTDIR}${mandir}/man8
        ${INSTALL_DATA} ${srcdir}/named.conf.5 ${DESTDIR}${mandir}/man5
 
+@DLZ_DRIVER_RULES@
index f40a8c6324d3ea0c73c00ed7b0ca349e90a2bf06..2d14bbb68af1439c1a65eae665340edac339bd2e 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: main.c,v 1.147 2005/06/10 07:00:19 marka Exp $ */
+/* $Id: main.c,v 1.148 2005/09/05 00:10:52 marka Exp $ */
 
 /*! \file */
 
  */
 /* #include "xxdb.h" */
 
+/*
+ * Include DLZ drivers if appropriate.
+ */
+#ifdef DLZ
+#include <dlz/dlz_drivers.h>
+#endif
+
 static isc_boolean_t   want_stats = ISC_FALSE;
 static char            program_name[ISC_DIR_NAMEMAX] = "named";
 static char            absolute_conffile[ISC_DIR_PATHMAX];
@@ -675,6 +682,16 @@ setup(void) {
         */
        /* xxdb_init(); */
 
+#ifdef DLZ
+       /*
+        * Registyer any DLZ drivers.
+        */
+       result = dlz_drivers_init();
+       if (result != ISC_R_SUCCESS)
+               ns_main_earlyfatal("dlz_drivers_init() failed: %s",
+                                  isc_result_totext(result));
+#endif
+
        ns_server_create(ns_g_mctx, &ns_g_server);
 }
 
@@ -691,6 +708,13 @@ cleanup(void) {
         */
        /* xxdb_clear(); */
 
+#ifdef DLZ
+       /*
+        * Unregister any DLZ drivers.
+        */
+       dlz_drivers_clear();
+#endif
+
        isc_log_write(ns_g_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_MAIN,
                      ISC_LOG_NOTICE, "exiting");
        ns_log_shutdown();
index 285f7133fad1c210947ef8a5869b0f9d3c166868..8cd4237e8d88600bd037fac8e365cdd2d7d3aae1 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: query.c,v 1.272 2005/08/18 00:57:26 marka Exp $ */
+/* $Id: query.c,v 1.273 2005/09/05 00:10:52 marka Exp $ */
 
 /*! \file */
 
@@ -29,6 +29,9 @@
 #include <dns/adb.h>
 #include <dns/byaddr.h>
 #include <dns/db.h>
+#ifdef DLZ
+#include <dns/dlz.h>
+#endif
 #include <dns/events.h>
 #include <dns/message.h>
 #include <dns/ncache.h>
@@ -508,7 +511,7 @@ ns_query_init(ns_client_t *client) {
        client->query.authdb = NULL;
        client->query.authzone = NULL;
        client->query.authdbset = ISC_FALSE;
-       client->query.isreferral = ISC_FALSE;   
+       client->query.isreferral = ISC_FALSE;
        query_reset(client, ISC_FALSE);
        result = query_newdbversion(client, 3);
        if (result != ISC_R_SUCCESS) {
@@ -577,7 +580,7 @@ query_validatezonedb(ns_client_t *client, dns_name_t *name,
        /*
         * This limits our searching to the zone where the first name
         * (the query target) was looked for.  This prevents following
-        * CNAMES or DNAMES into other zones and prevents returning 
+        * CNAMES or DNAMES into other zones and prevents returning
         * additional data from other zones.
         */
        if (!client->view->additionalfromauth &&
@@ -654,7 +657,7 @@ query_validatezonedb(ns_client_t *client, dns_name_t *name,
                                                      ISC_LOG_DEBUG(3),
                                                      "%s approved", msg);
                                }
-                       } else {
+                       } else {
                                ns_client_aclmsg("query", name, qtype,
                                                 client->view->rdclass,
                                                 msg, sizeof(msg));
@@ -733,7 +736,7 @@ query_getzonedb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype,
        if (result == ISC_R_SUCCESS || result == DNS_R_PARTIALMATCH)
                result = dns_zone_getdb(zone, &db);
 
-       if (result != ISC_R_SUCCESS)            
+       if (result != ISC_R_SUCCESS)
                goto fail;
 
        result = query_validatezonedb(client, name, qtype, options, zone, db,
@@ -801,7 +804,7 @@ query_getcachedb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype,
        if (check_acl) {
                isc_boolean_t log = ISC_TF((options & DNS_GETDB_NOLOG) == 0);
                char msg[NS_CLIENT_ACLMSGSIZE("query (cache)")];
-               
+
                result = ns_client_checkaclsilent(client,
                                                  client->view->queryacl,
                                                  ISC_TRUE);
@@ -867,9 +870,85 @@ query_getdb(ns_client_t *client, dns_name_t *name, dns_rdatatype_t qtype,
 {
        isc_result_t result;
 
+#ifdef DLZ
+       isc_result_t tresult;
+       unsigned int namelabels;
+       unsigned int zonelabels;
+       dns_zone_t *zone = NULL;
+       dns_db_t *tdbp;
+
+       REQUIRE(zonep != NULL && *zonep == NULL);
+
+       tdbp = NULL;
+
+       /* Calculate how many labels are in name. */
+       namelabels = dns_name_countlabels(name);
+       zonelabels = 0;
+
+       /* Try to find name in bind's standard database. */
+       result = query_getzonedb(client, name, qtype, options, &zone,
+                                dbp, versionp);
+
+       /* See how many labels are in the zone's name.    */
+       if (result == ISC_R_SUCCESS && zone != NULL)
+               zonelabels = dns_name_countlabels(dns_zone_getorigin(zone));
+       /*
+        * If # zone labels < # name labels, try to find an even better match
+        * Only try if a DLZ driver is loaded for this view
+        */
+       if (zonelabels < namelabels && client->view->dlzdatabase != NULL) {
+               tresult = dns_dlzfindzone(client->view, name,
+                                         zonelabels, &tdbp);
+                /* If we successful, we found a better match. */
+               if (tresult == ISC_R_SUCCESS) {
+                       /*
+                        * If the previous search returned a zone, detach it.
+                        */
+                       if (zone != NULL)
+                               dns_zone_detach(&zone);
+
+                       /*
+                        * If the previous search returned a database,
+                        * detach it.
+                        */
+                       if (*dbp != NULL)
+                               dns_db_detach(dbp);
+
+                       /*
+                        * If the previous search returned a version, clear it.
+                        */
+                       *versionp = NULL;
+
+                       /*
+                        * Get our database version.
+                        */
+                       dns_db_currentversion(tdbp, versionp);
+
+                       /*
+                        * Be sure to return our database.
+                        */
+                       *dbp = tdbp;
+
+                       /*
+                        * We return a null zone, No stats for DLZ zones.
+                        */
+                       zone = NULL;
+                       result = tresult;
+               }
+       }
+#else
        result = query_getzonedb(client, name, qtype, options,
                                 zonep, dbp, versionp);
+#endif
+
+       /* If successfull, Transfer ownership of zone. */
        if (result == ISC_R_SUCCESS) {
+#ifdef DLZ
+               *zonep = zone;
+#endif
+               /*
+                * If neither attempt above succeeded, return the cache instead
+                */
                *is_zonep = ISC_TRUE;
        } else if (result == ISC_R_NOTFOUND) {
                result = query_getcachedb(client, name, qtype, dbp, options);
@@ -1032,7 +1111,7 @@ query_addadditional(void *arg, dns_name_t *name, dns_rdatatype_t qtype) {
                 */
                goto try_glue;
 
-       result = dns_db_find(db, name, version, type,  client->query.dboptions,
+       result = dns_db_find(db, name, version, type, client->query.dboptions,
                             client->now, &node, fname, rdataset,
                             sigrdataset);
        if (result == ISC_R_SUCCESS)
@@ -1248,7 +1327,7 @@ query_addadditional(void *arg, dns_name_t *name, dns_rdatatype_t qtype) {
         * recursing to add address records, which in turn can cause
         * recursion to add KEYs.
         */
-       if (type == dns_rdatatype_srv && trdataset != NULL) {
+       if (type == dns_rdatatype_srv && trdataset != NULL) {
                /*
                 * If we're adding SRV records to the additional data
                 * section, it's helpful if we add the SRV additional data
@@ -1325,7 +1404,7 @@ query_iscachevalid(dns_zone_t *zone, dns_db_t *db, dns_db_t *db0,
        dns_db_closeversion(db_current, &version_current, ISC_FALSE);
        if (db0 == NULL && db_current != NULL)
                dns_db_detach(&db_current);
-       
+
        return (result);
 }
 
@@ -1391,7 +1470,7 @@ query_addadditional2(void *arg, dns_name_t *name, dns_rdatatype_t qtype) {
         * lookup and iterate over the node.
         * XXXJT: this approach can cause a suboptimal result when the cache
         * DB only has partial address types and the glue DB has remaining
-        * ones. 
+        * ones.
         */
        type = dns_rdatatype_any;
 
@@ -1682,7 +1761,7 @@ query_addadditional2(void *arg, dns_name_t *name, dns_rdatatype_t qtype) {
  setcache:
        /*
         * Set the new result in the cache if required.  We do not support
-        * caching additional data from a cache DB. 
+        * caching additional data from a cache DB.
         */
        if (needadditionalcache == ISC_TRUE &&
            (additionaltype == dns_rdatasetadditional_fromauth ||
@@ -2429,20 +2508,20 @@ query_addwildcardproof(ns_client_t *client, dns_db_t *db,
         *   Given:
         *      example SOA
         *      example NSEC b.example
-        *      b.example A
-        *      b.example NSEC a.d.example
-        *      a.d.example A
-        *      a.d.example NSEC g.f.example
-        *      g.f.example A
-        *      g.f.example NSEC z.i.example
-        *      z.i.example A
-        *      z.i.example NSEC example
+        *      b.example A
+        *      b.example NSEC a.d.example
+        *      a.d.example A
+        *      a.d.example NSEC g.f.example
+        *      g.f.example A
+        *      g.f.example NSEC z.i.example
+        *      z.i.example A
+        *      z.i.example NSEC example
         *
         *   QNAME:
         *   a.example -> example NSEC b.example
-        *      owner common example
-        *      next common example
-        *      wild *.example
+        *      owner common example
+        *      next common example
+        *      wild *.example
         *   d.b.example -> b.example NSEC a.d.example
         *      owner common b.example
         *      next common example
@@ -2453,7 +2532,7 @@ query_addwildcardproof(ns_client_t *client, dns_db_t *db,
         *      wild *.f.example
         *  j.example -> z.i.example NSEC example
         *      owner common example
-        *      next common example
+        *      next common example
         *      wild *.f.example
         */
        options = client->query.dboptions | DNS_DBFIND_NOWILD;
@@ -2514,7 +2593,7 @@ query_addwildcardproof(ns_client_t *client, dns_db_t *db,
                        name = wname;
                        goto again;
                }
-       } 
+       }
  cleanup:
        if (rdataset != NULL)
                query_putrdataset(client, &rdataset);
@@ -2777,7 +2856,7 @@ static isc_result_t
 rdata_tonetaddr(dns_rdata_t *rdata, isc_netaddr_t *netaddr) {
        struct in_addr ina;
        struct in6_addr in6a;
-       
+
        switch (rdata->type) {
        case dns_rdatatype_a:
                INSIST(rdata->length == 4);
@@ -2830,7 +2909,7 @@ setup_query_sortlist(ns_client_t *client) {
        isc_netaddr_t netaddr;
        dns_rdatasetorderfunc_t order = NULL;
        void *order_arg = NULL;
-       
+
        isc_netaddr_fromsockaddr(&netaddr, &client->peeraddr);
        switch (ns_sortlist_setup(client->view->sortlist,
                               &netaddr, &order_arg)) {
@@ -3121,7 +3200,7 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
 
                goto resume;
        }
-       
+
        /*
         * Not returning from recursion.
         */
@@ -3216,7 +3295,17 @@ query_find(ns_client_t *client, dns_fetchevent_t *event, dns_rdatatype_t qtype)
 
        if (event == NULL && client->query.restarts == 0) {
                if (is_zone) {
-                       dns_zone_attach(zone, &client->query.authzone);
+#ifdef DLZ
+                       if (zone != NULL) {
+                               /*
+                                * if is_zone = true, zone = NULL then this is
+                                * a DLZ zone.  Don't attempt to attach zone.
+                                */
+#endif
+                               dns_zone_attach(zone, &client->query.authzone);
+#ifdef DLZ
+                       }
+#endif
                        dns_db_attach(db, &client->query.authdb);
                }
                client->query.authdbset = ISC_TRUE;
@@ -4131,10 +4220,10 @@ ns_query_start(ns_client_t *client) {
 
        if ((message->flags & DNS_MESSAGEFLAG_RD) != 0)
                client->query.attributes |= NS_QUERYATTR_WANTRECURSION;
-       
+
        if ((client->extflags & DNS_MESSAGEEXTFLAG_DO) != 0)
                client->attributes |= NS_CLIENTATTR_WANTDNSSEC;
-       
+
        if (client->view->minimalresponses)
                client->query.attributes |= (NS_QUERYATTR_NOAUTHORITY |
                                             NS_QUERYATTR_NOADDITIONAL);
index 1a1674f517a66189e1742c69b5f0fa1040d4d04f..a169d67268b9d4bbe69bb003fcaaf35b58f65b05 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: server.c,v 1.449 2005/08/23 02:36:07 marka Exp $ */
+/* $Id: server.c,v 1.450 2005/09/05 00:10:52 marka Exp $ */
 
 /*! \file */
 
@@ -48,6 +48,9 @@
 #include <dns/cache.h>
 #include <dns/db.h>
 #include <dns/dispatch.h>
+#ifdef DLZ
+#include <dns/dlz.h>
+#endif
 #include <dns/forward.h>
 #include <dns/journal.h>
 #include <dns/keytable.h>
@@ -826,6 +829,11 @@ configure_view(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
        cfg_obj_t *forwarders;
        cfg_obj_t *alternates;
        cfg_obj_t *zonelist;
+#ifdef DLZ
+       cfg_obj_t *dlz;
+       unsigned int dlzargc;
+       char **dlzargv;
+#endif
        cfg_obj_t *disabled;
        cfg_obj_t *obj;
        cfg_listelt_t *element;
@@ -954,6 +962,45 @@ configure_view(dns_view_t *view, cfg_obj_t *config, cfg_obj_t *vconfig,
                                     actx));
        }
 
+#ifdef DLZ
+       /*
+        * Create Dynamically Loadable Zone driver.
+        */
+       dlz = NULL;
+       if (voptions != NULL)
+               (void)cfg_map_get(voptions, "dlz", &dlz);
+       else
+               (void)cfg_map_get(config, "dlz", &dlz);
+
+       obj = NULL;
+       if (dlz != NULL) {
+               (void)cfg_map_get(cfg_tuple_get(dlz, "options"),
+                                 "database", &obj);
+               if (obj != NULL) {
+                       char *s = isc_mem_strdup(mctx, cfg_obj_asstring(obj));
+                       if (s == NULL) {
+                               result = ISC_R_NOMEMORY;
+                               goto cleanup;
+                       }
+                       
+                       result = dns_dlzstrtoargv(mctx, s, &dlzargc, &dlzargv);
+                       if (result != ISC_R_SUCCESS) {
+                               isc_mem_free(mctx, s);
+                               goto cleanup;
+                       }
+
+                       obj = cfg_tuple_get(dlz, "name");
+                       result = dns_dlzcreate(mctx, cfg_obj_asstring(obj),
+                                              dlzargv[0], dlzargc, dlzargv,
+                                              &view->dlzdatabase);
+                       isc_mem_free(mctx, s);
+                       isc_mem_put(mctx, dlzargv, dlzargc * sizeof(*dlzargv));
+                       if (result == ISC_R_SUCCESS)
+                               goto cleanup;
+               }
+       }
+#endif
+
        /*
         * Configure the view's cache.  Try to reuse an existing
         * cache if possible, otherwise create a new cache.
index 3b8e19d5cf770f8878b14097bd48e4358f09bffd..adce9985c8c9fb1392d6f20dde453eb7069619e5 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: xfrout.c,v 1.119 2005/04/27 04:55:55 sra Exp $ */
+/* $Id: xfrout.c,v 1.120 2005/09/05 00:10:53 marka Exp $ */
 
 #include <config.h>
 
@@ -27,6 +27,9 @@
 
 #include <dns/db.h>
 #include <dns/dbiterator.h>
+#ifdef DLZ
+#include <dns/dlz.h>
+#endif
 #include <dns/fixedname.h>
 #include <dns/journal.h>
 #include <dns/message.h>
@@ -905,6 +908,9 @@ ns_xfr_start(ns_client_t *client, dns_rdatatype_t reqtype) {
        char msg[NS_CLIENT_ACLMSGSIZE("zone transfer")];
        char keyname[DNS_NAME_FORMATSIZE];
        isc_boolean_t is_poll = ISC_FALSE;
+#ifdef DLZ
+       isc_boolean_t is_dlz = ISC_FALSE;
+#endif
 
        switch (reqtype) {
        case dns_rdatatype_axfr:
@@ -955,19 +961,71 @@ ns_xfr_start(ns_client_t *client, dns_rdatatype_t reqtype) {
 
        result = dns_zt_find(client->view->zonetable, question_name, 0, NULL,
                             &zone);
+
        if (result != ISC_R_SUCCESS)
-               FAILQ(DNS_R_NOTAUTH, "non-authoritative zone",
-                     question_name, question_class);
-       switch(dns_zone_gettype(zone)) {
-       case dns_zone_master:
-       case dns_zone_slave:
-               break;  /* Master and slave zones are OK for transfer. */
-       default:
-               FAILQ(DNS_R_NOTAUTH, "non-authoritative zone",
-                     question_name, question_class);
+#ifdef DLZ
+       {
+               /*
+                * Normal zone table does not have a match.  Try the DLZ database
+                */
+               if (client->view->dlzdatabase != NULL) {
+                       result = dns_dlzallowzonexfr(client->view,
+                                                    question_name, &client->peeraddr,
+                                                    &db);
+
+                       if (result == ISC_R_NOPERM) {
+                               char _buf1[DNS_NAME_FORMATSIZE];
+                               char _buf2[DNS_RDATACLASS_FORMATSIZE];
+
+                               result = DNS_R_REFUSED;
+                               dns_name_format(question_name, _buf1,
+                                               sizeof(_buf1));
+                               dns_rdataclass_format(question_class,
+                                                     _buf2, sizeof(_buf2));
+                               ns_client_log(client, DNS_LOGCATEGORY_SECURITY,
+                                             NS_LOGMODULE_XFER_OUT,
+                                             ISC_LOG_ERROR,
+                                             "zone transfer '%s/%s' denied",
+                                             _buf1, _buf2);
+                               goto failure;
+                       }
+                       if (result != ISC_R_SUCCESS)
+#endif
+                       FAILQ(DNS_R_NOTAUTH, "non-authoritative zone",
+                                 question_name, question_class);
+#ifdef DLZ
+                       is_dlz = ISC_TRUE;
+                       /*
+                        * DLZ only support full zone transfer, not incremental
+                        */
+                       if (reqtype != dns_rdatatype_axfr) {
+                               mnemonic = "AXFR-style IXFR";
+                               reqtype = dns_rdatatype_axfr;
+                       }
+
+               } else {
+                       /*
+                        * not DLZ and not in normal zone table, we are
+                        * not authoritative
+                        */
+                       FAILQ(DNS_R_NOTAUTH, "non-authoritative zone",
+                             question_name, question_class);
+               }
+       } else {
+               /* zone table has a match */
+#endif
+               switch(dns_zone_gettype(zone)) {
+                       case dns_zone_master:
+                       case dns_zone_slave:
+                               break;  /* Master and slave zones are OK for transfer. */
+                       default:
+                               FAILQ(DNS_R_NOTAUTH, "non-authoritative zone", question_name, question_class);
+                       }
+               CHECK(dns_zone_getdb(zone, &db));
+               dns_db_currentversion(db, &ver);
+#ifdef DLZ
        }
-       CHECK(dns_zone_getdb(zone, &db));
-       dns_db_currentversion(db, &ver);
+#endif
 
        xfrout_log1(client, question_name, question_class, ISC_LOG_DEBUG(6),
                    "%s question section OK", mnemonic);
@@ -1023,11 +1081,20 @@ ns_xfr_start(ns_client_t *client, dns_rdatatype_t reqtype) {
        /*
         * Decide whether to allow this transfer.
         */
-       ns_client_aclmsg("zone transfer", question_name, reqtype,
-                        client->view->rdclass, msg, sizeof(msg));
-       CHECK(ns_client_checkacl(client, msg,
-                                dns_zone_getxfracl(zone), ISC_TRUE,
-                                ISC_LOG_ERROR));
+#ifdef DLZ
+       /*
+        * if not a DLZ zone decide whether to allow this transfer.
+        */
+       if (!is_dlz) {
+#endif
+               ns_client_aclmsg("zone transfer", question_name, reqtype,
+                                client->view->rdclass, msg, sizeof(msg));
+               CHECK(ns_client_checkacl(client, msg,
+                                        dns_zone_getxfracl(zone), ISC_TRUE,
+                                        ISC_LOG_ERROR));
+#ifdef DLZ
+       }
+#endif
 
        /*
         * AXFR over UDP is not possible.
@@ -1051,6 +1118,10 @@ ns_xfr_start(ns_client_t *client, dns_rdatatype_t reqtype) {
        /*
         * Get a dynamically allocated copy of the current SOA.
         */
+#ifdef DLZ
+       if (is_dlz)
+               dns_db_currentversion(db, &ver);
+#endif
        CHECK(dns_db_createsoatuple(db, ver, mctx, DNS_DIFFOP_EXISTS,
                                    &current_soa_tuple));
 
@@ -1133,15 +1204,32 @@ ns_xfr_start(ns_client_t *client, dns_rdatatype_t reqtype) {
         * Create the xfrout context object.  This transfers the ownership
         * of "stream", "db", "ver", and "quota" to the xfrout context object.
         */
-       CHECK(xfrout_ctx_create(mctx, client, request->id, question_name,
-                               reqtype, question_class, db, ver, quota,
-                               stream, dns_message_gettsigkey(request),
-                               tsigbuf,
-                               dns_zone_getmaxxfrout(zone),
-                               dns_zone_getidleout(zone),
-                               (format == dns_many_answers) ?
-                                       ISC_TRUE : ISC_FALSE,
-                               &xfr));
+
+
+
+#ifdef DLZ
+       if (is_dlz)
+               CHECK(xfrout_ctx_create(mctx, client, request->id, question_name,
+                                       reqtype, question_class, db, ver, quota,
+                                       stream, dns_message_gettsigkey(request),
+                                       tsigbuf,
+                                       3600,
+                                       3600,
+                                       (format == dns_many_answers) ?
+                                       ISC_TRUE : ISC_FALSE,
+                                       &xfr));
+       else
+#endif
+               CHECK(xfrout_ctx_create(mctx, client, request->id, question_name,
+                                       reqtype, question_class, db, ver, quota,
+                                       stream, dns_message_gettsigkey(request),
+                                       tsigbuf,
+                                       dns_zone_getmaxxfrout(zone),
+                                       dns_zone_getidleout(zone),
+                                       (format == dns_many_answers) ?
+                                       ISC_TRUE : ISC_FALSE,
+                                       &xfr));
+
        xfr->mnemonic = mnemonic;
        stream = NULL;
        quota = NULL;
index a13fe7503b3b9f62403ab6722c8e43d90428d00f..838b0072f036bbda711f8e1e1a7b9fc63f354828 100644 (file)
@@ -15,7 +15,7 @@
 # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 # PERFORMANCE OF THIS SOFTWARE.
 
-# $Id: conf.sh.in,v 1.32 2005/07/27 04:12:00 marka Exp $
+# $Id: conf.sh.in,v 1.33 2005/09/05 00:10:53 marka Exp $
 
 #
 # Common configuration data for system tests, to be sourced into
@@ -43,8 +43,8 @@ CHECKCONF=$TOP/bin/check/named-checkconf
 # The "stress" test is not run by default since it creates enough
 # load on the machine to make it unusable to other users.
 # v6synth
-SUBDIRS="cacheclean checkconf checknames dnssec forward glue ixfr limits \
-    lwresd masterfile masterformat notify nsupdate resolver sortlist stub \
+SUBDIRS="cacheclean checkconf checknames dnssec forward glue ixfr limits
+    lwresd masterfile masterformat notify nsupdate resolver sortlist stub
     tkey unknown upforwd views xfer xferquota zonechecks"
 
 # PERL will be an empty string if no perl interpreter was found.
index ade2f474e373438e1f4bfe9d9387fbce4ace864d..946a37269254d1709bcbd11538856603b22d0c98 100755 (executable)
--- a/configure
+++ b/configure
@@ -14,7 +14,7 @@
 # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 # PERFORMANCE OF THIS SOFTWARE.
 #
-# $Id: configure,v 1.380 2005/09/01 03:36:18 marka Exp $
+# $Id: configure,v 1.381 2005/09/05 00:12:29 marka Exp $
 #
 # Portions Copyright (C) 1996-2001  Nominum, Inc.
 #
@@ -29,7 +29,7 @@
 # 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.
-# From configure.in Revision: 1.391 .
+# From configure.in Revision: 1.392 .
 # Guess values for system-dependent variables and create Makefiles.
 # Generated by GNU Autoconf 2.59.
 #
@@ -495,8 +495,8 @@ ac_includes_default="\
 # include <unistd.h>
 #endif"
 
-ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS subdirs build build_cpu build_vendor build_os host host_cpu host_vendor host_os SET_MAKE RANLIB ac_ct_RANLIB INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA LN_S STD_CINCLUDES STD_CDEFINES STD_CWARNINGS CCOPT AR ARFLAGS LN ETAGS PERL CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CPP EGREP ISC_SOCKADDR_LEN_T ISC_PLATFORM_HAVELONGLONG ISC_PLATFORM_HAVELIFCONF ISC_PLATFORM_NEEDSYSSELECTH LWRES_PLATFORM_NEEDSYSSELECTH USE_OPENSSL DST_OPENSSL_INC USE_GSSAPI DST_GSSAPI_INC DNS_CRYPTO_LIBS ALWAYS_DEFINES ISC_PLATFORM_USETHREADS ISC_THREAD_DIR MKDEPCC MKDEPCFLAGS MKDEPPROG IRIX_DNSSEC_WARNINGS_HACK purify_path PURIFY ECHO ac_ct_AR STRIP ac_ct_STRIP CXX CXXFLAGS ac_ct_CXX CXXCPP F77 FFLAGS ac_ct_F77 LIBTOOL O A SA LIBTOOL_MKDEP_SED LIBTOOL_MODE_COMPILE LIBTOOL_MODE_INSTALL LIBTOOL_MODE_LINK LIBTOOL_ALLOW_UNDEFINED LIBTOOL_IN_MAIN LIBBIND ISC_PLATFORM_HAVEIPV6 LWRES_PLATFORM_HAVEIPV6 ISC_PLATFORM_NEEDNETINETIN6H LWRES_PLATFORM_NEEDNETINETIN6H ISC_PLATFORM_NEEDNETINET6IN6H LWRES_PLATFORM_NEEDNETINET6IN6H ISC_PLATFORM_HAVEINADDR6 LWRES_PLATFORM_HAVEINADDR6 ISC_PLATFORM_NEEDIN6ADDRANY LWRES_PLATFORM_NEEDIN6ADDRANY ISC_PLATFORM_NEEDIN6ADDRLOOPBACK LWRES_PLATFORM_NEEDIN6ADDRLOOPBACK ISC_PLATFORM_HAVEIN6PKTINFO ISC_PLATFORM_FIXIN6ISADDR ISC_IPV6_H ISC_IPV6_O ISC_ISCIPV6_O ISC_IPV6_C LWRES_HAVE_SIN6_SCOPE_ID ISC_PLATFORM_HAVESCOPEID ISC_PLATFORM_HAVEIF_LADDRREQ ISC_PLATFORM_HAVEIF_LADDRCONF ISC_PLATFORM_NEEDNTOP ISC_PLATFORM_NEEDPTON ISC_PLATFORM_NEEDATON ISC_PLATFORM_HAVESALEN LWRES_PLATFORM_HAVESALEN ISC_PLATFORM_MSGHDRFLAVOR ISC_PLATFORM_NEEDPORTT ISC_LWRES_NEEDADDRINFO ISC_LWRES_NEEDRRSETINFO ISC_LWRES_SETHOSTENTINT ISC_LWRES_ENDHOSTENTINT ISC_LWRES_GETNETBYADDRINADDR ISC_LWRES_SETNETENTINT ISC_LWRES_ENDNETENTINT ISC_LWRES_GETHOSTBYADDRVOID ISC_LWRES_NEEDHERRNO ISC_LWRES_GETIPNODEPROTO ISC_LWRES_GETADDRINFOPROTO ISC_LWRES_GETNAMEINFOPROTO ISC_PLATFORM_NEEDSTRSEP ISC_PLATFORM_NEEDMEMMOVE ISC_PLATFORM_NEEDSTRTOUL LWRES_PLATFORM_NEEDSTRTOUL GENRANDOMLIB ISC_PLATFORM_NEEDSTRLCPY ISC_PLATFORM_NEEDSTRLCAT ISC_PLATFORM_NEEDSPRINTF LWRES_PLATFORM_NEEDSPRINTF ISC_PLATFORM_NEEDVSNPRINTF LWRES_PLATFORM_NEEDVSNPRINTF ISC_EXTRA_OBJS ISC_EXTRA_SRCS ISC_PLATFORM_QUADFORMAT LWRES_PLATFORM_QUADFORMAT ISC_PLATFORM_HAVESYSUNH ISC_PLATFORM_RLIMITTYPE ISC_PLATFORM_USEDECLSPEC LWRES_PLATFORM_USEDECLSPEC ISC_PLATFORM_BRACEPTHREADONCEINIT ISC_PLATFORM_HAVEIFNAMETOINDEX ISC_PLATFORM_HAVEXADD ISC_PLATFORM_HAVECMPXCHG ISC_PLATFORM_HAVEATOMICSTORE ISC_PLATFORM_USEGCCASM ISC_PLATFORM_USEOSFASM ISC_PLATFORM_USESTDASM ISC_ARCH_DIR LATEX PDFLATEX W3M XSLTPROC XMLLINT XSLT_DOCBOOK_STYLE_HTML XSLT_DOCBOOK_STYLE_XHTML XSLT_DOCBOOK_STYLE_MAN XSLT_DOCBOOK_CHUNK_HTML XSLT_DOCBOOK_CHUNK_XHTML XSLT_DOCBOOK_CHUNKTOC_HTML XSLT_DOCBOOK_CHUNKTOC_XHTML XSLT_DOCBOOK_MAKETOC_HTML XSLT_DOCBOOK_MAKETOC_XHTML XSLT_DB2LATEX_STYLE XSLT_DB2LATEX_ADMONITIONS BIND9_TOP_BUILDDIR BIND9_ISC_BUILDINCLUDE BIND9_ISCCC_BUILDINCLUDE BIND9_ISCCFG_BUILDINCLUDE BIND9_DNS_BUILDINCLUDE BIND9_LWRES_BUILDINCLUDE BIND9_BIND9_BUILDINCLUDE BIND9_VERSION LIBOBJS LTLIBOBJS'
-ac_subst_files='BIND9_MAKE_INCLUDES BIND9_MAKE_RULES LIBISC_API LIBISCCC_API LIBISCCFG_API LIBDNS_API LIBBIND9_API LIBLWRES_API'
+ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS subdirs build build_cpu build_vendor build_os host host_cpu host_vendor host_os SET_MAKE RANLIB ac_ct_RANLIB INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA LN_S STD_CINCLUDES STD_CDEFINES STD_CWARNINGS CCOPT AR ARFLAGS LN ETAGS PERL CC CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT CPP EGREP ISC_SOCKADDR_LEN_T ISC_PLATFORM_HAVELONGLONG ISC_PLATFORM_HAVELIFCONF ISC_PLATFORM_NEEDSYSSELECTH LWRES_PLATFORM_NEEDSYSSELECTH USE_OPENSSL DST_OPENSSL_INC USE_GSSAPI DST_GSSAPI_INC DNS_CRYPTO_LIBS ALWAYS_DEFINES ISC_PLATFORM_USETHREADS ISC_THREAD_DIR MKDEPCC MKDEPCFLAGS MKDEPPROG IRIX_DNSSEC_WARNINGS_HACK purify_path PURIFY ECHO ac_ct_AR STRIP ac_ct_STRIP CXX CXXFLAGS ac_ct_CXX CXXCPP F77 FFLAGS ac_ct_F77 LIBTOOL O A SA LIBTOOL_MKDEP_SED LIBTOOL_MODE_COMPILE LIBTOOL_MODE_INSTALL LIBTOOL_MODE_LINK LIBTOOL_ALLOW_UNDEFINED LIBTOOL_IN_MAIN LIBBIND ISC_PLATFORM_HAVEIPV6 LWRES_PLATFORM_HAVEIPV6 ISC_PLATFORM_NEEDNETINETIN6H LWRES_PLATFORM_NEEDNETINETIN6H ISC_PLATFORM_NEEDNETINET6IN6H LWRES_PLATFORM_NEEDNETINET6IN6H ISC_PLATFORM_HAVEINADDR6 LWRES_PLATFORM_HAVEINADDR6 ISC_PLATFORM_NEEDIN6ADDRANY LWRES_PLATFORM_NEEDIN6ADDRANY ISC_PLATFORM_NEEDIN6ADDRLOOPBACK LWRES_PLATFORM_NEEDIN6ADDRLOOPBACK ISC_PLATFORM_HAVEIN6PKTINFO ISC_PLATFORM_FIXIN6ISADDR ISC_IPV6_H ISC_IPV6_O ISC_ISCIPV6_O ISC_IPV6_C LWRES_HAVE_SIN6_SCOPE_ID ISC_PLATFORM_HAVESCOPEID ISC_PLATFORM_HAVEIF_LADDRREQ ISC_PLATFORM_HAVEIF_LADDRCONF ISC_PLATFORM_NEEDNTOP ISC_PLATFORM_NEEDPTON ISC_PLATFORM_NEEDATON ISC_PLATFORM_HAVESALEN LWRES_PLATFORM_HAVESALEN ISC_PLATFORM_MSGHDRFLAVOR ISC_PLATFORM_NEEDPORTT ISC_LWRES_NEEDADDRINFO ISC_LWRES_NEEDRRSETINFO ISC_LWRES_SETHOSTENTINT ISC_LWRES_ENDHOSTENTINT ISC_LWRES_GETNETBYADDRINADDR ISC_LWRES_SETNETENTINT ISC_LWRES_ENDNETENTINT ISC_LWRES_GETHOSTBYADDRVOID ISC_LWRES_NEEDHERRNO ISC_LWRES_GETIPNODEPROTO ISC_LWRES_GETADDRINFOPROTO ISC_LWRES_GETNAMEINFOPROTO ISC_PLATFORM_NEEDSTRSEP ISC_PLATFORM_NEEDMEMMOVE ISC_PLATFORM_NEEDSTRTOUL LWRES_PLATFORM_NEEDSTRTOUL GENRANDOMLIB ISC_PLATFORM_NEEDSTRLCPY ISC_PLATFORM_NEEDSTRLCAT ISC_PLATFORM_NEEDSPRINTF LWRES_PLATFORM_NEEDSPRINTF ISC_PLATFORM_NEEDVSNPRINTF LWRES_PLATFORM_NEEDVSNPRINTF ISC_EXTRA_OBJS ISC_EXTRA_SRCS ISC_PLATFORM_QUADFORMAT LWRES_PLATFORM_QUADFORMAT ISC_PLATFORM_HAVESYSUNH ISC_PLATFORM_RLIMITTYPE ISC_PLATFORM_USEDECLSPEC LWRES_PLATFORM_USEDECLSPEC ISC_PLATFORM_BRACEPTHREADONCEINIT ISC_PLATFORM_HAVEIFNAMETOINDEX ISC_PLATFORM_HAVEXADD ISC_PLATFORM_HAVECMPXCHG ISC_PLATFORM_HAVEATOMICSTORE ISC_PLATFORM_USEGCCASM ISC_PLATFORM_USEOSFASM ISC_PLATFORM_USESTDASM ISC_ARCH_DIR LATEX PDFLATEX W3M XSLTPROC XMLLINT XSLT_DOCBOOK_STYLE_HTML XSLT_DOCBOOK_STYLE_XHTML XSLT_DOCBOOK_STYLE_MAN XSLT_DOCBOOK_CHUNK_HTML XSLT_DOCBOOK_CHUNK_XHTML XSLT_DOCBOOK_CHUNKTOC_HTML XSLT_DOCBOOK_CHUNKTOC_XHTML XSLT_DOCBOOK_MAKETOC_HTML XSLT_DOCBOOK_MAKETOC_XHTML XSLT_DB2LATEX_STYLE XSLT_DB2LATEX_ADMONITIONS BIND9_TOP_BUILDDIR BIND9_ISC_BUILDINCLUDE BIND9_ISCCC_BUILDINCLUDE BIND9_ISCCFG_BUILDINCLUDE BIND9_DNS_BUILDINCLUDE BIND9_LWRES_BUILDINCLUDE BIND9_BIND9_BUILDINCLUDE BIND9_VERSION PG_CONFIG USE_DLZ DLZ_DRIVER_INCLUDES DLZ_DRIVER_LIBS DLZ_DRIVER_SRCS DLZ_DRIVER_OBJS LIBOBJS LTLIBOBJS'
+ac_subst_files='BIND9_MAKE_INCLUDES BIND9_MAKE_RULES LIBISC_API LIBISCCC_API LIBISCCFG_API LIBDNS_API LIBBIND9_API LIBLWRES_API DLZ_DRIVER_RULES'
 
 # Initialize some variables set by options.
 ac_init_help=
@@ -1082,6 +1082,20 @@ Optional Packages:
   --with-tags[=TAGS]
                           include additional configurations [automatic]
   --with-kame=PATH     use Kame IPv6 default path /usr/local/v6
+  --with-dlz-postgres=PATH   Build with Postgres DLZ driver yes|no|path.
+                               (Required to use Postgres with DLZ)
+  --with-dlz-mysql=PATH   Build with MySQL DLZ driver yes|no|path.
+                               (Required to use MySQL with DLZ)
+  --with-dlz-bdb=PATH   Build with Berkeley DB DLZ driver yes|no|path.
+                               (Required to use Berkeley DB with DLZ)
+  --with-dlz-filesystem=PATH   Build with filesystem DLZ driver yes|no.
+                               (Required to use file system driver with DLZ)
+  --with-dlz-ldap=PATH   Build with LDAP DLZ driver yes|no|path.
+                               (Required to use LDAP with DLZ)
+  --with-dlz-odbc=PATH   Build with ODBC DLZ driver yes|no|path.
+                               (Required to use ODBC with DLZ)
+  --with-dlz-stub=PATH   Build with stub DLZ driver yes|no.
+                               (Required to use stub driver with DLZ)
 
 Some influential environment variables:
   CC          C compiler command
@@ -1829,6 +1843,14 @@ fi
 
 
 
+#
+# Make very sure that these are the first files processed by
+# config.status, since we use the processed output as the input for
+# AC_SUBST_FILE() subsitutions in other files.
+#
+                    ac_config_files="$ac_config_files make/rules make/includes"
+
+
 # Extract the first word of "ar", so it can be a program name with args.
 set dummy ar; ac_word=$2
 echo "$as_me:$LINENO: checking for $ac_word" >&5
@@ -4815,6 +4837,7 @@ presetting ac_cv_c_bigendian=no (or yes) will help" >&2;}
 esac
 
 
+
 #
 # was --with-openssl specified?
 #
@@ -8134,7 +8157,7 @@ ia64-*-hpux*)
   ;;
 *-*-irix6*)
   # Find out which ABI we are using.
-  echo '#line 8137 "configure"' > conftest.$ac_ext
+  echo '#line 8160 "configure"' > conftest.$ac_ext
   if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
   (eval $ac_compile) 2>&5
   ac_status=$?
@@ -9131,7 +9154,7 @@ fi
 
 
 # Provide some information about the compiler.
-echo "$as_me:9134:" \
+echo "$as_me:9157:" \
      "checking for Fortran 77 compiler version" >&5
 ac_compiler=`set X $ac_compile; echo $2`
 { (eval echo "$as_me:$LINENO: \"$ac_compiler --version </dev/null >&5\"") >&5
@@ -10192,11 +10215,11 @@ else
    -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:10195: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:10218: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:10199: \$? = $ac_status" >&5
+   echo "$as_me:10222: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings
@@ -10435,11 +10458,11 @@ else
    -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:10438: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:10461: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:10442: \$? = $ac_status" >&5
+   echo "$as_me:10465: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings
@@ -10495,11 +10518,11 @@ else
    -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:10498: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:10521: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>out/conftest.err)
    ac_status=$?
    cat out/conftest.err >&5
-   echo "$as_me:10502: \$? = $ac_status" >&5
+   echo "$as_me:10525: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s out/conftest2.$ac_objext
    then
      # The compiler can only warn and ignore the option if not recognized
@@ -12680,7 +12703,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<EOF
-#line 12683 "configure"
+#line 12706 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -12778,7 +12801,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<EOF
-#line 12781 "configure"
+#line 12804 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -14975,11 +14998,11 @@ else
    -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:14978: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:15001: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:14982: \$? = $ac_status" >&5
+   echo "$as_me:15005: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings
@@ -15035,11 +15058,11 @@ else
    -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:15038: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:15061: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>out/conftest.err)
    ac_status=$?
    cat out/conftest.err >&5
-   echo "$as_me:15042: \$? = $ac_status" >&5
+   echo "$as_me:15065: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s out/conftest2.$ac_objext
    then
      # The compiler can only warn and ignore the option if not recognized
@@ -16396,7 +16419,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<EOF
-#line 16399 "configure"
+#line 16422 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -16494,7 +16517,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<EOF
-#line 16497 "configure"
+#line 16520 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -17331,11 +17354,11 @@ else
    -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:17334: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:17357: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:17338: \$? = $ac_status" >&5
+   echo "$as_me:17361: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings
@@ -17391,11 +17414,11 @@ else
    -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:17394: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:17417: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>out/conftest.err)
    ac_status=$?
    cat out/conftest.err >&5
-   echo "$as_me:17398: \$? = $ac_status" >&5
+   echo "$as_me:17421: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s out/conftest2.$ac_objext
    then
      # The compiler can only warn and ignore the option if not recognized
@@ -19430,11 +19453,11 @@ else
    -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:19433: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:19456: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:19437: \$? = $ac_status" >&5
+   echo "$as_me:19460: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings
@@ -19673,11 +19696,11 @@ else
    -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:19676: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:19699: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:19680: \$? = $ac_status" >&5
+   echo "$as_me:19703: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings
@@ -19733,11 +19756,11 @@ else
    -e 's:.*FLAGS}? :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:19736: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:19759: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>out/conftest.err)
    ac_status=$?
    cat out/conftest.err >&5
-   echo "$as_me:19740: \$? = $ac_status" >&5
+   echo "$as_me:19763: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s out/conftest2.$ac_objext
    then
      # The compiler can only warn and ignore the option if not recognized
@@ -21918,7 +21941,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<EOF
-#line 21921 "configure"
+#line 21944 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -22016,7 +22039,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<EOF
-#line 22019 "configure"
+#line 22042 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -23092,6 +23115,7 @@ case "$enable_libbind" in
                ;;
 esac
 
+
 #
 # Here begins a very long section to determine the system's networking
 # capabilities.  The order of the tests is signficant.
@@ -28390,6 +28414,673 @@ LIBBIND9_API=$srcdir/lib/bind9/api
 
 LIBLWRES_API=$srcdir/lib/lwres/api
 
+#
+# Configure any DLZ drivers.
+#
+# If config.dlz.in selects one or more DLZ drivers, it will set
+# USE_DLZ to a non-empty value, which will be our clue to
+# enable the DLZ core functions.
+#
+# This section has to come after the libtool stuff because it needs to
+# know how to name the driver object files.
+#
+
+USE_DLZ=""
+DLZ_DRIVER_INCLUDES=""
+DLZ_DRIVER_LIBS=""
+DLZ_DRIVER_SRCS=""
+DLZ_DRIVER_OBJS=""
+
+# Copyright (C) 2005  Internet Systems Consortium, Inc. ("ISC")
+#
+# 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 ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS.  IN NO EVENT SHALL ISC 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.
+
+#
+# Shorthand.  Note quoting: DLZ_DRIVER_DIR expanded in Makefile, not here.
+#
+dlzdir='${DLZ_DRIVER_DIR}'
+
+#
+# Private autoconf macro to simplify configuring drivers:
+#
+#   DLZ_ADD_DRIVER(DEFINE, DRIVER, INCLUDES, LIBS)
+#
+# where:
+#   DEFINE is FOO (to define -DDLZ_FOO)
+#   DRIVER is dlz_foo_driver (sources without the .c)
+#   INCLUDES is any necessary include definitions
+#   LIBS is any necessary library definitions
+#
+
+
+#
+# Check for the various DLZ drivers
+#
+
+#
+# Was --with-dlz-postgres specified?
+#
+
+echo "$as_me:$LINENO: checking for Postgres DLZ driver" >&5
+echo $ECHO_N "checking for Postgres DLZ driver... $ECHO_C" >&6
+
+# Check whether --with-dlz_postgres or --without-dlz_postgres was given.
+if test "${with_dlz_postgres+set}" = set; then
+  withval="$with_dlz_postgres"
+  use_dlz_postgres="$withval"
+else
+  use_dlz_postgres="no"
+fi;
+
+if test "$use_dlz_postgres" = "yes"
+then
+       # User did not specify a path - guess it
+       # Ask Postgres to tell us where it is
+
+       for ac_prog in pg_config
+do
+  # Extract the first word of "$ac_prog", so it can be a program name with args.
+set dummy $ac_prog; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_path_PG_CONFIG+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  case $PG_CONFIG in
+  [\\/]* | ?:[\\/]*)
+  ac_cv_path_PG_CONFIG="$PG_CONFIG" # Let the user override the test with a path.
+  ;;
+  *)
+  as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_path_PG_CONFIG="$as_dir/$ac_word$ac_exec_ext"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+  ;;
+esac
+fi
+PG_CONFIG=$ac_cv_path_PG_CONFIG
+
+if test -n "$PG_CONFIG"; then
+  echo "$as_me:$LINENO: result: $PG_CONFIG" >&5
+echo "${ECHO_T}$PG_CONFIG" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+  test -n "$PG_CONFIG" && break
+done
+test -n "$PG_CONFIG" || PG_CONFIG="not found"
+
+
+       if test "$PG_CONFIG" != "not found"
+       then
+               use_dlz_postgres=`$PG_CONFIG --includedir`
+               use_dlz_postgres_lib=`$PG_CONFIG --libdir`
+       fi
+fi
+
+if test "$use_dlz_postgres" = "yes"
+then
+       # User did not specify path and Postgres didn't say - guess it
+
+       pgdirs="/usr /usr/local /usr/local/pgsql /usr/pkg"
+       for d in $pgdirs
+       do
+               if test -f $d/include/libpq-fe.h
+               then
+                       use_dlz_postgres=$d/include
+                       use_dlz_postgres_lib=$d/lib
+                       break
+               fi
+       done
+fi
+
+if test "$use_dlz_postgres" = "yes"
+then
+       # Still no joy, give up
+
+       echo "$as_me:$LINENO: result: not found" >&5
+echo "${ECHO_T}not found" >&6
+       { { echo "$as_me:$LINENO: error: No pg_config and PostgreSQL was not found in any of $pgdirs; use --with-dlz-postgres=/path or put pg_config in your path" >&5
+echo "$as_me: error: No pg_config and PostgreSQL was not found in any of $pgdirs; use --with-dlz-postgres=/path or put pg_config in your path" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+case "$use_dlz_postgres" in
+       no)
+               echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+               ;;
+       *)
+
+       USE_DLZ="$USE_DLZ -DDLZ_POSTGRES"
+       for i in dlz_postgres_driver
+       do
+               DLZ_DRIVER_SRCS="$DLZ_DRIVER_SRCS $dlzdir/$i.c"
+               DLZ_DRIVER_OBJS="$DLZ_DRIVER_OBJS $i.$O"
+       done
+       if test -n "-I$use_dlz_postgres"
+       then
+               DLZ_DRIVER_INCLUDES="$DLZ_DRIVER_INCLUDES -I$use_dlz_postgres"
+       fi
+       if test -n "-L$use_dlz_postgres_lib -lpq"
+       then
+               DLZ_DRIVER_LIBS="$DLZ_DRIVER_LIBS -L$use_dlz_postgres_lib -lpq"
+       fi
+
+
+               echo "$as_me:$LINENO: result: using PostgreSQL from $use_dlz_postgres_lib and $use_dlz_postgres" >&5
+echo "${ECHO_T}using PostgreSQL from $use_dlz_postgres_lib and $use_dlz_postgres" >&6
+               ;;
+esac
+
+
+#
+# Was --with-dlz-mysql specified?
+#
+
+echo "$as_me:$LINENO: checking for MySQL DLZ driver" >&5
+echo $ECHO_N "checking for MySQL DLZ driver... $ECHO_C" >&6
+
+# Check whether --with-dlz_mysql or --without-dlz_mysql was given.
+if test "${with_dlz_mysql+set}" = set; then
+  withval="$with_dlz_mysql"
+  use_dlz_mysql="$withval"
+else
+  use_dlz_mysql="no"
+fi;
+
+if test "$use_dlz_mysql" = "yes"
+then
+       # User did not specify a path - guess it
+       mysqldirs="/usr /usr/local /usr/local/mysql /usr/pkg"
+       for d in $mysqldirs
+       do
+               if test -f $d/include/mysql/mysql.h
+               then
+                       use_dlz_mysql=$d
+                       break
+               fi
+       done
+fi
+
+if test "$use_dlz_mysql" = "yes"
+then
+       echo "$as_me:$LINENO: result: not found" >&5
+echo "${ECHO_T}not found" >&6
+       { { echo "$as_me:$LINENO: error: MySQL was not found in any of $mysqldirs; use --with-dlz-mysql=/path" >&5
+echo "$as_me: error: MySQL was not found in any of $mysqldirs; use --with-dlz-mysql=/path" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+case "$use_dlz_mysql" in
+       no)
+               echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+               ;;
+       *)
+
+       USE_DLZ="$USE_DLZ -DDLZ_MYSQL"
+       for i in dlz_mysql_driver
+       do
+               DLZ_DRIVER_SRCS="$DLZ_DRIVER_SRCS $dlzdir/$i.c"
+               DLZ_DRIVER_OBJS="$DLZ_DRIVER_OBJS $i.$O"
+       done
+       if test -n "-I$use_dlz_mysql/include/mysql"
+       then
+               DLZ_DRIVER_INCLUDES="$DLZ_DRIVER_INCLUDES -I$use_dlz_mysql/include/mysql"
+       fi
+       if test -n "-L$use_dlz_mysql/lib/mysql -lmysqlclient -lz -lcrypt -lm"
+       then
+               DLZ_DRIVER_LIBS="$DLZ_DRIVER_LIBS -L$use_dlz_mysql/lib/mysql -lmysqlclient -lz -lcrypt -lm"
+       fi
+
+
+               echo "$as_me:$LINENO: result: using mysql from $use_dlz_mysql/lib/mysql and $use_dlz_mysql/include/mysql" >&5
+echo "${ECHO_T}using mysql from $use_dlz_mysql/lib/mysql and $use_dlz_mysql/include/mysql" >&6
+               ;;
+esac
+
+
+#
+# Was --with-dlz-bdb specified?
+#
+
+echo "$as_me:$LINENO: checking for Berkeley DB DLZ driver" >&5
+echo $ECHO_N "checking for Berkeley DB DLZ driver... $ECHO_C" >&6
+
+# Check whether --with-dlz_bdb or --without-dlz_bdb was given.
+if test "${with_dlz_bdb+set}" = set; then
+  withval="$with_dlz_bdb"
+  use_dlz_bdb="$withval"
+else
+  use_dlz_bdb="no"
+fi;
+
+case "$use_dlz_bdb" in
+       no)
+               echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+               ;;
+       *)
+               if test "$use_dlz_bdb" = "yes"
+               then
+                       # User did not specify a path - guess directories
+                       bdbdirs="/usr/local /usr/pkg /usr"
+               elif test -d "$use_dlz_bdb"
+               then
+                       # User specified directory and it exists
+                       bdbdirs="$use_dlz_bdb"
+               else
+                       echo "$as_me:$LINENO: result: not found" >&5
+echo "${ECHO_T}not found" >&6
+                       { { echo "$as_me:$LINENO: error: path $use_dlz_bdb does not exist" >&5
+echo "$as_me: error: path $use_dlz_bdb does not exist" >&2;}
+   { (exit 1); exit 1; }; }
+                       bdbdirs=""
+               fi
+
+               # Use path we were given or guessed.  This is insanely
+               # complicated because we have to search for a bunch of
+               # platform-specific variations and have to check
+               # separately for include and library directories.
+
+               # Set both to yes, so we can check them later
+               dlz_bdb_inc="yes"
+               dlz_bdb_libs="yes"
+
+               for dd in $bdbdirs
+               do
+                       # Skip nonexistant directories
+                       if test ! -d "$dd"
+                       then
+                               continue
+                       fi
+
+                       # Check other locations for includes.
+                       # Order is important (sigh).
+
+                       bdb_incdirs="/ /db42/ /db41/ /db4/ /db/"
+                       for d in $bdb_incdirs
+                       do
+                               if test -f "$dd/include${d}db.h"
+                               then
+                                       dlz_bdb_inc="-I$dd/include${d}"
+                                       break
+                               fi
+                       done
+
+                       # Give up on this directory if we couldn't
+                       # find the include subdir
+
+                       if test "$dlz_bdb_inc" = "yes"
+                       then
+                               continue
+                       fi
+
+                       # Look for libname other than libdb.so.
+                       # Order is important (sigh).
+
+                       bdb_libnames="db42 db-4.2 db41 db-4.1 db"
+                       for d in $bdb_libnames
+                       do
+                               if test -f "$dd/lib/lib${d}.so"
+                               then
+                                       if test "$dd" != "/usr"
+                                       then
+                                               dlz_bdb_libs="-L${dd}/lib "
+                                       fi
+                                       dlz_bdb_libs="${dlz_bdb_libs}-l${d}"
+                                       break
+                               fi
+                       done
+
+                       # If we found both incdir and lib, we're done
+                       if test "$dlz_bdb_libs" != "yes"
+                       then
+                               break
+                       fi
+
+                       # Otherwise, we're starting over
+
+                       dlz_bdb_inc="yes"
+                       dlz_bdb_libs="yes"
+               done
+
+               # Done searching, now make sure we got everything.
+
+               if test "$dlz_bdb_inc" = "yes"
+               then
+                       echo "$as_me:$LINENO: result: not found" >&5
+echo "${ECHO_T}not found" >&6
+                       { { echo "$as_me:$LINENO: error: could not find Berkeley DB include directory" >&5
+echo "$as_me: error: could not find Berkeley DB include directory" >&2;}
+   { (exit 1); exit 1; }; }
+               fi
+
+               if test "$dlz_bdb_libs" = "yes"
+               then
+                       echo "$as_me:$LINENO: result: not found" >&5
+echo "${ECHO_T}not found" >&6
+                       { { echo "$as_me:$LINENO: error: could not find Berkeley DB library" >&5
+echo "$as_me: error: could not find Berkeley DB library" >&2;}
+   { (exit 1); exit 1; }; }
+               fi
+
+
+       USE_DLZ="$USE_DLZ -DDLZ_BDB"
+       for i in dlz_bdb_driver dlz_bdbhpt_driver
+       do
+               DLZ_DRIVER_SRCS="$DLZ_DRIVER_SRCS $dlzdir/$i.c"
+               DLZ_DRIVER_OBJS="$DLZ_DRIVER_OBJS $i.$O"
+       done
+       if test -n "$dlz_bdb_inc"
+       then
+               DLZ_DRIVER_INCLUDES="$DLZ_DRIVER_INCLUDES $dlz_bdb_inc"
+       fi
+       if test -n "$dlz_bdb_libs"
+       then
+               DLZ_DRIVER_LIBS="$DLZ_DRIVER_LIBS $dlz_bdb_libs"
+       fi
+
+
+               echo "$as_me:$LINENO: result: using Berkeley DB: $dlz_bdb_inc $dlz_bdb_libs" >&5
+echo "${ECHO_T}using Berkeley DB: $dlz_bdb_inc $dlz_bdb_libs" >&6
+
+                         ac_config_files="$ac_config_files contrib/dlz/bin/dlzbdb/Makefile"
+
+               ;;
+esac
+
+
+#
+# Was --with-dlz-filesystem specified?
+#
+
+echo "$as_me:$LINENO: checking for file system DLZ driver" >&5
+echo $ECHO_N "checking for file system DLZ driver... $ECHO_C" >&6
+
+# Check whether --with-dlz_filesystem or --without-dlz_filesystem was given.
+if test "${with_dlz_filesystem+set}" = set; then
+  withval="$with_dlz_filesystem"
+  use_dlz_filesystem="$withval"
+else
+  use_dlz_filesystem="no"
+fi;
+
+case "$use_dlz_filesystem" in
+       no)
+               echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+               ;;
+       *)
+
+       USE_DLZ="$USE_DLZ -DDLZ_FILESYSTEM"
+       for i in dlz_filesystem_driver
+       do
+               DLZ_DRIVER_SRCS="$DLZ_DRIVER_SRCS $dlzdir/$i.c"
+               DLZ_DRIVER_OBJS="$DLZ_DRIVER_OBJS $i.$O"
+       done
+       if test -n ""
+       then
+               DLZ_DRIVER_INCLUDES="$DLZ_DRIVER_INCLUDES "
+       fi
+       if test -n ""
+       then
+               DLZ_DRIVER_LIBS="$DLZ_DRIVER_LIBS "
+       fi
+
+
+               echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+               ;;
+esac
+
+
+#
+# Was --with-dlz-ldap specified?
+#
+
+echo "$as_me:$LINENO: checking for LDAP DLZ driver" >&5
+echo $ECHO_N "checking for LDAP DLZ driver... $ECHO_C" >&6
+
+# Check whether --with-dlz_ldap or --without-dlz_ldap was given.
+if test "${with_dlz_ldap+set}" = set; then
+  withval="$with_dlz_ldap"
+  use_dlz_ldap="$withval"
+else
+  use_dlz_ldap="no"
+fi;
+
+if test "$use_dlz_ldap" = "yes"
+then
+       # User did not specify a path - guess it
+       ldapdirs="/usr /usr/local /usr/pkg"
+       for d in $ldapdirs
+       do
+               if test -f $d/include/ldap.h
+               then
+                       use_dlz_ldap=$d
+                       break
+               fi
+       done
+fi
+
+if test "$use_dlz_ldap" = "yes"
+then
+       echo "$as_me:$LINENO: result: not found" >&5
+echo "${ECHO_T}not found" >&6
+       { { echo "$as_me:$LINENO: error: LDAP headers were not found in any of $ldapdirs; use --with-dlz-ldap=/path" >&5
+echo "$as_me: error: LDAP headers were not found in any of $ldapdirs; use --with-dlz-ldap=/path" >&2;}
+   { (exit 1); exit 1; }; }
+fi
+
+case "$use_dlz_ldap" in
+       no)
+               echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+               ;;
+       *)
+
+       USE_DLZ="$USE_DLZ -DDLZ_LDAP"
+       for i in dlz_ldap_driver
+       do
+               DLZ_DRIVER_SRCS="$DLZ_DRIVER_SRCS $dlzdir/$i.c"
+               DLZ_DRIVER_OBJS="$DLZ_DRIVER_OBJS $i.$O"
+       done
+       if test -n "-I$use_dlz_ldap/include"
+       then
+               DLZ_DRIVER_INCLUDES="$DLZ_DRIVER_INCLUDES -I$use_dlz_ldap/include"
+       fi
+       if test -n "-L$use_dlz_ldap/lib -lldap -llber"
+       then
+               DLZ_DRIVER_LIBS="$DLZ_DRIVER_LIBS -L$use_dlz_ldap/lib -lldap -llber"
+       fi
+
+
+               echo "$as_me:$LINENO: result: using LDAP from $use_dlz_ldap/lib and $use_dlz_ldap/include" >&5
+echo "${ECHO_T}using LDAP from $use_dlz_ldap/lib and $use_dlz_ldap/include" >&6
+               ;;
+esac
+
+
+#
+# Was --with-dlz-odbc specified?
+#
+
+echo "$as_me:$LINENO: checking for ODBC DLZ driver" >&5
+echo $ECHO_N "checking for ODBC DLZ driver... $ECHO_C" >&6
+
+# Check whether --with-dlz_odbc or --without-dlz_odbc was given.
+if test "${with_dlz_odbc+set}" = set; then
+  withval="$with_dlz_odbc"
+  use_dlz_odbc="$withval"
+else
+  use_dlz_odbc="no"
+fi;
+
+if test "$use_dlz_odbc" = "yes"
+then
+       # User did not specify a path - guess it
+       odbcdirs="/usr /usr/local /usr/pkg"
+       for d in $odbcdirs
+       do
+               if test -f $d/include/sql.h -a -f $d/lib/libodbc.a
+               then
+                       use_dlz_odbc=$d
+                       break
+               fi
+       done
+fi
+
+case "$use_dlz_odbc" in
+       no)
+               echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+               ;;
+       yes)
+               echo "$as_me:$LINENO: result: not found" >&5
+echo "${ECHO_T}not found" >&6
+               { { echo "$as_me:$LINENO: error: ODBC headers were not found in any of $odbcdirs; use --with-dlz-odbc=/path" >&5
+echo "$as_me: error: ODBC headers were not found in any of $odbcdirs; use --with-dlz-odbc=/path" >&2;}
+   { (exit 1); exit 1; }; }
+               ;;
+       *)
+
+       USE_DLZ="$USE_DLZ -DDLZ_ODBC"
+       for i in dlz_odbc_driver
+       do
+               DLZ_DRIVER_SRCS="$DLZ_DRIVER_SRCS $dlzdir/$i.c"
+               DLZ_DRIVER_OBJS="$DLZ_DRIVER_OBJS $i.$O"
+       done
+       if test -n "-I$use_dlz_odbc/include"
+       then
+               DLZ_DRIVER_INCLUDES="$DLZ_DRIVER_INCLUDES -I$use_dlz_odbc/include"
+       fi
+       if test -n "-L$use_dlz_odbc/lib -lodbc"
+       then
+               DLZ_DRIVER_LIBS="$DLZ_DRIVER_LIBS -L$use_dlz_odbc/lib -lodbc"
+       fi
+
+
+               echo "$as_me:$LINENO: result: using ODBC from $use_dlz_odbc" >&5
+echo "${ECHO_T}using ODBC from $use_dlz_odbc" >&6
+               ;;
+esac
+
+
+#
+# Was --with-dlz-stub specified?
+#
+
+echo "$as_me:$LINENO: checking for stub DLZ driver" >&5
+echo $ECHO_N "checking for stub DLZ driver... $ECHO_C" >&6
+
+# Check whether --with-dlz_stub or --without-dlz_stub was given.
+if test "${with_dlz_stub+set}" = set; then
+  withval="$with_dlz_stub"
+  use_dlz_stub="$withval"
+else
+  use_dlz_stub="no"
+fi;
+
+case "$use_dlz_stub" in
+       no)
+               echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+               ;;
+       *)
+
+
+       USE_DLZ="$USE_DLZ -DDLZ_STUB"
+       for i in dlz_stub_driver
+       do
+               DLZ_DRIVER_SRCS="$DLZ_DRIVER_SRCS $dlzdir/$i.c"
+               DLZ_DRIVER_OBJS="$DLZ_DRIVER_OBJS $i.$O"
+       done
+       if test -n ""
+       then
+               DLZ_DRIVER_INCLUDES="$DLZ_DRIVER_INCLUDES "
+       fi
+       if test -n ""
+       then
+               DLZ_DRIVER_LIBS="$DLZ_DRIVER_LIBS "
+       fi
+
+
+               echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+               ;;
+esac
+
+
+# Add any additional DLZ drivers here.
+
+#
+# Finally, some generic stuff that applies to all drivers, assuming
+# we're compiling DLZ at all.
+#
+if test -n "$USE_DLZ"
+then
+       #
+       # Where to find DLZ driver header files.
+       #
+       DLZ_DRIVER_INCLUDES="-I$dlzdir/include $DLZ_DRIVER_INCLUDES"
+
+       #
+       # Initialization and shutdown wrappers, helper functions.
+       #
+       DLZ_DRIVER_SRCS="$dlzdir/dlz_drivers.c $dlzdir/sdlz_helper.c $DLZ_DRIVER_SRCS"
+       DLZ_DRIVER_OBJS="dlz_drivers.$O sdlz_helper.$O $DLZ_DRIVER_OBJS"
+fi
+
+
+echo "$as_me:$LINENO: checking for DLZ" >&5
+echo $ECHO_N "checking for DLZ... $ECHO_C" >&6
+
+if test -n "$USE_DLZ"
+then
+       echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+       USE_DLZ="-DDLZ $USE_DLZ"
+       DLZ_DRIVER_RULES=contrib/dlz/drivers/rules
+                 ac_config_files="$ac_config_files $DLZ_DRIVER_RULES"
+
+else
+       echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+       DLZ_DRIVER_RULES=/dev/null
+fi
+
+
+
+
+
+
+
+
+
 #
 # Commands to run at the end of config.status.
 # Don't just put these into configure, it won't work right if somebody
@@ -28399,11 +29090,19 @@ LIBLWRES_API=$srcdir/lib/lwres/api
           ac_config_commands="$ac_config_commands chmod"
 
 
+#
+# Files to configure.  These are listed here because we used to
+# specify them as arguments to AC_OUTPUT.  It's (now) ok to move these
+# elsewhere if there's a good reason for doing so.
+#
+
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    ac_config_files="$ac_config_files Makefile make/Makefile make/mkdep lib/Makefile lib/isc/Makefile lib/isc/include/Makefile lib/isc/include/isc/Makefile lib/isc/include/isc/platform.h lib/isc/unix/Makefile lib/isc/unix/include/Makefile lib/isc/unix/include/isc/Makefile lib/isc/nls/Makefile lib/isc/$thread_dir/Makefile lib/isc/$thread_dir/include/Makefile lib/isc/$thread_dir/include/isc/Makefile lib/isccc/Makefile lib/isccc/include/Makefile lib/isccc/include/isccc/Makefile lib/isccfg/Makefile lib/isccfg/include/Makefile lib/isccfg/include/isccfg/Makefile lib/dns/Makefile lib/dns/include/Makefile lib/dns/include/dns/Makefile lib/dns/include/dst/Makefile lib/bind9/Makefile lib/bind9/include/Makefile lib/bind9/include/bind9/Makefile lib/lwres/Makefile lib/lwres/include/Makefile lib/lwres/include/lwres/Makefile lib/lwres/include/lwres/netdb.h lib/lwres/include/lwres/platform.h lib/lwres/man/Makefile lib/lwres/unix/Makefile lib/lwres/unix/include/Makefile lib/lwres/unix/include/lwres/Makefile lib/tests/Makefile lib/tests/include/Makefile lib/tests/include/tests/Makefile bin/Makefile bin/check/Makefile bin/named/Makefile bin/named/unix/Makefile bin/rndc/Makefile bin/rndc/unix/Makefile bin/dig/Makefile bin/nsupdate/Makefile bin/tests/Makefile bin/tests/names/Makefile bin/tests/master/Makefile bin/tests/rbt/Makefile bin/tests/db/Makefile bin/tests/tasks/Makefile bin/tests/timers/Makefile bin/tests/dst/Makefile bin/tests/mem/Makefile bin/tests/net/Makefile bin/tests/sockaddr/Makefile bin/tests/system/Makefile bin/tests/system/conf.sh bin/tests/system/lwresd/Makefile bin/tests/system/tkey/Makefile bin/tests/headerdep_test.sh bin/dnssec/Makefile doc/Makefile doc/arm/Makefile doc/misc/Makefile isc-config.sh doc/xsl/Makefile doc/xsl/isc-docbook-chunk.xsl doc/xsl/isc-docbook-html.xsl doc/xsl/isc-docbook-latex.xsl doc/xsl/isc-manpage.xsl"
+
+
 #
 # Do it
 #
 
-                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        ac_config_files="$ac_config_files make/rules make/includes Makefile make/Makefile make/mkdep lib/Makefile lib/isc/Makefile lib/isc/include/Makefile lib/isc/include/isc/Makefile lib/isc/include/isc/platform.h lib/isc/unix/Makefile lib/isc/unix/include/Makefile lib/isc/unix/include/isc/Makefile lib/isc/nls/Makefile lib/isc/$thread_dir/Makefile lib/isc/$thread_dir/include/Makefile lib/isc/$thread_dir/include/isc/Makefile lib/isccc/Makefile lib/isccc/include/Makefile lib/isccc/include/isccc/Makefile lib/isccfg/Makefile lib/isccfg/include/Makefile lib/isccfg/include/isccfg/Makefile lib/dns/Makefile lib/dns/include/Makefile lib/dns/include/dns/Makefile lib/dns/include/dst/Makefile lib/bind9/Makefile lib/bind9/include/Makefile lib/bind9/include/bind9/Makefile lib/lwres/Makefile lib/lwres/include/Makefile lib/lwres/include/lwres/Makefile lib/lwres/include/lwres/netdb.h lib/lwres/include/lwres/platform.h lib/lwres/man/Makefile lib/lwres/unix/Makefile lib/lwres/unix/include/Makefile lib/lwres/unix/include/lwres/Makefile lib/tests/Makefile lib/tests/include/Makefile lib/tests/include/tests/Makefile bin/Makefile bin/check/Makefile bin/named/Makefile bin/named/unix/Makefile bin/rndc/Makefile bin/rndc/unix/Makefile bin/dig/Makefile bin/nsupdate/Makefile bin/tests/Makefile bin/tests/names/Makefile bin/tests/master/Makefile bin/tests/rbt/Makefile bin/tests/db/Makefile bin/tests/tasks/Makefile bin/tests/timers/Makefile bin/tests/dst/Makefile bin/tests/mem/Makefile bin/tests/net/Makefile bin/tests/sockaddr/Makefile bin/tests/system/Makefile bin/tests/system/conf.sh bin/tests/system/lwresd/Makefile bin/tests/system/tkey/Makefile bin/tests/headerdep_test.sh bin/dnssec/Makefile doc/Makefile doc/arm/Makefile doc/misc/Makefile isc-config.sh doc/xsl/Makefile doc/xsl/isc-docbook-chunk.xsl doc/xsl/isc-docbook-html.xsl doc/xsl/isc-docbook-latex.xsl doc/xsl/isc-manpage.xsl"
 cat >confcache <<\_ACEOF
 # This file is a shell script that caches the results of configure
 # tests run on this system so they can be shared between configure
@@ -28934,6 +29633,8 @@ do
   # Handling of arguments.
   "make/rules" ) CONFIG_FILES="$CONFIG_FILES make/rules" ;;
   "make/includes" ) CONFIG_FILES="$CONFIG_FILES make/includes" ;;
+  "contrib/dlz/bin/dlzbdb/Makefile" ) CONFIG_FILES="$CONFIG_FILES contrib/dlz/bin/dlzbdb/Makefile" ;;
+  "$DLZ_DRIVER_RULES" ) CONFIG_FILES="$CONFIG_FILES $DLZ_DRIVER_RULES" ;;
   "Makefile" ) CONFIG_FILES="$CONFIG_FILES Makefile" ;;
   "make/Makefile" ) CONFIG_FILES="$CONFIG_FILES make/Makefile" ;;
   "make/mkdep" ) CONFIG_FILES="$CONFIG_FILES make/mkdep" ;;
@@ -29263,6 +29964,12 @@ s,@BIND9_DNS_BUILDINCLUDE@,$BIND9_DNS_BUILDINCLUDE,;t t
 s,@BIND9_LWRES_BUILDINCLUDE@,$BIND9_LWRES_BUILDINCLUDE,;t t
 s,@BIND9_BIND9_BUILDINCLUDE@,$BIND9_BIND9_BUILDINCLUDE,;t t
 s,@BIND9_VERSION@,$BIND9_VERSION,;t t
+s,@PG_CONFIG@,$PG_CONFIG,;t t
+s,@USE_DLZ@,$USE_DLZ,;t t
+s,@DLZ_DRIVER_INCLUDES@,$DLZ_DRIVER_INCLUDES,;t t
+s,@DLZ_DRIVER_LIBS@,$DLZ_DRIVER_LIBS,;t t
+s,@DLZ_DRIVER_SRCS@,$DLZ_DRIVER_SRCS,;t t
+s,@DLZ_DRIVER_OBJS@,$DLZ_DRIVER_OBJS,;t t
 s,@LIBOBJS@,$LIBOBJS,;t t
 s,@LTLIBOBJS@,$LTLIBOBJS,;t t
 /@BIND9_MAKE_INCLUDES@/r $BIND9_MAKE_INCLUDES
@@ -29281,6 +29988,8 @@ s,@LIBDNS_API@,,;t t
 s,@LIBBIND9_API@,,;t t
 /@LIBLWRES_API@/r $LIBLWRES_API
 s,@LIBLWRES_API@,,;t t
+/@DLZ_DRIVER_RULES@/r $DLZ_DRIVER_RULES
+s,@DLZ_DRIVER_RULES@,,;t t
 CEOF
 
 _ACEOF
index e8da02eef621e59cf0eb89f3fa47b79cf41edc96..e1f883a68b5a2d9fdb0edf29fb963821f0004a7a 100644 (file)
@@ -18,10 +18,10 @@ AC_DIVERT_PUSH(1)dnl
 esyscmd([sed "s/^/# /" COPYRIGHT])dnl
 AC_DIVERT_POP()dnl
 
-AC_REVISION($Revision: 1.391 $)
+AC_REVISION($Revision: 1.392 $)
 
 AC_INIT(lib/dns/name.c)
-AC_PREREQ(2.13)
+AC_PREREQ(2.59)
 
 AC_CONFIG_HEADER(config.h)
 AC_CONFIG_SUBDIRS(lib/bind)
@@ -38,6 +38,13 @@ AC_SUBST(STD_CDEFINES)
 AC_SUBST(STD_CWARNINGS)
 AC_SUBST(CCOPT)
 
+#
+# Make very sure that these are the first files processed by
+# config.status, since we use the processed output as the input for
+# AC_SUBST_FILE() subsitutions in other files.
+#
+AC_CONFIG_FILES([make/rules make/includes])
+
 AC_PATH_PROG(AR, ar)
 ARFLAGS="cruv"
 AC_SUBST(AR)
@@ -355,6 +362,7 @@ AC_SUBST(LWRES_PLATFORM_NEEDSYSSELECTH)
 #
 AC_C_BIGENDIAN
 
+
 #
 # was --with-openssl specified?
 #
@@ -979,6 +987,7 @@ case "$enable_libbind" in
                ;;
 esac
 
+
 #
 # Here begins a very long section to determine the system's networking
 # capabilities.  The order of the tests is signficant.
@@ -2180,6 +2189,46 @@ LIBBIND9_API=$srcdir/lib/bind9/api
 AC_SUBST_FILE(LIBLWRES_API)
 LIBLWRES_API=$srcdir/lib/lwres/api
 
+#
+# Configure any DLZ drivers.
+#
+# If config.dlz.in selects one or more DLZ drivers, it will set
+# USE_DLZ to a non-empty value, which will be our clue to
+# enable the DLZ core functions.
+#
+# This section has to come after the libtool stuff because it needs to
+# know how to name the driver object files.
+#
+
+USE_DLZ=""
+DLZ_DRIVER_INCLUDES=""
+DLZ_DRIVER_LIBS=""
+DLZ_DRIVER_SRCS=""
+DLZ_DRIVER_OBJS=""
+
+sinclude(contrib/dlz/config.dlz.in)
+
+AC_MSG_CHECKING(for DLZ)
+
+if test -n "$USE_DLZ"
+then
+       AC_MSG_RESULT(yes)
+       USE_DLZ="-DDLZ $USE_DLZ"
+       DLZ_DRIVER_RULES=contrib/dlz/drivers/rules
+       AC_CONFIG_FILES([$DLZ_DRIVER_RULES])
+else
+       AC_MSG_RESULT(no)
+       DLZ_DRIVER_RULES=/dev/null
+fi
+
+AC_SUBST(USE_DLZ)
+AC_SUBST(DLZ_DRIVER_INCLUDES)
+AC_SUBST(DLZ_DRIVER_LIBS)
+AC_SUBST(DLZ_DRIVER_SRCS)
+AC_SUBST(DLZ_DRIVER_OBJS)
+AC_SUBST_FILE(DLZ_DRIVER_RULES)
+
+
 #
 # Commands to run at the end of config.status.
 # Don't just put these into configure, it won't work right if somebody
@@ -2191,12 +2240,12 @@ AC_CONFIG_COMMANDS(
        [chmod a+x isc-config.sh])
 
 #
-# Do it
+# Files to configure.  These are listed here because we used to
+# specify them as arguments to AC_OUTPUT.  It's (now) ok to move these
+# elsewhere if there's a good reason for doing so.
 #
 
-AC_OUTPUT(
-       make/rules
-       make/includes
+AC_CONFIG_FILES([
        Makefile
        make/Makefile
        make/mkdep
@@ -2271,7 +2320,13 @@ AC_OUTPUT(
        doc/xsl/isc-docbook-html.xsl
        doc/xsl/isc-docbook-latex.xsl
        doc/xsl/isc-manpage.xsl
-)
+])
+
+#
+# Do it
+#
+
+AC_OUTPUT
 
 # Tell Emacs to edit this file in shell mode.
 # Local Variables:
diff --git a/contrib/dlz/bin/dlzbdb/.cvsignore b/contrib/dlz/bin/dlzbdb/.cvsignore
new file mode 100644 (file)
index 0000000..5e57bac
--- /dev/null
@@ -0,0 +1,3 @@
+Makefile
+dlzbdb
+*.o
diff --git a/contrib/dlz/bin/dlzbdb/Makefile.in b/contrib/dlz/bin/dlzbdb/Makefile.in
new file mode 100644 (file)
index 0000000..140ce62
--- /dev/null
@@ -0,0 +1,73 @@
+# Copyright (C) 1998-2001  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.2 2005/09/05 00:10:54 marka Exp $
+
+srcdir =       @srcdir@
+VPATH =                @srcdir@
+top_srcdir =   @top_srcdir@
+
+@BIND9_VERSION@
+
+@BIND9_MAKE_INCLUDES@
+
+DLZINCLUDES =  @DLZ_BDB_INC@
+
+CINCLUDES =    -I${srcdir}/include -I${srcdir}/unix/include \
+                ${ISC_INCLUDES} ${DLZINCLUDES}
+                
+CDEFINES =      @USE_DLZ_BDB@
+CWARNINGS =
+
+DLZLIBS =      @DLZ_BDB_LIBS@
+ISCLIBS =      ../../lib/isc/libisc.@A@
+
+DEPLIBS =      ${ISCDEPLIBS}
+
+LIBS =         ${ISCLIBS} ${DLZLIBS} @LIBS@
+
+TARGETS =      dlzbdb
+
+SRCS =         dlzbdb.c
+
+#MANPAGES =    
+
+#HTMLPAGES =   
+
+#MANOBJS =     ${MANPAGES} ${HTMLPAGES}
+
+@BIND9_MAKE_RULES@
+
+dlzbdb.@O@: dlzbdb.c
+       ${LIBTOOL} ${CC} ${ALL_CFLAGS} -DVERSION=\"${VERSION}\" \
+               -c ${srcdir}/dlzbdb.c
+
+dlzbdb: dlzbdb.@O@ ${DEPLIBS}
+       ${LIBTOOL} ${PURIFY} ${CC} ${CFLAGS} -o $@ dlzbdb.@O@ ${LIBS}
+
+doc man:: ${MANOBJS}
+
+#docclean manclean maintainer-clean::
+#      rm -f ${MANOBJS}
+
+clean distclean maintainer-clean::
+       rm -f ${TARGETS}
+
+installdirs:
+       $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${sbindir}
+#      $(SHELL) ${top_srcdir}/mkinstalldirs ${DESTDIR}${mandir}/man8
+
+install:: dlzbdb installdirs
+       ${LIBTOOL} ${INSTALL_PROGRAM} dlzbdb ${DESTDIR}${sbindir}
diff --git a/contrib/dlz/bin/dlzbdb/dlzbdb.c b/contrib/dlz/bin/dlzbdb/dlzbdb.c
new file mode 100644 (file)
index 0000000..aedc953
--- /dev/null
@@ -0,0 +1,1273 @@
+/*
+ * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
+ * 
+ * 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 STICHTING NLNET
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * STICHTING NLNET 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 development of Dynamically Loadable Zones (DLZ) for Bind 9 was
+ * conceived and contributed by Rob Butler.
+ * 
+ * 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 ROB BUTLER
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * ROB BUTLER 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.
+ */
+
+/*
+ * Copyright (C) 1999-2001  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.
+ */
+
+#ifdef DLZ_BDB
+
+/*
+ * exit codes
+ * 0 everything ok
+ * 1 error parsing command line
+ * 2 Missing, too many or invalid combination of command line parameters
+ * 3 Unable to open BDB database.
+ * 4 Unable to allocate memory for, or create lexer.
+ * 5 unable to perform BDB cursor operation
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <isc/buffer.h>
+#include <isc/commandline.h>
+#include <isc/formatcheck.h>
+#include <isc/lex.h>
+#include <isc/mem.h>
+#include <isc/result.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <db.h>
+
+/* shut up compiler warnings about no previous prototype */
+
+static void
+show_usage(void);
+
+int
+getzone(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey);
+
+int
+gethost(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey);
+
+void
+bdb_cleanup(void);
+
+isc_result_t
+bdb_opendb(DBTYPE db_type, DB **db_out, const char *db_name, int flags);
+
+void
+put_data(isc_boolean_t dns_data, char *input_key, char *input_data);
+
+void
+insert_data(void);
+
+isc_result_t
+openBDB(void);
+
+isc_result_t
+open_lexer(void);
+
+void
+close_lexer(void);
+
+isc_result_t
+bulk_write(char type, DB *database, DBC *dbcursor, DBT *bdbkey, DBT *bdbdata);
+
+void
+operation_add(void);
+
+void
+operation_bulk(void);
+
+void
+operation_listOrDelete(isc_boolean_t dlt);
+
+                                             
+/*%
+ * Maximum length of a single data line that 
+ * may be inserted into database by this program.
+ * If you need to insert a line of data that is more
+ * than 10,000 characters change this definition.
+ */
+
+#define max_data_len 10000
+
+/*%
+ * BDB database names.  If you want to use different
+ * database names change them here. 
+ */
+
+#define dlz_data "dns_data"
+#define dlz_zone "dns_zone"
+#define dlz_host "dns_host"
+#define dlz_client "dns_client"
+
+
+/*%
+ * Error code returned by BDB secondary index callback functions.
+ * This error is returned if the callback function could not create
+ * the secondary index for any reason.
+ */
+
+#define BDBparseErr 1
+
+/* A struct to hold all the relevant info about the database */
+
+typedef struct bdb_instance {
+       DB_ENV  *dbenv;         /* BDB environment */
+       DB      *data;          /* dns_data database handle */
+       DBC     *cursor;        /* database cursor */
+       DBC     *cursor2; /* second cursor used during list operation. */
+       DBC     *cursor3; /* third cursor used during list operation */
+       DBC     *cursor4; /* fourth cursor used during list operation */
+       DB      *zone;          /* zone database handle */
+       DB      *host;          /* host database handle */
+       DB      *client;        /* client database handle */
+} bdb_instance_t;
+
+/* Possible operations */
+
+#define list 1         /* list data */
+#define dele 2         /* delete data */
+#define add 3          /* add a single piece of data */
+#define bulk 4         /* bulk load data */
+
+
+/*%
+ * quit macro is used instead of exit.  quit always trys to close the lexer
+ * and the BDB database before exiting.
+ */
+
+#define quit(i)        close_lexer(); bdb_cleanup(); exit(i);
+
+/*%
+ * checkOp is used to verify that only one operation (list, del, add,
+ * bulk from file, bulk from stdin) is specified on the command line.
+ * This prevents a user from specifying two operations on the command
+ * line, which would make no sense anyway.
+ */
+
+#define checkOp(x) if (x != 0) {fprintf(stderr, "\nonly one operation "\
+                               "(l e d a f s) may be specified\n"); quit(2);}
+
+/*%
+ * checkParam is used to only allow a parameter to be specified once.
+ * I.E. the parameter key can only be used on the command line once.
+ * any attempt to use it twice causes an error.
+ */
+
+#define checkParam(x, y) if (x != NULL) {fprintf(stderr, "\n%s may only "\
+                                        "be specified once\n", y); quit(2);}
+
+/*%
+ * checkInvalidParam is used to only allow paramters which make sense for
+ * the operation selected.  I.E. passing the key parameter makes no sense
+ * for the add operation, and thus it isn't allowed.
+ */
+
+#define checkInvalidParam(x, y, z) if (x != NULL) {fprintf(stderr, "\n%s "\
+                                               "may not be specified %s\n", y, z); quit(2);}
+
+/*%
+ * checkInvalidOption is used to only allow paramters which make sense for
+ * the operation selected - but checks boolean options.
+ * I.E. passing the "b" bare_list parameter makes no sense for the add
+ * operation, and thus it isn't allowed.
+ * if w == x then output error message "flag", "message"
+ */
+
+#define checkInvalidOption(w, x, y, z) if (w == x) {fprintf(stderr, "\n%s "\
+                                               "may not be specified %s\n", y, z); quit(2);}
+
+/* Global Variables */
+
+int operation = 0;             /*%< operation to perform. */
+/*% allow new lock files or DB to be created. */
+isc_boolean_t create_allowed = isc_boolean_false;
+char *key = NULL;              /*%< key to use in list & del operations */
+
+/*% dump DB in DLZBDB bulk format */
+isc_boolean_t list_everything = isc_boolean_false;     
+unsigned int key_val; /*%< key as unsigned int used in list & del operations */
+char *zone = NULL;             /*%< zone to use in list operations */
+char *host = NULL;             /*%< host to use in list operations */
+char *c_zone = NULL;    /*%< client zone to use in list operations */
+char *c_ip = NULL;        /*%< client IP to use in list operations */
+char *a_data = NULL;           /*%< data in add operation */
+char *bulk_file = NULL;                /*%< bulk data file to load */
+char *db_envdir = NULL;                /*%< BDB environment location  */
+char *db_file = NULL;          /*%< BDB database file location. */
+bdb_instance_t db;             /* BDB instance we are operating on */
+isc_lex_t *lexer = NULL; /*%< lexer for use to use in parsing input */
+isc_mem_t *lex_mctx = NULL;    /*%< memory context for lexer */
+char lex_data_buf[max_data_len]; /*%< data array to use for lex_buffer below */
+isc_buffer_t lex_buffer;   /*%< buffer for lexer during add operation */
+
+/*%
+ * Displays usage message
+ */
+
+static void
+show_usage(void) {
+       fprintf(stderr, "\n\n\
+---Usage:---------------------------------------------------------------------\
+\n\n\
+   List data:\n\
+      dlzbdb -l [-k key] [-z zone] [-h host] [-c client_zone] [-i client_ip]\n\
+               BDB_environment BDB_database\n\n\
+   Delete data:\n\
+      dlzbdb -d [-k key] [-c client_zone] [-i client_ip]\n\
+               BDB_environment BDB_database\n\n\
+   Bulk load data from file:\n\
+      dlzbdb -f file_to_load BDB_environment BDB_database\n\n\
+   Bulk load data from stdin\n\
+      dlzbdb -s BDB_environment BDB_database\n\n\
+   Add data:\n\
+      dlzbdb -a \"dns data to be added\" BDB_environment BDB_database\n\n\
+   Export data:\n\
+      dlzbdb -e BDB_environment BDB_database\n\n\
+   Normally operations can only be performed on an existing database files.\n\
+   Use the -n flag with any operation to allow files to be created.\n\
+   Existing files will NOT be truncated by using the -n flag.\n\
+   The -n flag will allow a new database to be created, or allow new\n\
+   environment files to be created for an existing database.\n\n\
+---Format for -f & -a options:------------------------------------------------\
+\n\n\
+db_type zone host dns_type ttl ip\n\
+db_type zone host dns_type ttl mx_priority mail_host\n\
+db_type zone host dns_type ttl nm_svr resp_psn serial refresh retry expire min\
+\n\
+db_type zone client_ip\n\n\
+---Examples:------------------------------------------------------------------\
+\n\n\
+d mynm.com www A 10 127.0.0.1\n\
+d mynm.com @ MX 10 5 mail\n\
+d mynm.com @ SOA 10 ns1.mynm.com. root.mynm.com. 2 28800 7200 604800 86400\n\
+c mynm.com 127.0.0.1\n\
+c mynm.com 192.168.0.10\n\
+");
+quit(1);
+}
+
+
+/*% BDB callback to create zone secondary index */
+
+int
+getzone(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey) {
+       char *tmp;
+       char *left;
+       char *right;
+       int result=0;
+
+       UNUSED(dbp);
+       UNUSED(pkey);
+
+       /* Allocate memory to use in parsing the string */
+       tmp = right = malloc(pdata->size + 1);
+
+       /* verify memory was allocated */
+       if (right == NULL) {
+               result = BDBparseErr;
+               goto getzone_cleanup;
+       }               
+
+       /* copy data string into newly allocated memory */
+       strncpy(right, pdata->data, pdata->size);
+       right[pdata->size] = '\0';
+
+       /* split string at the first space */
+       left = isc_string_separate(&right, " ");
+
+       /* copy string for "zone" secondary index */
+       skey->data = strdup(left);
+       if (skey->data == NULL) {
+               result = BDBparseErr;
+               goto getzone_cleanup;
+       }
+       /* set required values for BDB */
+       skey->size = strlen(skey->data);
+       skey->flags = DB_DBT_APPMALLOC;
+
+ getzone_cleanup:
+
+       /* cleanup memory */
+       if (tmp != NULL)
+               free(tmp);
+       
+       return result;
+}
+
+/*%
+ * BDB callback to create host secondary index
+ */
+
+int
+gethost(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey) {
+       char *tmp;
+       char *left;
+       char *right;
+       int result=0;
+
+       UNUSED(dbp);
+       UNUSED(pkey);
+
+       /* allocate memory to use in parsing the string */
+       tmp = right = malloc(pdata->size + 1);
+
+       /* verify memory was allocated */
+       if (tmp == NULL) {
+               result = BDBparseErr;
+               goto gethost_cleanup;
+       }               
+
+       /* copy data string into newly allocated memory */
+       strncpy(right, pdata->data, pdata->size);
+       right[pdata->size] = '\0';
+
+       /* we don't care about left string. */
+       /* memory of left string will be freed when tmp is freed. */
+       isc_string_separate(&right, " ");
+
+       /* verify right still has some characters left */
+       if (right == NULL) {
+               result = BDBparseErr;
+               goto gethost_cleanup;
+       }
+
+       /* get "host" from data string */
+       left = isc_string_separate(&right, " ");
+       /* copy string for "host" secondary index */
+       skey->data = strdup(left);
+       if (skey->data == NULL) {
+               result = BDBparseErr;
+               goto gethost_cleanup;
+       }
+       /* set required values for BDB */
+       skey->size = strlen(skey->data);
+       skey->flags = DB_DBT_APPMALLOC;
+
+ gethost_cleanup:
+
+       /* cleanup memory */
+       if (tmp != NULL)
+               free(tmp);
+       
+       return result;
+}
+
+/*%
+ * Performs BDB cleanup. Close each database that we opened.
+ * Close environment.  Set each var to NULL so we know they
+ * were closed and don't accidentally try to close them twice.
+ */
+
+void
+bdb_cleanup(void) {
+
+       /* close cursors */
+       if (db.cursor4 != NULL) {
+               db.cursor4->c_close(db.cursor4);
+               db.cursor4 = NULL;
+       }
+
+       if (db.cursor3 != NULL) {
+               db.cursor3->c_close(db.cursor3);
+               db.cursor3 = NULL;
+       }
+
+       if (db.cursor2 != NULL) {
+               db.cursor2->c_close(db.cursor2);
+               db.cursor2 = NULL;
+       }
+
+       if (db.cursor != NULL) {
+               db.cursor->c_close(db.cursor);
+               db.cursor = NULL;
+       }
+
+       /* close databases */
+       if (db.data != NULL) {
+               db.data->close(db.data, 0);
+               db.data = NULL;
+       }
+       if (db.host != NULL) {
+               db.host->close(db.host, 0);
+               db.host = NULL;
+       }
+       if (db.zone != NULL) {
+               db.zone->close(db.zone, 0);
+               db.zone = NULL;
+       }
+       if (db.client != NULL) {
+               db.client->close(db.client, 0);
+               db.client = NULL;
+       }
+
+       /* close environment */
+       if (db.dbenv != NULL) {
+               db.dbenv->close(db.dbenv, 0);
+               db.dbenv = NULL;
+       }
+}
+
+/*% Initializes, sets flags and then opens Berkeley databases. */
+
+isc_result_t
+bdb_opendb(DBTYPE db_type, DB **db_out, const char *db_name, int flags) {
+
+       int result;
+       int createFlag = 0;
+
+       /* Initialize the database. */
+       if ((result = db_create(db_out, db.dbenv, 0)) != 0) {
+               fprintf(stderr, "BDB could not initialize %s database. BDB error: %s",
+                       db_name, db_strerror(result));
+               return ISC_R_FAILURE;
+       }
+
+       /* set database flags. */
+       if ((result = (*db_out)->set_flags(*db_out, flags)) != 0) {
+               fprintf(stderr, "BDB could not set flags for %s database. BDB error: %s",
+                       db_name, db_strerror(result));
+               return ISC_R_FAILURE;
+       }
+
+       if (create_allowed == isc_boolean_true) {
+               createFlag = DB_CREATE;
+       }
+       /* open the database. */
+       if ((result = (*db_out)->open(*db_out, NULL, db_file, db_name, db_type,
+                                     createFlag, 0)) != 0) {
+               fprintf(stderr, "BDB could not open %s database in %s. BDB error: %s",
+                       db_name, db_file, db_strerror(result));
+               return ISC_R_FAILURE;
+       }
+
+       return ISC_R_SUCCESS;
+}
+
+/*%
+ * parses input and adds it to the BDB database
+ * Lexer should be instantiated, and either a file or buffer opened for it.
+ * The insert_data function is used by both the add, and bulk insert
+ * operations
+ */
+
+void
+put_data(isc_boolean_t dns_data, char *input_key, char *input_data) {
+
+       int bdbres;
+       DBT key, data;
+
+       /* make sure key & data are completely empty */
+       memset(&key, 0, sizeof(key));
+       memset(&data, 0, sizeof(data));
+
+       /* if client data, setup key for insertion */
+       if (!dns_data && input_key != NULL) {
+               key.data = input_key;
+               key.size = strlen(input_key);
+               key.flags = 0;
+       }
+       /* always setup data for insertion */
+       data.data = input_data;
+       data.size = strlen(input_data);
+       data.flags = 0;
+
+       /* execute insert against appropriate database. */
+       if (dns_data) {
+               bdbres = db.data->put(db.data, NULL, &key, &data, DB_APPEND);
+       } else {
+               bdbres = db.client->put(db.client, NULL, &key, &data, 0);
+       }
+
+       /* if something went wrong, log error and quit */
+       if (bdbres != 0) {
+               fprintf(stderr, "BDB could not insert data.  Error: %s",
+                       db_strerror(bdbres));
+               quit(5);
+       }
+}
+
+void
+insert_data(void) {
+       unsigned int opt =
+               ISC_LEXOPT_EOL |                /* Want end-of-line token. */
+               ISC_LEXOPT_EOF |                /* Want end-of-file token. */
+               ISC_LEXOPT_QSTRING |            /* Recognize qstrings. */
+               ISC_LEXOPT_QSTRINGMULTILINE;    /* Allow multiline "" strings */
+
+       isc_result_t result;
+       isc_token_t token;      /* token from lexer */
+       isc_boolean_t loop = isc_boolean_true;
+       isc_boolean_t have_czone = isc_boolean_false;
+       char data_arr[max_data_len];
+       isc_buffer_t buf;
+       char data_arr2[max_data_len];
+       isc_buffer_t buf2;
+       char data_type = 'u'; /* u =unknown, b =bad token, d/D =DNS, c/C =client IP */
+
+       /* Initialize buffers */
+       isc_buffer_init(&buf, &data_arr, max_data_len);
+       isc_buffer_init(&buf2, &data_arr2, max_data_len);
+
+       while (loop) {
+               result = isc_lex_gettoken(lexer, opt, &token); 
+               if (result != ISC_R_SUCCESS) 
+                       goto data_cleanup;
+
+               switch(token.type) {
+               case isc_tokentype_string:
+                       if (data_type == 'u') {
+                               /* store data_type */
+                               strncpy(&data_type, token.value.as_pointer, 1);
+                               /* verify data_type was specified correctly on input */
+                               if (strlen(token.value.as_pointer) > 1 || (
+                                           data_type != 'd' && data_type != 'D' &&
+                                           data_type != 'c' && data_type != 'C') ) {
+                                       /* if not, set to 'b' so this line is ignored. */
+                                       data_type = 'b';
+                               }
+                       } else if (data_type == 'c' || data_type == 'C') {
+                               if (have_czone == isc_boolean_true) {
+                                       isc_buffer_putstr(&buf2, token.value.as_pointer);
+                                       /* add string terminator to buffer */
+                                       isc_buffer_putmem(&buf2, "\0", 1);
+                               } else {
+                                       isc_buffer_putstr(&buf, token.value.as_pointer);
+                                       /* add string terminator to buffer */
+                                       isc_buffer_putmem(&buf, "\0", 1);
+                                       have_czone = isc_boolean_true;
+                               }
+                       } else {
+                               isc_buffer_putstr(&buf, token.value.as_pointer);
+                               isc_buffer_putstr(&buf, " ");
+                       }
+                       break;
+               case isc_tokentype_qstring:
+                       isc_buffer_putstr(&buf, "\"");
+                       isc_buffer_putstr(&buf, token.value.as_pointer);
+                       isc_buffer_putstr(&buf, "\" ");
+                       break;
+               case isc_tokentype_eol:
+               case isc_tokentype_eof:
+                       
+                       if ((data_type != 'u' && isc_buffer_usedlength(&buf) > 0) || data_type == 'b') {
+                               /* perform insert operation */
+                               if (data_type == 'd' || data_type == 'D') {
+                                       /* add string terminator to buffer */
+                                       isc_buffer_putmem(&buf, "\0", 1);
+                                       put_data(isc_boolean_true, NULL, (char *) &data_arr);
+                               } else if (data_type == 'c' || data_type == 'C') {
+                                       put_data(isc_boolean_false, (char *) &data_arr, 
+                                                (char *) &data_arr2);
+                               } else if (data_type == 'b') {
+                                       fprintf(stderr, "Bad / unknown token encountered on line %lu."\
+                                               "  Skipping line.",     isc_lex_getsourceline(lexer) - 1);
+                               } else {
+                                       fprintf(stderr, "Bad / unknown db data type encountered on " \
+                                               "line %lu.  Skipping line\n", isc_lex_getsourceline(lexer) - 1);
+                               }
+                       }
+
+                       if (token.type == isc_tokentype_eof) {
+                               loop = isc_boolean_false;
+                       }       
+
+                       /* reset buffer for next insert */
+                       isc_buffer_clear(&buf);
+                       isc_buffer_clear(&buf2);
+                       have_czone = isc_boolean_false;
+                       data_type ='u';
+                       break;
+               default:
+                       data_type = 'b';
+                       break;
+               }
+       }
+
+       return;
+
+ data_cleanup:
+       /* let user know we had problems */
+       fprintf(stderr, "Unknown error processing tokens during \"add\" or " \
+               "\"bulk\" operation.\nStoped processing on line %lu.", 
+               isc_lex_getsourceline(lexer));
+}
+
+
+isc_result_t
+openBDB(void) {
+
+       int bdbres;
+       isc_result_t result;
+
+       /* create BDB environment  */
+       /* Basically BDB allocates and assigns memory to db->dbenv */
+       bdbres = db_env_create(&db.dbenv, 0);
+       if (bdbres != 0) {
+               fprintf(stderr, "BDB environment could not be created. BDB error: %s", 
+                       db_strerror(bdbres));
+               result = ISC_R_FAILURE;
+               goto openBDB_cleanup;
+       }
+
+       /* open BDB environment */
+       if (create_allowed == isc_boolean_true) {
+               /* allowed to create new files */
+               bdbres = db.dbenv->open(db.dbenv, db_envdir, 
+                                       DB_INIT_CDB | DB_INIT_MPOOL | DB_CREATE, 0);
+       } else {        /* not allowed to create new files. */
+               bdbres = db.dbenv->open(db.dbenv, db_envdir, 
+                                       DB_INIT_CDB | DB_INIT_MPOOL, 0);
+       }
+       if (bdbres != 0) {
+               fprintf(stderr, "BDB environment at '%s' could not be opened. BDB " \
+                       "error: %s", db_envdir, db_strerror(bdbres));
+               result = ISC_R_FAILURE;
+               goto openBDB_cleanup;
+       }
+
+       /* open dlz_data database. */
+
+       result = bdb_opendb(DB_RECNO, &db.data, dlz_data, 0);
+       if (result != ISC_R_SUCCESS)
+               goto openBDB_cleanup;
+       
+       /* open dlz_host database */
+       result = bdb_opendb(DB_BTREE, &db.host, dlz_host, DB_DUP | DB_DUPSORT);
+       if (result != ISC_R_SUCCESS)
+               goto openBDB_cleanup;
+
+       /* open dlz_zone database. */
+       result = bdb_opendb(DB_BTREE, &db.zone, dlz_zone, DB_DUP | DB_DUPSORT);
+       if (result != ISC_R_SUCCESS)
+               goto openBDB_cleanup;
+
+       /* open dlz_client database. */
+       result = bdb_opendb(DB_BTREE, &db.client, dlz_client, DB_DUP | DB_DUPSORT);
+       if (result != ISC_R_SUCCESS)
+               goto openBDB_cleanup;
+
+       /* associate the host secondary database with the primary database */
+       bdbres = db.data->associate(db.data, NULL, db.host, gethost, 0);
+       if (bdbres != 0) {
+               fprintf(stderr, "BDB could not associate %s database with %s. BDB "\
+                       "error: %s", dlz_host, dlz_data, db_strerror(bdbres));
+               result = ISC_R_FAILURE;
+               goto openBDB_cleanup;
+       }
+
+       /* associate the zone secondary database with the primary database */
+       bdbres = db.data->associate(db.data, NULL, db.zone, getzone, 0);
+       if (bdbres != 0) {
+               fprintf(stderr, "BDB could not associate %s database with %s. BDB "\
+                       "error: %s", dlz_zone, dlz_data, db_strerror(bdbres));
+               result = ISC_R_FAILURE;
+               goto openBDB_cleanup;
+       }
+
+       return result;
+
+ openBDB_cleanup:
+
+       bdb_cleanup();
+       return result;
+}
+
+/*% Create & open lexer to parse input data */
+
+isc_result_t
+open_lexer(void) {
+       isc_result_t result;
+
+       /* check if we already opened the lexer, if we did, return success */
+       if (lexer != NULL)
+               return ISC_R_SUCCESS;
+
+       /* allocate memory for lexer, and verify it was allocated */
+       result = isc_mem_create(0, 0, &lex_mctx);
+       if (result != ISC_R_SUCCESS) {
+               fprintf(stderr, "unexpected error creating lexer\n");
+               return result;
+       }
+
+       /* create lexer */
+       result = isc_lex_create(lex_mctx, 1500, &lexer);
+       if (result != ISC_R_SUCCESS)
+               fprintf(stderr, "unexpected error creating lexer\n");
+
+       /* set allowed commenting style */
+       isc_lex_setcomments(lexer, ISC_LEXCOMMENT_C |   /* Allow C comments */
+                           ISC_LEXCOMMENT_CPLUSPLUS |  /* Allow C++ comments */
+                           ISC_LEXCOMMENT_SHELL);              /* Allow shellcomments */
+
+       isc_buffer_init(&lex_buffer, &lex_data_buf, max_data_len);
+
+       return result;
+}
+
+/*% Close the lexer, and cleanup memory */
+
+void
+close_lexer(void) {
+               
+       /* If lexer is still open, close it & destroy it. */
+       if (lexer != NULL) {
+               isc_lex_close(lexer);
+               isc_lex_destroy(&lexer);
+       }
+
+       /* if lexer memory is still allocated, destroy it. */
+       if (lex_mctx != NULL)
+               isc_mem_destroy(&lex_mctx);
+}
+
+/*% Perform add operation */
+
+void
+operation_add(void) {
+       /* check for any parameters that are not allowed during add */
+       checkInvalidParam(key, "k", "for add operation");
+       checkInvalidParam(zone, "z", "for add operation");
+       checkInvalidParam(host, "h", "for add operation");
+       checkInvalidParam(c_zone, "c", "for add operation");
+       checkInvalidParam(c_ip, "i", "for add operation");
+       checkInvalidOption(list_everything, isc_boolean_true, "e",
+                          "for add operation");
+
+       /* if open lexer fails it alread prints error messages. */
+       if (open_lexer() != ISC_R_SUCCESS) {
+               quit(4);
+       }
+       
+       /* copy input data to buffer */
+       isc_buffer_putstr(&lex_buffer, a_data);
+               
+       /* tell lexer to use buffer as input  */
+       if (isc_lex_openbuffer(lexer, &lex_buffer) != ISC_R_SUCCESS) {
+               fprintf(stderr, "unexpected error opening lexer buffer");
+               quit(4);
+       }
+
+       /*common logic for "add" & "bulk" operations are handled by insert_data */
+       insert_data();
+}
+
+/*% Perform bulk insert operation */
+
+void
+operation_bulk(void) {
+       /* check for any parameters that are not allowed during bulk */
+       checkInvalidParam(key, "k", "for bulk load operation");
+       checkInvalidParam(zone, "z", "for bulk load operation");
+       checkInvalidParam(host, "h", "for bulk load operation");
+       checkInvalidParam(c_zone, "c", "for bulk load operation");
+       checkInvalidParam(c_ip, "i", "for bulk load operation");
+       checkInvalidOption(list_everything, isc_boolean_true, "e",
+                          "for bulk load operation");
+
+       /* if open lexer fails it already prints error messages. */
+       if (open_lexer() != ISC_R_SUCCESS) {
+               quit(4);
+       }
+
+       if (bulk_file == NULL) {
+               if (isc_lex_openstream(lexer, stdin) != ISC_R_SUCCESS) {
+                       fprintf(stderr, "unexpected error opening stdin by lexer.");
+                       quit(4);                                
+               }
+       } else if (isc_lex_openfile(lexer, bulk_file) != ISC_R_SUCCESS) {
+               fprintf(stderr, "unexpected error opening %s by lexer.", bulk_file);
+               quit(4);
+       }
+
+       /* common logic for "add" & "bulk" operations are handled by insert_data */
+       insert_data();  
+}
+
+isc_result_t
+bulk_write(char type, DB *database, DBC *dbcursor, DBT *bdbkey, DBT *bdbdata) {
+
+       int bdbres;
+       db_recno_t recNum;
+       char *retkey = NULL, *retdata;
+       size_t retklen = 0, retdlen;
+       void *p;
+
+       /* use a 5MB buffer for the bulk dump */
+       int buffer_size = 5 * 1024 * 1024;
+         
+       /* try to allocate a 5 MB buffer, if we fail write err msg, die. */
+       bdbdata->data = malloc(buffer_size);
+       if (bdbdata->data == NULL) {
+               fprintf(stderr,
+                       "Unable to allocate 5 MB buffer for bulk database dump\n");
+               return ISC_R_FAILURE;
+       }
+       bdbdata->ulen = buffer_size;
+       bdbdata->flags = DB_DBT_USERMEM;
+
+       /* get a cursor, make sure it worked. */
+       bdbres = database->cursor(database, NULL, &dbcursor, 0);
+       if (bdbres != 0) {
+               fprintf(stderr, "Unexpected error. BDB Error: %s\n",db_strerror(bdbres));
+               free(bdbdata->data);
+               return ISC_R_FAILURE;
+       }
+
+       /* loop and dump all data */
+       for (;;) {
+
+               /* loop through data until DB_NOTFOUND is returned */
+               bdbres = dbcursor->c_get(dbcursor, bdbkey, bdbdata, 
+                                        DB_MULTIPLE_KEY | DB_NEXT);
+               /* if not successful did we encounter DB_NOTFOUND, or */
+               /* have  a different problem. */
+               if (bdbres != 0) {
+                       if (bdbres != DB_NOTFOUND) {
+                               fprintf(stderr, "Unexpected error. BDB Error: %s\n",
+                                       db_strerror(bdbres));
+                               free(bdbdata->data);
+                               return ISC_R_FAILURE;
+                       }
+                       /* Hit DB_NOTFOUND which means end of data. */
+                       break;
+               } /* end of if (bdbres !=0) */
+
+               for (DB_MULTIPLE_INIT(p, bdbdata);;) {
+                       if (type == 'c')
+                               DB_MULTIPLE_KEY_NEXT(p, bdbdata, retkey, retklen, retdata, retdlen);
+                       else
+                               DB_MULTIPLE_RECNO_NEXT(p, bdbdata, recNum, retdata, retdlen);
+
+                       if (p == NULL)
+                               break;
+                       if (type == 'c')
+                               printf("c %.*s %.*s\n",(int)retklen, retkey,(int)retdlen, retdata);
+                       else
+                               printf("d %.*s\n", (int)retdlen, retdata); 
+               } /* end of for (DB_MULTIPLE_INIT....) */
+
+       } /* end of for (;;) */
+
+       /* free the buffer we created earlier */
+       free(bdbdata->data);
+
+       return ISC_R_SUCCESS;
+}
+
+/*%
+ * Perform listOrDelete operation
+ * if dlt == true, delete data
+ * else list data
+ */
+
+void
+operation_listOrDelete(isc_boolean_t dlt) {
+
+       int bdbres = 0;
+       DBC *curList[3];
+       DBT bdbkey, bdbdata;
+       db_recno_t recno;
+       int curIndex = 0;
+
+
+       /* verify that only allowed parameters were passed. */
+       if (dlt == isc_boolean_true) {
+               checkInvalidParam(zone, "z", "for delete operation");
+               checkInvalidParam(host, "h", "for delete operation");
+               checkInvalidOption(list_everything, isc_boolean_true, "e",
+                                  "for delete operation");
+               checkInvalidOption(create_allowed, isc_boolean_true, "n",
+                                  "for delete operation");
+       } else if (key != NULL || zone != NULL || host != NULL) {
+               checkInvalidParam(c_zone, "c", "for list when k, z or h are specified");
+               checkInvalidParam(c_ip, "i", "for list when k, z, or h are specified");
+               checkInvalidOption(list_everything, isc_boolean_true, "e",
+                                  "for list when k, z, or h are specified");
+               checkInvalidOption(create_allowed, isc_boolean_true, "n",
+                                  "for list operation");
+       } else if (c_ip != NULL || c_zone != NULL) {
+               checkInvalidOption(list_everything, isc_boolean_true, "e",
+                                  "for list when c or i are specified");
+               checkInvalidOption(create_allowed, isc_boolean_true, "n",
+                                  "for list operation");
+       }
+
+       memset(&bdbkey, 0, sizeof(bdbkey));
+       memset(&bdbdata, 0, sizeof(bdbdata));
+
+       /* Dump database in "dlzbdb" bulk format */
+       if (list_everything == isc_boolean_true) {
+               if (bulk_write('c', db.client, db.cursor, &bdbkey, &bdbdata)
+                   != ISC_R_SUCCESS)
+                       return;
+               memset(&bdbkey, 0, sizeof(bdbkey));
+               memset(&bdbdata, 0, sizeof(bdbdata));
+               bulk_write('d', db.data, db.cursor2, &bdbkey, &bdbdata);
+               return;
+       } /* end if (list_everything) */
+
+       /* set NULL the 2nd and 3rd positions in curList. */
+       /* that way later when add cursors to the join list */
+       /* it is already null terminated. */
+       curList[1] = curList[2] = NULL;
+
+       if (key != NULL) {
+               /* make sure other parameters weren't */
+               checkInvalidParam(zone, "z", "when k is specified");
+               checkInvalidParam(host, "h", "when k is specified");
+
+               recno = key_val;
+               bdbkey.data = &recno;
+               bdbkey.size = sizeof(recno);
+
+               if (dlt == isc_boolean_true) {
+                       bdbres = db.data->del(db.data, NULL, &bdbkey, 0);
+               } else {
+                       bdbdata.flags = DB_DBT_REALLOC;
+                       bdbres = db.data->get(db.data, NULL, &bdbkey, &bdbdata, 0);
+
+                       if (bdbres == 0) {
+                               printf("KEY | DATA\n");
+                               printf("%lu | %.*s\n", *(u_long *) bdbkey.data,
+                                      (int)bdbdata.size, (char *)bdbdata.data);
+                       }
+               } /* closes else of if (dlt == isc_boolean_true) */
+               if (bdbres == DB_NOTFOUND) {
+                       printf("Key not found in database");
+               }
+       }       /* closes if (key != NULL) */
+
+               /* if zone is passed */
+       if (zone != NULL) {
+               /* create a cursor and make sure it worked */
+               bdbres = db.zone->cursor(db.zone, NULL, &db.cursor2, 0);
+               if (bdbres != 0) {
+                       fprintf(stderr, "Unexpected error. BDB Error: %s\n",
+                               db_strerror(bdbres));
+                       return;
+               }
+
+               bdbkey.data = zone;
+               bdbkey.size = strlen(zone);
+               bdbres = db.cursor2->c_get(db.cursor2, &bdbkey, &bdbdata, DB_SET);
+               if (bdbres != 0) {
+                       if (bdbres != DB_NOTFOUND) {
+                               fprintf(stderr, "Unexpected error. BDB Error: %s\n",
+                                       db_strerror(bdbres));
+                       } else {
+                               printf("Zone not found in database");
+                       }
+                       return;
+               }
+
+               /* add cursor to cursor list for later use in join */
+               curList[curIndex++] = db.cursor2;
+       }
+
+       /* if host is passed */
+       if (host != NULL) {
+
+               /* create a cursor and make sure it worked. */
+               bdbres = db.host->cursor(db.host, NULL, &db.cursor3, 0);
+               if (bdbres != 0) {
+                       fprintf(stderr, "Unexpected error. BDB Error: %s\n",
+                               db_strerror(bdbres));
+                       return;
+               }
+               bdbkey.data = host;
+               bdbkey.size = strlen(host);
+               bdbres = db.cursor3->c_get(db.cursor3, &bdbkey, &bdbdata, DB_SET);
+               if (bdbres != 0) {
+                       if (bdbres != DB_NOTFOUND) {
+                               fprintf(stderr, "Unexpected error. BDB Error: %s\n",
+                                       db_strerror(bdbres));
+                       } else {
+                               printf("Host not found in database");
+                       }
+                       return;
+               }
+
+               /* add cursor to cursor list for later use in join */
+               curList[curIndex++] = db.cursor3;
+       }
+
+
+       if (zone != NULL || host != NULL) {
+
+               /* join any cursors */
+               bdbres = db.data->join(db.data, curList, &db.cursor4, 0);
+               if (bdbres != 0) {
+                       fprintf(stderr, "Unexpected error. BDB Error: %s\n",
+                               db_strerror(bdbres));
+                       return;
+               }
+
+               memset(&bdbkey, 0, sizeof(bdbkey));
+               bdbkey.flags = DB_DBT_REALLOC;
+               memset(&bdbdata, 0, sizeof(bdbdata));
+               bdbdata.flags = DB_DBT_REALLOC;
+
+               /* print a header to explain the output */
+               printf("KEY | DATA\n");
+               /* loop and list all results. */
+               while (bdbres == 0) {   
+                       /* get data */
+                       bdbres = db.cursor4->c_get(db.cursor4, &bdbkey, &bdbdata, 0);
+                       /* verify call had no errors */
+                       if (bdbres != 0) {
+                               break;
+                       }
+                       printf("%lu | %.*s\n", *(u_long *) bdbkey.data,
+                              (int)bdbdata.size, (char *)bdbdata.data);
+               } /* closes while loop */
+       }
+
+       if (c_ip != NULL && c_zone == NULL) {
+               fprintf(stderr, "i may only be specified when c is also specified\n");                  
+               quit(2);
+       }
+       /* if client_zone was passed */
+       if (c_zone != NULL) {
+
+               /* create a cursor and make sure it worked. */
+               if (dlt == isc_boolean_true) {
+                       /* open read-write cursor */
+                       bdbres = db.client->cursor(db.client, NULL, &db.cursor,
+                                                  DB_WRITECURSOR);
+               } else {
+                       /* open read only cursor */
+                       bdbres = db.client->cursor(db.client, NULL, &db.cursor, 0);
+                       /* print a header to explain the output */
+                       printf("CLIENT_ZONE | CLIENT_IP\n");
+               }
+
+               bdbkey.data = c_zone;
+               bdbkey.size = strlen(c_zone);
+
+               if (c_ip != NULL) {
+                       bdbdata.data = c_ip;
+                       bdbdata.size = strlen(c_ip);
+                       bdbres = db.cursor->c_get(db.cursor, &bdbkey, &bdbdata, DB_GET_BOTH);
+                       if (bdbres == DB_NOTFOUND) {
+                               printf("Client zone & IP not found in database");
+                       }
+               } else {
+                       bdbdata.flags = DB_DBT_REALLOC;
+                       bdbres = db.cursor->c_get(db.cursor, &bdbkey, &bdbdata, DB_SET);
+                       if (bdbres == DB_NOTFOUND) {
+                               printf("Client zone not found in database");
+                       }
+               }
+
+               while (bdbres == 0) {
+                       if (dlt == isc_boolean_false) {
+                               printf("%.*s | %.*s\n", (int)bdbkey.size, (char *) bdbkey.data,
+                                      (int)bdbdata.size, (char *) bdbdata.data);
+                       } else {
+                               /* delete record. */
+                               bdbres = db.cursor->c_del(db.cursor, 0);
+                               if (bdbres != 0) {
+                                       fprintf(stderr, "Unexpected error. BDB Error: %s\n",
+                                               db_strerror(bdbres));
+                                       break;
+                               }
+                       }
+                       if (c_ip != NULL) {
+                               break;
+                       }
+                       bdbres = db.cursor->c_get(db.cursor, &bdbkey, &bdbdata, DB_NEXT_DUP);                   
+                       if (bdbres != 0) {
+                               break;
+                       }
+               } /* end while loop */
+       }
+
+
+       if (bdbres != 0 && bdbres != DB_NOTFOUND) {
+               fprintf(stderr, "Unexpected error during list operation " \
+                       "BDB error: %s", db_strerror(bdbres));
+       }
+
+       if (bdbkey.flags == DB_DBT_REALLOC && bdbkey.data != NULL) {
+               free(bdbkey.data);
+       }
+       if (bdbdata.flags == DB_DBT_REALLOC && bdbdata.data != NULL) {
+               free(bdbdata.data);
+       }
+}
+
+
+int
+main(int argc, char **argv) {
+
+       int ch;
+       char *endp;
+
+       /* there has to be at least 2 args, some operations require more */
+       if (argc < 2)
+               show_usage();
+
+       /* use the ISC commandline parser to get all the program arguments */
+       while ((ch= isc_commandline_parse(argc, argv, "ldesna:f:k:z:h:c:i:")) != -1) {
+               switch (ch) {
+               case 'n':
+                       create_allowed = isc_boolean_true;
+                       break;
+               case 'l':
+                       checkOp(operation);
+                       operation = list;
+                       break;
+               case 'd':
+                       checkOp(operation);
+                       operation = dele;
+                       break;
+               case 'a':
+                       checkOp(operation);
+                       operation = add;
+                       a_data = isc_commandline_argument; 
+                       break;
+               case 'f':
+                       checkOp(operation);
+                       operation = bulk;
+                       bulk_file = isc_commandline_argument;
+                       break;
+               case 's':
+                       checkOp(operation);
+                       operation = bulk;
+                       break;
+               case 'k':
+                       checkParam(key, "k");
+                       key = isc_commandline_argument;
+                       key_val = strtoul(key, &endp, 10);
+                       if (*endp != '\0' || key_val < 1) {
+                               fprintf(stderr, "Error converting key to integer");
+                       }
+                       break;
+               case 'z':
+                       checkParam(zone, "z");
+                       zone = isc_commandline_argument;
+                       break;
+               case 'h':
+                       checkParam(host, "h");
+                       host = isc_commandline_argument;
+                       break;
+               case 'c':
+                       checkParam(c_zone, "c");
+                       c_zone = isc_commandline_argument;
+                       break;
+               case 'i':
+                       checkParam(c_ip, "i");
+                       c_ip = isc_commandline_argument;
+                       break;
+               case 'e':
+                       checkOp(operation);
+                       operation = list;
+                       list_everything = isc_boolean_true;
+                       break;
+               case '?':
+                       show_usage();
+                       break;
+               default:
+                       /* should never reach this point */
+                       fprintf(stderr, "unexpected error parsing command arguments\n");
+                       quit(1);
+                       break;
+               }
+       }
+
+       argc -= isc_commandline_index;
+       argv += isc_commandline_index;
+
+       /* argc & argv have been modified, so now only "extra" parameters are */
+       /* left in argc & argv.  "Extra" parameters are any parameters that were */
+       /* not passed using a command line flag.  Exactly 2 args should be left. */
+       /* The first should be the BDB environment path, the second should be the */
+       /* BDB database.  The BDB database path can be either relative to the */
+       /* BDB environment path, or absolute. */
+       if (argc < 2) {
+               fprintf(stderr, "Both a Berkeley DB environment and file "\
+                       "must be specified");
+               quit(2);
+       } else if (argc > 2) {
+               fprintf(stderr, "Too many parameters. Check command line for errors.");
+               quit(2);
+       }
+
+       /* get db_file to operate on */
+       db_envdir = argv[0];
+       db_file = argv[1];
+
+       if (openBDB() != ISC_R_SUCCESS) {
+               /* openBDB already prints error messages, don't do it here. */
+               bdb_cleanup();
+               quit(3);
+       }
+
+       switch(operation) {
+       case list:
+               operation_listOrDelete(isc_boolean_false);
+               break;
+       case dele:
+               operation_listOrDelete(isc_boolean_true);
+               break;
+       case add:
+               operation_add();
+               break;
+       case bulk:
+               operation_bulk();
+               break;
+       default:
+               fprintf(stderr, "\nNo operation was selected. "\
+                       "Select an operation (l d a f)");
+               quit(2);
+               break;
+       }
+
+       quit(0);
+}
+#endif
+
diff --git a/contrib/dlz/config.dlz.in b/contrib/dlz/config.dlz.in
new file mode 100644 (file)
index 0000000..af463eb
--- /dev/null
@@ -0,0 +1,434 @@
+# Copyright (C) 2005  Internet Systems Consortium, Inc. ("ISC")
+#
+# 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 ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS.  IN NO EVENT SHALL ISC 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.
+
+#
+# Shorthand.  Note quoting: DLZ_DRIVER_DIR expanded in Makefile, not here.
+#
+dlzdir='${DLZ_DRIVER_DIR}'
+
+#
+# Private autoconf macro to simplify configuring drivers:
+#
+#   DLZ_ADD_DRIVER(DEFINE, DRIVER, INCLUDES, LIBS)
+#
+# where:
+#   DEFINE is FOO (to define -DDLZ_FOO)
+#   DRIVER is dlz_foo_driver (sources without the .c)
+#   INCLUDES is any necessary include definitions
+#   LIBS is any necessary library definitions
+#
+AC_DEFUN(DLZ_ADD_DRIVER, [
+       USE_DLZ="$USE_DLZ -DDLZ_$1"
+       for i in $2
+       do
+               DLZ_DRIVER_SRCS="$DLZ_DRIVER_SRCS $dlzdir/$i.c"
+               DLZ_DRIVER_OBJS="$DLZ_DRIVER_OBJS $i.$O"
+       done
+       if test -n "$3"
+       then
+               DLZ_DRIVER_INCLUDES="$DLZ_DRIVER_INCLUDES $3"
+       fi
+       if test -n "$4"
+       then
+               DLZ_DRIVER_LIBS="$DLZ_DRIVER_LIBS $4"
+       fi
+])
+
+#
+# Check for the various DLZ drivers
+#
+
+#
+# Was --with-dlz-postgres specified?
+#
+
+AC_MSG_CHECKING(for Postgres DLZ driver)
+AC_ARG_WITH(dlz_postgres,
+[  --with-dlz-postgres[=PATH]   Build with Postgres DLZ driver [yes|no|path].
+                               (Required to use Postgres with DLZ)],
+    use_dlz_postgres="$withval", use_dlz_postgres="no")
+
+if test "$use_dlz_postgres" = "yes"
+then
+       # User did not specify a path - guess it
+       # Ask Postgres to tell us where it is
+
+       AC_PATH_PROGS(PG_CONFIG, pg_config, [not found])
+
+       if test "$PG_CONFIG" != "not found"
+       then
+               use_dlz_postgres=`$PG_CONFIG --includedir`
+               use_dlz_postgres_lib=`$PG_CONFIG --libdir`
+       fi
+fi
+
+if test "$use_dlz_postgres" = "yes"
+then
+       # User did not specify path and Postgres didn't say - guess it
+
+       pgdirs="/usr /usr/local /usr/local/pgsql /usr/pkg"
+       for d in $pgdirs
+       do
+               if test -f $d/include/libpq-fe.h
+               then
+                       use_dlz_postgres=$d/include
+                       use_dlz_postgres_lib=$d/lib
+                       break
+               fi
+       done
+fi
+
+if test "$use_dlz_postgres" = "yes"
+then
+       # Still no joy, give up
+
+       AC_MSG_RESULT(not found)
+       AC_MSG_ERROR(
+[No pg_config and PostgreSQL was not found in any of $pgdirs; use --with-dlz-postgres=/path or put pg_config in your path])
+fi
+
+case "$use_dlz_postgres" in
+       no)
+               AC_MSG_RESULT(no)
+               ;;
+       *)
+               DLZ_ADD_DRIVER(POSTGRES, dlz_postgres_driver,
+                               [-I$use_dlz_postgres],
+                               [-L$use_dlz_postgres_lib -lpq])
+
+               AC_MSG_RESULT(
+[using PostgreSQL from $use_dlz_postgres_lib and $use_dlz_postgres])
+               ;;
+esac
+
+
+#
+# Was --with-dlz-mysql specified?
+#
+
+AC_MSG_CHECKING(for MySQL DLZ driver)
+AC_ARG_WITH(dlz_mysql,
+[  --with-dlz-mysql[=PATH]   Build with MySQL DLZ driver [yes|no|path].
+                               (Required to use MySQL with DLZ)],
+    use_dlz_mysql="$withval", use_dlz_mysql="no")
+
+if test "$use_dlz_mysql" = "yes"
+then
+       # User did not specify a path - guess it
+       mysqldirs="/usr /usr/local /usr/local/mysql /usr/pkg"
+       for d in $mysqldirs
+       do
+               if test -f $d/include/mysql/mysql.h
+               then
+                       use_dlz_mysql=$d
+                       break
+               fi
+       done
+fi
+
+if test "$use_dlz_mysql" = "yes"
+then
+       AC_MSG_RESULT(not found)
+       AC_MSG_ERROR(
+[MySQL was not found in any of $mysqldirs; use --with-dlz-mysql=/path])
+fi
+
+case "$use_dlz_mysql" in
+       no)
+               AC_MSG_RESULT(no)
+               ;;
+       *)
+               DLZ_ADD_DRIVER(MYSQL, dlz_mysql_driver,
+                               [-I$use_dlz_mysql/include/mysql],
+                               [-L$use_dlz_mysql/lib/mysql -lmysqlclient -lz -lcrypt -lm])
+
+               AC_MSG_RESULT(
+[using mysql from $use_dlz_mysql/lib/mysql and $use_dlz_mysql/include/mysql])
+               ;;
+esac
+
+
+#
+# Was --with-dlz-bdb specified?
+#
+
+AC_MSG_CHECKING(for Berkeley DB DLZ driver)
+AC_ARG_WITH(dlz_bdb,
+[  --with-dlz-bdb[=PATH]   Build with Berkeley DB DLZ driver [yes|no|path].
+                               (Required to use Berkeley DB with DLZ)],
+    use_dlz_bdb="$withval", use_dlz_bdb="no")
+
+case "$use_dlz_bdb" in
+       no)
+               AC_MSG_RESULT(no)
+               ;;
+       *)
+               if test "$use_dlz_bdb" = "yes"
+               then
+                       # User did not specify a path - guess directories
+                       bdbdirs="/usr/local /usr/pkg /usr"
+               elif test -d "$use_dlz_bdb"
+               then
+                       # User specified directory and it exists
+                       bdbdirs="$use_dlz_bdb"
+               else
+                       AC_MSG_RESULT(not found)
+                       AC_MSG_ERROR([path $use_dlz_bdb does not exist])
+                       bdbdirs=""
+               fi
+
+               # Use path we were given or guessed.  This is insanely
+               # complicated because we have to search for a bunch of
+               # platform-specific variations and have to check
+               # separately for include and library directories.
+
+               # Set both to yes, so we can check them later
+               dlz_bdb_inc="yes"
+               dlz_bdb_libs="yes"
+
+               for dd in $bdbdirs
+               do
+                       # Skip nonexistant directories
+                       if test ! -d "$dd"
+                       then
+                               continue
+                       fi
+
+                       # Check other locations for includes.
+                       # Order is important (sigh).
+
+                       bdb_incdirs="/ /db42/ /db41/ /db4/ /db/"
+                       for d in $bdb_incdirs
+                       do
+                               if test -f "$dd/include${d}db.h"
+                               then
+                                       dlz_bdb_inc="-I$dd/include${d}"
+                                       break
+                               fi
+                       done
+
+                       # Give up on this directory if we couldn't
+                       # find the include subdir
+
+                       if test "$dlz_bdb_inc" = "yes"
+                       then
+                               continue
+                       fi
+
+                       # Look for libname other than libdb.so.
+                       # Order is important (sigh).
+
+                       bdb_libnames="db42 db-4.2 db41 db-4.1 db"
+                       for d in $bdb_libnames
+                       do
+                               if test -f "$dd/lib/lib${d}.so"
+                               then
+                                       if test "$dd" != "/usr"
+                                       then
+                                               dlz_bdb_libs="-L${dd}/lib "
+                                       fi
+                                       dlz_bdb_libs="${dlz_bdb_libs}-l${d}"
+                                       break
+                               fi
+                       done
+
+                       # If we found both incdir and lib, we're done
+                       if test "$dlz_bdb_libs" != "yes"
+                       then
+                               break
+                       fi
+
+                       # Otherwise, we're starting over
+
+                       dlz_bdb_inc="yes"
+                       dlz_bdb_libs="yes"
+               done
+               
+               # Done searching, now make sure we got everything.
+
+               if test "$dlz_bdb_inc" = "yes"
+               then
+                       AC_MSG_RESULT(not found)
+                       AC_MSG_ERROR([could not find Berkeley DB include directory])
+               fi
+
+               if test "$dlz_bdb_libs" = "yes"
+               then
+                       AC_MSG_RESULT(not found)
+                       AC_MSG_ERROR([could not find Berkeley DB library])
+               fi
+
+               DLZ_ADD_DRIVER(BDB, dlz_bdb_driver dlz_bdbhpt_driver,
+                              [$dlz_bdb_inc], [$dlz_bdb_libs])
+
+               AC_MSG_RESULT([using Berkeley DB: $dlz_bdb_inc $dlz_bdb_libs])
+
+               AC_CONFIG_FILES([contrib/dlz/bin/dlzbdb/Makefile])
+               ;;
+esac
+
+
+#
+# Was --with-dlz-filesystem specified?
+#
+
+AC_MSG_CHECKING(for file system DLZ driver)
+AC_ARG_WITH(dlz_filesystem,
+[  --with-dlz-filesystem[=PATH]   Build with filesystem DLZ driver [yes|no].
+                               (Required to use file system driver with DLZ)],
+    use_dlz_filesystem="$withval", use_dlz_filesystem="no")
+
+case "$use_dlz_filesystem" in
+       no)
+               AC_MSG_RESULT(no)
+               ;;
+       *)
+               DLZ_ADD_DRIVER(FILESYSTEM, dlz_filesystem_driver)
+
+               AC_MSG_RESULT(yes)
+               ;;
+esac
+
+
+#
+# Was --with-dlz-ldap specified?
+#
+
+AC_MSG_CHECKING(for LDAP DLZ driver)
+AC_ARG_WITH(dlz_ldap,
+[  --with-dlz-ldap[=PATH]   Build with LDAP DLZ driver [yes|no|path].
+                               (Required to use LDAP with DLZ)],
+    use_dlz_ldap="$withval", use_dlz_ldap="no")
+
+if test "$use_dlz_ldap" = "yes"
+then
+       # User did not specify a path - guess it
+       ldapdirs="/usr /usr/local /usr/pkg"
+       for d in $ldapdirs
+       do
+               if test -f $d/include/ldap.h
+               then
+                       use_dlz_ldap=$d
+                       break
+               fi
+       done
+fi
+
+if test "$use_dlz_ldap" = "yes"
+then
+       AC_MSG_RESULT(not found)
+       AC_MSG_ERROR(
+[LDAP headers were not found in any of $ldapdirs; use --with-dlz-ldap=/path])
+fi
+
+case "$use_dlz_ldap" in
+       no)
+               AC_MSG_RESULT(no)
+               ;;
+       *)
+               DLZ_ADD_DRIVER(LDAP, dlz_ldap_driver,
+                               [-I$use_dlz_ldap/include],
+                               [-L$use_dlz_ldap/lib -lldap -llber])
+
+               AC_MSG_RESULT(
+[using LDAP from $use_dlz_ldap/lib and $use_dlz_ldap/include])
+               ;;
+esac
+
+
+#
+# Was --with-dlz-odbc specified?
+#
+
+AC_MSG_CHECKING(for ODBC DLZ driver)
+AC_ARG_WITH(dlz_odbc,
+[  --with-dlz-odbc[=PATH]   Build with ODBC DLZ driver [yes|no|path].
+                               (Required to use ODBC with DLZ)],
+    use_dlz_odbc="$withval", use_dlz_odbc="no")
+
+if test "$use_dlz_odbc" = "yes"
+then
+       # User did not specify a path - guess it
+       odbcdirs="/usr /usr/local /usr/pkg"
+       for d in $odbcdirs
+       do
+               if test -f $d/include/sql.h -a -f $d/lib/libodbc.a
+               then
+                       use_dlz_odbc=$d
+                       break
+               fi
+       done
+fi
+
+case "$use_dlz_odbc" in
+       no)
+               AC_MSG_RESULT(no)
+               ;;
+       yes)
+               AC_MSG_RESULT(not found)
+               AC_MSG_ERROR(
+[ODBC headers were not found in any of $odbcdirs; use --with-dlz-odbc=/path])
+               ;;
+       *)
+               DLZ_ADD_DRIVER(ODBC, dlz_odbc_driver,
+                               [-I$use_dlz_odbc/include],
+                               [-L$use_dlz_odbc/lib -lodbc])
+
+               AC_MSG_RESULT([using ODBC from $use_dlz_odbc])
+               ;;
+esac
+
+
+#
+# Was --with-dlz-stub specified?
+#
+
+AC_MSG_CHECKING(for stub DLZ driver)
+AC_ARG_WITH(dlz_stub,
+[  --with-dlz-stub[=PATH]   Build with stub DLZ driver [yes|no].
+                               (Required to use stub driver with DLZ)],
+    use_dlz_stub="$withval", use_dlz_stub="no")
+
+case "$use_dlz_stub" in
+       no)
+               AC_MSG_RESULT(no)
+               ;;
+       *)
+
+               DLZ_ADD_DRIVER(STUB, dlz_stub_driver)
+
+               AC_MSG_RESULT(yes)
+               ;;
+esac
+
+
+# Add any additional DLZ drivers here.
+
+#
+# Finally, some generic stuff that applies to all drivers, assuming
+# we're compiling DLZ at all.
+#
+if test -n "$USE_DLZ"
+then
+       #
+       # Where to find DLZ driver header files.
+       #
+       DLZ_DRIVER_INCLUDES="-I$dlzdir/include $DLZ_DRIVER_INCLUDES"
+
+       #
+       # Initialization and shutdown wrappers, helper functions.
+       #
+       DLZ_DRIVER_SRCS="$dlzdir/dlz_drivers.c $dlzdir/sdlz_helper.c $DLZ_DRIVER_SRCS"
+       DLZ_DRIVER_OBJS="dlz_drivers.$O sdlz_helper.$O $DLZ_DRIVER_OBJS"
+fi
diff --git a/contrib/dlz/drivers/.cvsignore b/contrib/dlz/drivers/.cvsignore
new file mode 100644 (file)
index 0000000..de7475d
--- /dev/null
@@ -0,0 +1 @@
+rules
diff --git a/contrib/dlz/drivers/dlz_bdb_driver.c b/contrib/dlz/drivers/dlz_bdb_driver.c
new file mode 100644 (file)
index 0000000..5aa96a5
--- /dev/null
@@ -0,0 +1,797 @@
+/*
+ * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
+ *
+ * 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 STICHTING NLNET
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * STICHTING NLNET 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 development of Dynamically Loadable Zones (DLZ) for Bind 9 was
+ * conceived and contributed by Rob Butler.
+ *
+ * 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 ROB BUTLER
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * ROB BUTLER 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.
+ */
+
+/*
+ * Copyright (C) 1999-2001  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.
+ */
+
+#ifdef DLZ_BDB
+
+#include <config.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <dns/log.h>
+#include <dns/sdlz.h>
+#include <dns/result.h>
+
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/result.h>
+#include <isc/util.h>
+
+#include <named/globals.h>
+
+#include <dlz/dlz_bdb_driver.h>
+
+#include <db.h>
+
+static dns_sdlzimplementation_t *dlz_bdb = NULL;
+
+/* should the bdb driver use threads. */
+#ifdef ISC_PLATFORM_USETHREADS
+#define bdb_threads DB_THREAD
+#else
+#define bdb_threads 0
+#endif
+
+/* BDB database names */
+#define dlz_data "dns_data"
+#define dlz_zone "dns_zone"
+#define dlz_host "dns_host"
+#define dlz_client "dns_client"
+
+/*%
+ * This structure contains all the Berkeley DB handles
+ * for this instance of the BDB driver.
+ */
+
+typedef struct bdb_instance {
+       DB_ENV  *dbenv;         /*%< BDB environment */
+       DB      *data;          /*%< dns_data database handle */
+       DB      *zone;          /*%< zone database handle */
+       DB      *host;          /*%< host database handle */
+       DB      *client;        /*%< client database handle */
+       isc_mem_t *mctx;        /*%< memory context */
+
+} bdb_instance_t;
+
+typedef struct parsed_data {
+       char *zone;
+       char *host;
+       char *type;
+       int ttl;
+       char *data;
+} parsed_data_t;
+
+
+/* forward reference */
+
+static isc_result_t
+bdb_findzone(void *driverarg, void *dbdata, const char *name);
+
+/*%
+ * Parses the DBT from the Berkeley DB into a parsed_data record
+ * The parsed_data record should be allocated before and passed into the
+ * bdb_parse_data function.  The char (type & data) fields should not
+ * be "free"d as that memory is part of the DBT data field.  It will be
+ * "free"d when the DBT is freed.
+ */
+
+static isc_result_t
+bdb_parse_data(char *in, parsed_data_t *pd) {
+
+       char *endp, *ttlStr;
+       char *tmp = in;
+       char *lastchar = (char *) &tmp[strlen(tmp) + 1];
+
+       /*%
+        * String should be formated as:
+        * zone(a space)host(a space)ttl(a space)type(a space)remaining data
+        * examples:
+        * example.com www 10 A 127.0.0.1
+        * example.com mail 10 A 127.0.0.2
+        * example.com @ 10 MX 20 mail.example.com
+        */
+
+       /* save pointer to zone */
+       pd->zone = tmp;
+
+       /* find space after zone and change it to a '\0' */
+       tmp = strchr(tmp, ' ');
+       /* verify we found a space */
+       if (tmp == NULL)
+               return ISC_R_FAILURE;
+       /* change the space to a null (string terminator) */
+       tmp[0] = '\0';
+       /* make sure it is safe to increment pointer */
+       if (++tmp > lastchar)
+               return ISC_R_FAILURE;
+
+       /* save pointer to host */
+       pd->host = tmp;
+
+       /* find space after type and change it to a '\0' */
+       tmp = strchr(tmp, ' ');
+       /* verify we found a space */
+       if (tmp == NULL)
+               return ISC_R_FAILURE;
+       /* change the space to a null (string terminator) */
+       tmp[0] = '\0';
+       /* make sure it is safe to increment pointer */
+       if (++tmp > lastchar)
+               return ISC_R_FAILURE;
+
+       /* save pointer to dns type */
+       pd->type = tmp;
+
+       /* find space after type and change it to a '\0' */
+       tmp = strchr(tmp, ' ');
+       /* verify we found a space */
+       if (tmp == NULL)
+               return ISC_R_FAILURE;
+       /* change the space to a null (string terminator) */
+       tmp[0] = '\0';
+       /* make sure it is safe to increment pointer */
+       if (++tmp > lastchar)
+               return ISC_R_FAILURE;
+
+       /* save pointer to dns ttl */
+       ttlStr = tmp;
+
+       /* find space after ttl and change it to a '\0' */
+       tmp = strchr(tmp, ' ');
+       /* verify we found a space */
+       if (tmp == NULL)
+               return ISC_R_FAILURE;
+       /* change the space to a null (string terminator) */
+       tmp[0] = '\0';
+       /* make sure it is safe to increment pointer */
+       if (++tmp > lastchar)
+               return ISC_R_FAILURE;
+
+       /* save pointer to remainder of DNS data */
+       pd->data = tmp;
+
+       /* convert ttl string to integer */
+       pd->ttl = strtol(ttlStr, &endp, 10);
+       if (*endp != '\0' || pd->ttl < 0) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "BDB driver ttl must be a postive number");
+               return ISC_R_FAILURE;
+       }
+
+       /* if we get this far everything should have worked. */
+       return ISC_R_SUCCESS;
+}
+
+/*
+ * DLZ methods
+ */
+
+static isc_result_t
+bdb_allowzonexfr(void *driverarg, void *dbdata, const char *name,
+                const char *client)
+{
+       isc_result_t result;
+       bdb_instance_t *db = (bdb_instance_t *) dbdata;
+       DBC *client_cursor = NULL;
+       DBT key, data;
+
+       /* check to see if we are authoritative for the zone first. */
+       result = bdb_findzone(driverarg, dbdata, name);
+       if (result != ISC_R_SUCCESS)
+               return (ISC_R_NOTFOUND);
+
+       memset(&key, 0, sizeof(DBT));
+       key.flags = DB_DBT_MALLOC;
+       key.data = strdup(name);
+       if (key.data == NULL) {
+               result = ISC_R_NOMEMORY;
+               goto xfr_cleanup;
+       }
+       key.size = strlen(key.data);
+
+       memset(&data, 0, sizeof(DBT));
+       data.flags = DB_DBT_MALLOC;
+       data.data = strdup(client);
+       if (data.data == NULL) {
+               result = ISC_R_NOMEMORY;
+               goto xfr_cleanup;
+       }
+       data.size = strlen(data.data);
+
+       /* get a cursor to loop through zone data */
+       if (db->client->cursor(db->client, NULL, &client_cursor, 0) != 0) {
+               result = ISC_R_FAILURE;
+               goto xfr_cleanup;
+       }
+
+       switch(client_cursor->c_get(client_cursor, &key, &data, DB_GET_BOTH)) {
+       case DB_NOTFOUND:
+       case DB_SECONDARY_BAD:
+               result = ISC_R_NOTFOUND;
+               break;
+       case 0:
+               result = ISC_R_SUCCESS;
+               break;
+       default:
+               result = ISC_R_FAILURE;
+       }
+
+ xfr_cleanup:
+
+       /* free any memory duplicate string in the key field */
+       if (key.data != NULL)
+               free(key.data);
+
+       /* free any memory allocated to the data field. */
+       if (data.data != NULL)
+               free(data.data);
+
+       /* get rid of zone_cursor */
+       if (client_cursor != NULL)
+               client_cursor->c_close(client_cursor);
+
+       return result;
+
+}
+
+static isc_result_t
+bdb_allnodes(const char *zone, void *driverarg, void *dbdata,
+            dns_sdlzallnodes_t *allnodes)
+{
+
+       isc_result_t result = ISC_R_NOTFOUND;
+       bdb_instance_t *db = (bdb_instance_t *) dbdata;
+       DBC *zone_cursor = NULL;
+       DBT key, data;
+       int flags;
+       int bdbres;
+       parsed_data_t pd;
+       char *tmp = NULL, *tmp_zone;
+
+       UNUSED(driverarg);
+
+       memset(&key, 0, sizeof(DBT));
+       memset(&data, 0, sizeof(DBT));
+
+       key.data = tmp_zone = strdup(zone);
+
+       if (key.data == NULL)
+               return (ISC_R_NOMEMORY);
+
+       key.size = strlen(key.data);
+
+       /* get a cursor to loop through zone data */
+       if (db->zone->cursor(db->zone, NULL, &zone_cursor, 0) != 0) {
+               result = ISC_R_FAILURE;
+               goto allnodes_cleanup;
+       }
+
+       flags = DB_SET;
+
+       while ((bdbres = zone_cursor->c_get(zone_cursor, &key, &data,
+                                           flags)) == 0) {
+
+               flags = DB_NEXT_DUP;
+
+               tmp = realloc(tmp, data.size + 1);
+               if (tmp == NULL)
+                       goto allnodes_cleanup;
+
+               strncpy(tmp, data.data, data.size);
+               tmp[data.size] = '\0';
+
+               if (bdb_parse_data(tmp, &pd) != ISC_R_SUCCESS)
+                       goto allnodes_cleanup;
+
+               result = dns_sdlz_putnamedrr(allnodes, pd.host, pd.type,
+                                            pd.ttl, pd.data);
+               if (result != ISC_R_SUCCESS)
+                       goto allnodes_cleanup;
+
+       } /* end while loop */
+
+ allnodes_cleanup:
+
+       if (tmp != NULL)
+               free(tmp);
+
+       /* free any memory duplicate string in the key field */
+       if (tmp_zone != NULL)
+               free(tmp_zone);
+
+       /* get rid of zone_cursor */
+       if (zone_cursor != NULL)
+               zone_cursor->c_close(zone_cursor);
+
+       return result;
+
+}
+
+/*%
+ * Performs BDB cleanup.
+ * Used by bdb_create if there is an error starting up.
+ * Used by bdb_destroy when the driver is shutting down.
+ */
+
+static void
+bdb_cleanup(bdb_instance_t *db) {
+
+       isc_mem_t *mctx;
+
+       /* close databases */
+       if (db->data != NULL)
+               db->data->close(db->data, 0);
+       if (db->host != NULL)
+               db->host->close(db->host, 0);
+       if (db->zone != NULL)
+               db->zone->close(db->zone, 0);
+       if (db->client != NULL)
+               db->client->close(db->client, 0);
+
+       /* close environment */
+       if (db->dbenv != NULL)
+               db->dbenv->close(db->dbenv, 0);
+
+       /* cleanup memory */
+       if (db->mctx != NULL) {
+               /* save mctx for later */
+               mctx = db->mctx;
+               /* return, and detach the memory */
+               isc_mem_put(mctx, db, sizeof(bdb_instance_t));
+               isc_mem_detach(&mctx);
+       }
+}
+
+static isc_result_t
+bdb_findzone(void *driverarg, void *dbdata, const char *name)
+{
+
+       isc_result_t result;
+       bdb_instance_t *db = (bdb_instance_t *) dbdata;
+       DBC *zone_cursor = NULL;
+       DBT key, data;
+
+       UNUSED(driverarg);
+
+       memset(&key, 0, sizeof(DBT));
+       memset(&data, 0, sizeof(DBT));
+       data.flags = DB_DBT_MALLOC;
+
+       key.data = strdup(name);
+
+       if (key.data == NULL)
+               return (ISC_R_NOMEMORY);
+
+       key.size = strlen(key.data);
+
+       /* get a cursor to loop through zone data */
+       if (db->zone->cursor(db->zone, NULL, &zone_cursor, 0) != 0) {
+               result = ISC_R_NOTFOUND;
+               goto findzone_cleanup;
+       }
+
+       switch(zone_cursor->c_get(zone_cursor, &key, &data, DB_SET)) {
+       case DB_NOTFOUND:
+       case DB_SECONDARY_BAD:
+               result = ISC_R_NOTFOUND;
+               break;
+       case 0:
+               result = ISC_R_SUCCESS;
+               break;
+       default:
+               result = ISC_R_FAILURE;
+       }
+
+ findzone_cleanup:
+
+       /* free any memory duplicate string in the key field */
+       if (key.data != NULL)
+               free(key.data);
+
+       /* free any memory allocated to the data field. */
+       if (data.data != NULL)
+               free(data.data);
+
+       /* get rid of zone_cursor */
+       if (zone_cursor != NULL)
+               zone_cursor->c_close(zone_cursor);
+
+       return result;
+}
+
+static isc_result_t
+bdb_lookup(const char *zone, const char *name, void *driverarg,
+          void *dbdata, dns_sdlzlookup_t *lookup)
+{
+
+       isc_result_t result = ISC_R_NOTFOUND;
+       bdb_instance_t *db = (bdb_instance_t *) dbdata;
+       DBC *zone_cursor = NULL;
+       DBC *host_cursor = NULL;
+       DBC *join_cursor = NULL;
+       DBT key, data;
+       DBC *cur_arr[3];
+       int bdbres;
+       parsed_data_t pd;
+       char *tmp_zone, *tmp_host = NULL;
+       char *tmp = NULL;
+
+       UNUSED(driverarg);
+
+       memset(&key, 0, sizeof(DBT));
+       memset(&data, 0, sizeof(DBT));
+
+       /* set zone key */
+       key.data = tmp_zone = strdup(zone);
+       if (key.data == NULL) {
+               result = ISC_R_NOMEMORY;
+               goto lookup_cleanup;
+       }
+       key.size = strlen(key.data);
+
+       /* get a cursor to loop through zone data */
+       if (db->zone->cursor(db->zone, NULL, &zone_cursor, 0) != 0) {
+               result = ISC_R_FAILURE;
+               goto lookup_cleanup;
+       }
+
+       /* initialize zone_cursor with zone_key */
+       if (zone_cursor->c_get(zone_cursor, &key, &data, DB_SET) != 0) {
+               result = ISC_R_NOTFOUND;
+               goto lookup_cleanup;
+       }
+
+       /* set host key */
+       key.data = tmp_host = strdup(name);
+       if (key.data == NULL) {
+               result = ISC_R_NOMEMORY;
+               goto lookup_cleanup;
+       }
+       key.size = strlen(key.data);
+
+       /* get a cursor to loop through host data */
+       if (db->host->cursor(db->host, NULL, &host_cursor, 0) != 0) {
+               result = ISC_R_FAILURE;
+               goto lookup_cleanup;
+       }
+
+       /* initialize host_cursor with host_key */
+       if (host_cursor->c_get(host_cursor, &key, &data, DB_SET) != 0) {
+               result = ISC_R_NOTFOUND;
+               goto lookup_cleanup;
+       }
+
+       cur_arr[0] = zone_cursor;
+       cur_arr[1] = host_cursor;
+       cur_arr[2] = NULL;
+
+       db->data->join(db->data, cur_arr, &join_cursor, 0);
+
+       while ((bdbres = join_cursor->c_get(join_cursor, &key,
+                                           &data, 0)) == 0) {
+
+               tmp = realloc(tmp, data.size + 1);
+               if (tmp == NULL)
+                       goto lookup_cleanup;
+
+               strncpy(tmp, data.data, data.size);
+               tmp[data.size] = '\0';
+
+               if (bdb_parse_data(tmp, &pd) != ISC_R_SUCCESS)
+                       goto lookup_cleanup;
+
+               result = dns_sdlz_putrr(lookup, pd.type, pd.ttl, pd.data);
+
+               if (result != ISC_R_SUCCESS)
+                       goto lookup_cleanup;
+       } /* end while loop */
+
+ lookup_cleanup:
+
+       if (tmp != NULL)
+               free(tmp);
+       if (tmp_zone != NULL)
+               free(tmp_zone);
+       if (tmp_host != NULL)
+               free(tmp_host);
+
+       /* get rid of the joined cusor */
+       if (join_cursor != NULL)
+               join_cursor->c_close(join_cursor);
+
+       /* get rid of zone_cursor */
+       if (zone_cursor != NULL)
+               zone_cursor->c_close(zone_cursor);
+
+       /* get rid of host_cursor */
+       if (host_cursor != NULL)
+               host_cursor->c_close(host_cursor);
+
+       return result;
+
+       return ISC_R_NOTFOUND;
+}
+
+
+/*% Initializes, sets flags and then opens Berkeley databases. */
+
+static isc_result_t
+bdb_opendb(DB_ENV *db_env, DBTYPE db_type, DB **db, const char *db_name,
+          char *db_file, int flags) {
+
+       int result;
+
+       /* Initialize the database. */
+       if ((result = db_create(db, db_env, 0)) != 0) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "BDB could not initialize %s database. "
+                             "BDB error: %s",
+                             db_name, db_strerror(result));
+               return ISC_R_FAILURE;
+       }
+
+       /* set database flags. */
+       if ((result = (*db)->set_flags(*db, flags)) != 0) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "BDB could not set flags for %s database. "
+                             "BDB error: %s",
+                             db_name, db_strerror(result));
+               return ISC_R_FAILURE;
+       }
+
+       /* open the database. */
+       if ((result = (*db)->open(*db, NULL, db_file, db_name, db_type,
+                                 DB_RDONLY | bdb_threads, 0)) != 0) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "BDB could not open %s database in %s. "
+                             "BDB error: %s",
+                             db_name, db_file, db_strerror(result));
+               return ISC_R_FAILURE;
+       }
+
+       return ISC_R_SUCCESS;
+}
+
+static isc_result_t
+bdb_create(const char *dlzname, unsigned int argc, char *argv[],
+          void *driverarg, void **dbdata)
+{
+       isc_result_t result;
+       int bdbres;
+       bdb_instance_t *db = NULL;
+
+       UNUSED(dlzname);
+       UNUSED(driverarg);
+
+       /* verify we have 3 arg's passed to the driver */
+       if (argc != 3) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Berkeley DB driver requires at least "
+                             "2 command line args.");
+               return (ISC_R_FAILURE);
+       }
+
+       /* allocate and zero memory for driver structure */
+       db = isc_mem_get(ns_g_mctx, sizeof(bdb_instance_t));
+       if (db == NULL) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Could not allocate memory for "
+                             "database instance object.");
+               return (ISC_R_NOMEMORY);
+       }
+       memset(db, 0, sizeof(bdb_instance_t));
+
+       /* attach to the memory context */
+       isc_mem_attach(ns_g_mctx, &db->mctx);
+
+       /* create BDB environment
+        * Basically BDB allocates and assigns memory to db->dbenv
+        */
+       bdbres = db_env_create(&db->dbenv, 0);
+       if (bdbres != 0) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "BDB environment could not be created. "
+                             "BDB error: %s",
+                             db_strerror(bdbres));
+               result = ISC_R_FAILURE;
+               goto init_cleanup;
+       }
+
+       /* open BDB environment */
+       bdbres = db->dbenv->open(db->dbenv, argv[1],
+                                DB_INIT_CDB | DB_INIT_MPOOL |
+                                bdb_threads | DB_CREATE,
+                                0);
+       if (bdbres != 0) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "BDB environment at '%s' could not be opened. "
+                             "BDB error: %s",
+                             argv[1], db_strerror(bdbres));
+               result = ISC_R_FAILURE;
+               goto init_cleanup;
+       }
+
+       /* open dlz_data database. */
+       result = bdb_opendb(db->dbenv, DB_UNKNOWN, &db->data,
+                           dlz_data, argv[2], 0);
+       if (result != ISC_R_SUCCESS)
+               goto init_cleanup;
+
+       /* open dlz_host database. */
+       result = bdb_opendb(db->dbenv, DB_UNKNOWN, &db->host,
+                           dlz_host, argv[2],
+                           DB_DUP | DB_DUPSORT);
+       if (result != ISC_R_SUCCESS)
+               goto init_cleanup;
+
+       /* open dlz_zone database. */
+       result = bdb_opendb(db->dbenv, DB_UNKNOWN, &db->zone,
+                           dlz_zone, argv[2],
+                           DB_DUP | DB_DUPSORT);
+       if (result != ISC_R_SUCCESS)
+               goto init_cleanup;
+
+       /* open dlz_client database. */
+       result = bdb_opendb(db->dbenv, DB_UNKNOWN, &db->client,
+                           dlz_client, argv[2], DB_DUP | DB_DUPSORT);
+       if (result != ISC_R_SUCCESS)
+               goto init_cleanup;
+
+       /* associate the host secondary database with the primary database */
+       bdbres = db->data->associate(db->data, NULL, db->host, NULL, 0);
+       if (bdbres != 0) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "BDB could not associate %s database with %s. "
+                             "BDB error: %s",
+                             dlz_host, dlz_data, db_strerror(bdbres));
+               result = ISC_R_FAILURE;
+               goto init_cleanup;
+       }
+
+       /* associate the zone secondary database with the primary database */
+       bdbres = db->data->associate(db->data, NULL, db->zone, NULL, 0);
+       if (bdbres != 0) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "BDB could not associate %s database with %s. "
+                             "BDB error: %s",
+                             dlz_zone, dlz_data, db_strerror(bdbres));
+               result = ISC_R_FAILURE;
+               goto init_cleanup;
+       }
+
+       *dbdata = db;
+
+       return(ISC_R_SUCCESS);
+
+ init_cleanup:
+
+       bdb_cleanup(db);
+       return result;
+}
+
+static void
+bdb_destroy(void *driverarg, void *dbdata)
+{
+       UNUSED(driverarg);
+
+       bdb_cleanup((bdb_instance_t *) dbdata);
+}
+
+/* bdb_authority not needed as authority data is returned by lookup */
+static dns_sdlzmethods_t dlz_bdb_methods = {
+       bdb_create,
+       bdb_destroy,
+       bdb_findzone,
+       bdb_lookup,
+       NULL,
+       bdb_allnodes,
+       bdb_allowzonexfr
+};
+
+/*%
+ * Wrapper around dns_sdlzregister().
+ */
+isc_result_t
+dlz_bdb_init(void) {
+       isc_result_t result;
+
+       /*
+        * Write debugging message to log
+        */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                     "Registering DLZ bdb driver.");
+
+       result = dns_sdlzregister("bdb", &dlz_bdb_methods, NULL,
+                                 DNS_SDLZFLAG_RELATIVEOWNER |
+                                 DNS_SDLZFLAG_RELATIVERDATA |
+                                 DNS_SDLZFLAG_THREADSAFE,
+                                 ns_g_mctx, &dlz_bdb);
+       if (result != ISC_R_SUCCESS) {
+               UNEXPECTED_ERROR(__FILE__, __LINE__,
+                                "dns_sdlzregister() failed: %s",
+                                isc_result_totext(result));
+               result = ISC_R_UNEXPECTED;
+       }
+
+
+       return result;
+}
+
+/*%
+ * Wrapper around dns_sdlzunregister().
+ */
+void
+dlz_bdb_clear(void) {
+
+       /*
+        * Write debugging message to log
+        */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                     "Unregistering DLZ bdb driver.");
+
+       if (dlz_bdb != NULL)
+               dns_sdlzunregister(&dlz_bdb);
+}
+
+#endif
diff --git a/contrib/dlz/drivers/dlz_bdbhpt_driver.c b/contrib/dlz/drivers/dlz_bdbhpt_driver.c
new file mode 100644 (file)
index 0000000..da5b235
--- /dev/null
@@ -0,0 +1,860 @@
+/*
+ * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
+ *
+ * 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 STICHTING NLNET
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * STICHTING NLNET 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 development of Dynamically Loadable Zones (DLZ) for Bind 9 was
+ * conceived and contributed by Rob Butler.
+ *
+ * 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 ROB BUTLER
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * ROB BUTLER 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.
+ */
+
+/*
+ * Copyright (C) 1999-2001  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.
+ */
+
+#ifdef DLZ_BDB
+
+#include <config.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <dns/log.h>
+#include <dns/sdlz.h>
+#include <dns/result.h>
+
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/result.h>
+#include <isc/util.h>
+
+#include <named/globals.h>
+
+#include <dlz/dlz_bdbhpt_driver.h>
+
+#include <db.h>
+
+static dns_sdlzimplementation_t *dlz_bdbhpt = NULL;
+
+/* should the bdb driver use threads. */
+#ifdef ISC_PLATFORM_USETHREADS
+#define bdbhpt_threads DB_THREAD
+#else
+#define bdbhpt_threads 0
+#endif
+
+/* bdbhpt database names */
+#define dlz_data "dns_data"
+#define dlz_zone "dns_zone"
+#define dlz_xfr "dns_xfr"
+#define dlz_client "dns_client"
+
+       /* This structure contains all the Berkeley DB handles
+        * for this instance of the bdbhpt driver.
+        */
+
+typedef struct bdbhpt_instance {
+       DB_ENV  *dbenv;         /*%< bdbhpt environment */
+       DB      *data;          /*%< dns_data database handle */
+       DB      *zone;          /*%< zone database handle */
+       DB      *xfr;           /*%< zone xfr database handle */
+       DB      *client;        /*%< client database handle */
+       isc_mem_t *mctx;        /*%< memory context */
+
+} bdbhpt_instance_t;
+
+typedef struct bdbhpt_parsed_data {
+       char *host;
+       char *type;
+       int ttl;
+       char *data;
+} bdbhpt_parsed_data_t;
+
+
+/* forward reference */
+
+static isc_result_t
+bdbhpt_findzone(void *driverarg, void *dbdata, const char *name);
+
+/*%
+ * Reverses a string in place.
+ */
+
+static char *bdbhpt_strrev(char *str)
+{
+       char *p1, *p2;
+
+       if (! str || ! *str)
+               return str;
+       for (p1 = str, p2 = str + strlen(str) - 1; p2 > p1; ++p1, --p2)
+       {
+               *p1 ^= *p2;
+               *p2 ^= *p1;
+               *p1 ^= *p2;
+       }
+       return str;
+}
+
+/*%
+ * Parses the DBT from the Berkeley DB into a parsed_data record
+ * The parsed_data record should be allocated before and passed into the
+ * bdbhpt_parse_data function.  The char (type & data) fields should not
+ * be "free"d as that memory is part of the DBT data field.  It will be
+ * "free"d when the DBT is freed.
+ */
+
+static isc_result_t
+bdbhpt_parse_data(char *in, bdbhpt_parsed_data_t *pd) {
+
+       char *endp, *ttlStr;
+       char *tmp = in;
+       char *lastchar = (char *) &tmp[strlen(tmp)];
+
+       /*%
+        * String should be formated as:
+        *   replication_id
+        *   (a space)
+        *   host_name
+        *   (a space)
+        *   ttl
+        *   (a space)
+        *   type
+        *   (a space)
+        *   remaining data
+        *
+        * examples:
+        *
+        * 9191 host 10 A 127.0.0.1
+        * server1_212 host 10 A 127.0.0.2
+        * {xxxx-xxxx-xxxx-xxxx-xxxx} host 10 MX 20 mail.example.com
+        */
+
+       /*
+        * we don't need the replication id, so don't
+        * bother saving a pointer to it.
+        */
+
+       /* find space after replication id */
+       tmp = strchr(tmp, ' ');
+       /* verify we found a space */
+       if (tmp == NULL)
+               return ISC_R_FAILURE;
+       /* make sure it is safe to increment pointer */
+       if (++tmp > lastchar)
+               return ISC_R_FAILURE;
+
+       /* save pointer to host */
+       pd->host = tmp;
+
+       /* find space after host and change it to a '\0' */
+       tmp = strchr(tmp, ' ');
+       /* verify we found a space */
+       if (tmp == NULL)
+               return ISC_R_FAILURE;
+       /* change the space to a null (string terminator) */
+       tmp[0] = '\0';
+       /* make sure it is safe to increment pointer */
+       if (++tmp > lastchar)
+               return ISC_R_FAILURE;
+
+       /* save pointer to ttl string */
+       ttlStr = tmp;
+
+       /* find space after ttl and change it to a '\0' */
+       tmp = strchr(tmp, ' ');
+       /* verify we found a space */
+       if (tmp == NULL)
+               return ISC_R_FAILURE;
+       /* change the space to a null (string terminator) */
+       tmp[0] = '\0';
+       /* make sure it is safe to increment pointer */
+       if (++tmp > lastchar)
+               return ISC_R_FAILURE;
+
+       /* save pointer to dns type */
+       pd->type = tmp;
+
+       /* find space after type and change it to a '\0' */
+       tmp = strchr(tmp, ' ');
+       /* verify we found a space */
+       if (tmp == NULL)
+               return ISC_R_FAILURE;
+       /* change the space to a null (string terminator) */
+       tmp[0] = '\0';
+       /* make sure it is safe to increment pointer */
+       if (++tmp > lastchar)
+               return ISC_R_FAILURE;
+
+       /* save pointer to remainder of DNS data */
+       pd->data = tmp;
+
+       /* convert ttl string to integer */
+       pd->ttl = strtol(ttlStr, &endp, 10);
+       if (*endp != '\0' || pd->ttl < 0) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "bdbhpt driver ttl must be a postive number");
+               return ISC_R_FAILURE;
+       }
+
+       /* if we get this far everything should have worked. */
+       return ISC_R_SUCCESS;
+}
+
+/*
+ * DLZ methods
+ */
+
+static isc_result_t
+bdbhpt_allowzonexfr(void *driverarg, void *dbdata, const char *name,
+                   const char *client)
+{
+       isc_result_t result;
+       bdbhpt_instance_t *db = (bdbhpt_instance_t *) dbdata;
+       DBT key, data;
+
+       /* check to see if we are authoritative for the zone first. */
+       result = bdbhpt_findzone(driverarg, dbdata, name);
+       if (result != ISC_R_SUCCESS)
+               return (ISC_R_NOTFOUND);
+
+       memset(&key, 0, sizeof(DBT));
+       key.flags = DB_DBT_MALLOC;
+       key.data = strdup(name);
+       if (key.data == NULL) {
+               result = ISC_R_NOMEMORY;
+               goto xfr_cleanup;
+       }
+       key.size = strlen(key.data);
+
+       memset(&data, 0, sizeof(DBT));
+       data.flags = DB_DBT_MALLOC;
+       data.data = strdup(client);
+       if (data.data == NULL) {
+               result = ISC_R_NOMEMORY;
+               goto xfr_cleanup;
+       }
+       data.size = strlen(data.data);
+
+       switch(db->client->get(db->client, NULL, &key, &data, DB_GET_BOTH)) {
+       case DB_NOTFOUND:
+               result = ISC_R_NOTFOUND;
+               break;
+       case 0:
+               result = ISC_R_SUCCESS;
+               break;
+       default:
+               result = ISC_R_FAILURE;
+       }
+
+ xfr_cleanup:
+
+       /* free any memory duplicate string in the key field */
+       if (key.data != NULL)
+               free(key.data);
+
+       /* free any memory allocated to the data field. */
+       if (data.data != NULL)
+               free(data.data);
+
+       return result;
+
+}
+
+/*%
+ * BDB does not allow a secondary index on a database that allows
+ * duplicates.  We have a few options:
+ *
+ * 1) kill speed by having lookup method use a secondary db which
+ * is associated to the primary DB with the DNS data.  Then have
+ * another secondary db for zone transfer which also points to
+ * the dns_data primary.  NO - The  point of this driver is
+ * lookup performance.
+ *
+ * 2) Blow up database size by storing DNS data twice.  Once for
+ * the lookup (dns_data) database, and a second time for the zone
+ * transfer (dns_xfr) database. NO - That would probably require
+ * a larger cache to provide good performance.  Also, that would
+ * make the DB larger on disk potentially slowing it as well.
+ *
+ * 3) Loop through the dns_xfr database with a cursor to get
+ * all the different hosts in a zone.  Then use the zone & host
+ * together to lookup the data in the dns_data database. YES -
+ * This may slow down zone xfr's a little, but that's ok they
+ * don't happen as often and don't need to be as fast. We can
+ * also use this table when deleting a zone (The BDB driver
+ * is read only - the delete would be used during replication
+ * updates by a separate process).
+ */
+
+static isc_result_t
+bdbhpt_allnodes(const char *zone, void *driverarg, void *dbdata,
+               dns_sdlzallnodes_t *allnodes)
+{
+
+       isc_result_t result = ISC_R_NOTFOUND;
+       bdbhpt_instance_t *db = (bdbhpt_instance_t *) dbdata;
+       DBC *xfr_cursor = NULL;
+       DBC *dns_cursor = NULL;
+       DBT xfr_key, xfr_data, dns_key, dns_data;
+       int xfr_flags;
+       int dns_flags;
+       int bdbhptres;
+       bdbhpt_parsed_data_t pd;
+       char *tmp = NULL, *tmp_zone, *tmp_zone_host = NULL;
+
+       UNUSED(driverarg);
+
+       memset(&xfr_key, 0, sizeof(DBT));
+       memset(&xfr_data, 0, sizeof(DBT));
+       memset(&dns_key, 0, sizeof(DBT));
+       memset(&dns_data, 0, sizeof(DBT));
+
+       xfr_key.data = tmp_zone = strdup(zone);
+       if (xfr_key.data == NULL)
+               return (ISC_R_NOMEMORY);
+
+       xfr_key.size = strlen(xfr_key.data);
+
+       /* get a cursor to loop through dns_xfr table */
+       if (db->xfr->cursor(db->xfr, NULL, &xfr_cursor, 0) != 0) {
+               result = ISC_R_FAILURE;
+               goto allnodes_cleanup;
+       }
+
+       /* get a cursor to loop through dns_data table */
+       if (db->data->cursor(db->data, NULL, &dns_cursor, 0) != 0) {
+               result = ISC_R_FAILURE;
+               goto allnodes_cleanup;
+       }
+
+       xfr_flags = DB_SET;
+
+       /* loop through xfr table for specified zone. */
+       while ((bdbhptres = xfr_cursor->c_get(xfr_cursor, &xfr_key, &xfr_data,
+                                             xfr_flags)) == 0) {
+
+               xfr_flags = DB_NEXT_DUP;
+
+               /* +1 to allow for space between zone and host names */
+               dns_key.size = xfr_data.size + xfr_key.size + 1;
+
+               /* +1 to allow for null term at end of string. */
+               dns_key.data = tmp_zone_host = malloc(dns_key.size + 1);
+               if (dns_key.data == NULL)
+                       goto allnodes_cleanup;
+
+               /*
+                * construct search key for dns_data.
+                * zone_name(a space)host_name
+                */
+               strcpy(dns_key.data, zone);
+               strcat(dns_key.data, " ");
+               strncat(dns_key.data, xfr_data.data, xfr_data.size);
+
+               dns_flags = DB_SET;
+
+               while ((bdbhptres = dns_cursor->c_get(dns_cursor, &dns_key,
+                                                     &dns_data,
+                                                     dns_flags)) == 0) {
+
+                       dns_flags = DB_NEXT_DUP;
+
+                       /* +1 to allow for null term at end of string. */
+                       tmp = realloc(tmp, dns_data.size + 1);
+                       if (tmp == NULL)
+                               goto allnodes_cleanup;
+
+                       /* copy data to tmp string, and append null term. */
+                       strncpy(tmp, dns_data.data, dns_data.size);
+                       tmp[dns_data.size] = '\0';
+
+                       /* split string into dns data parts. */
+                       if (bdbhpt_parse_data(tmp, &pd) != ISC_R_SUCCESS)
+                               goto allnodes_cleanup;
+
+                       result = dns_sdlz_putnamedrr(allnodes, pd.host,
+                                                    pd.type, pd.ttl, pd.data);
+                       if (result != ISC_R_SUCCESS)
+                               goto allnodes_cleanup;
+
+               }  /* end inner while loop */
+
+               /* clean up memory */
+               if (tmp_zone_host != NULL) {
+                       free(tmp_zone_host);
+                       tmp_zone_host = NULL;
+               }
+       } /* end outer while loop */
+
+ allnodes_cleanup:
+
+       /* free any memory */
+       if (tmp != NULL)
+               free(tmp);
+
+       if (tmp_zone_host != NULL)
+               free(tmp_zone_host);
+
+       if (tmp_zone != NULL)
+               free(tmp_zone);
+
+       /* get rid of cursors */
+       if (xfr_cursor != NULL)
+               xfr_cursor->c_close(xfr_cursor);
+
+       if (dns_cursor != NULL)
+               dns_cursor->c_close(xfr_cursor);
+
+       return result;
+}
+
+/*%
+ * Performs bdbhpt cleanup.
+ * Used by bdbhpt_create if there is an error starting up.
+ * Used by bdbhpt_destroy when the driver is shutting down.
+ */
+
+static void
+bdbhpt_cleanup(bdbhpt_instance_t *db) {
+
+       isc_mem_t *mctx;
+
+       /* close databases */
+       if (db->data != NULL)
+               db->data->close(db->data, 0);
+       if (db->xfr != NULL)
+               db->xfr->close(db->xfr, 0);
+       if (db->zone != NULL)
+               db->zone->close(db->zone, 0);
+       if (db->client != NULL)
+               db->client->close(db->client, 0);
+
+       /* close environment */
+       if (db->dbenv != NULL)
+               db->dbenv->close(db->dbenv, 0);
+
+       /* cleanup memory */
+       if (db->mctx != NULL) {
+               /* save mctx for later */
+               mctx = db->mctx;
+               /* return, and detach the memory */
+               isc_mem_put(mctx, db, sizeof(bdbhpt_instance_t));
+               isc_mem_detach(&mctx);
+       }
+}
+
+static isc_result_t
+bdbhpt_findzone(void *driverarg, void *dbdata, const char *name)
+{
+
+       isc_result_t result;
+       bdbhpt_instance_t *db = (bdbhpt_instance_t *) dbdata;
+       DBT key, data;
+
+       UNUSED(driverarg);
+
+       memset(&key, 0, sizeof(DBT));
+       memset(&data, 0, sizeof(DBT));
+       data.flags = DB_DBT_MALLOC;
+
+       key.data = strdup(name);
+
+       if (key.data == NULL)
+               return (ISC_R_NOMEMORY);
+
+       /*
+        * reverse string to take advantage of BDB locality of reference
+        * if we need futher lookups because the zone doesn't match the
+        * first time.
+        */
+       key.data = bdbhpt_strrev(key.data);
+       key.size = strlen(key.data);
+
+       switch(db->zone->get(db->zone, NULL, &key, &data, 0)) {
+       case DB_NOTFOUND:
+               result = ISC_R_NOTFOUND;
+               break;
+       case 0:
+               result = ISC_R_SUCCESS;
+               break;
+       default:
+               result = ISC_R_FAILURE;
+       }
+
+       /* free any memory duplicate string in the key field */
+       if (key.data != NULL)
+               free(key.data);
+
+       /* free any memory allocated to the data field. */
+       if (data.data != NULL)
+               free(data.data);
+
+       return result;
+}
+
+static isc_result_t
+bdbhpt_lookup(const char *zone, const char *name, void *driverarg,
+             void *dbdata, dns_sdlzlookup_t *lookup)
+{
+
+       isc_result_t result = ISC_R_NOTFOUND;
+       bdbhpt_instance_t *db = (bdbhpt_instance_t *) dbdata;
+       DBC *data_cursor = NULL;
+       DBT key, data;
+       int bdbhptres;
+       int flags;
+
+       bdbhpt_parsed_data_t pd;
+       char *tmp = NULL;
+       char *keyStr = NULL;
+
+       UNUSED(driverarg);
+
+       memset(&key, 0, sizeof(DBT));
+       memset(&data, 0, sizeof(DBT));
+
+       key.size = strlen(zone) + strlen(name) + 1;
+
+       /* allocate mem for key */
+       key.data = keyStr = malloc((key.size + 1) * sizeof(char));
+
+       if (keyStr == NULL)
+               return ISC_R_NOMEMORY;
+
+       strcpy(keyStr, zone);
+       strcat(keyStr, " ");
+       strcat(keyStr, name);
+
+       /* get a cursor to loop through data */
+       if (db->data->cursor(db->data, NULL, &data_cursor, 0) != 0) {
+               result = ISC_R_FAILURE;
+               goto lookup_cleanup;
+       }
+
+       result = ISC_R_NOTFOUND;
+
+       flags = DB_SET;
+       while ((bdbhptres = data_cursor->c_get(data_cursor, &key, &data,
+                                              flags)) == 0) {
+
+               flags = DB_NEXT_DUP;
+               tmp = realloc(tmp, data.size + 1);
+               if (tmp == NULL)
+                       goto lookup_cleanup;
+
+               strncpy(tmp, data.data, data.size);
+               tmp[data.size] = '\0';
+
+               if (bdbhpt_parse_data(tmp, &pd) != ISC_R_SUCCESS)
+                       goto lookup_cleanup;
+
+               result = dns_sdlz_putrr(lookup, pd.type, pd.ttl, pd.data);
+
+               if (result != ISC_R_SUCCESS)
+                       goto lookup_cleanup;
+       } /* end while loop */
+
+ lookup_cleanup:
+
+       /* get rid of cursor */
+       if (data_cursor != NULL)
+               data_cursor->c_close(data_cursor);
+
+       if (keyStr != NULL)
+               free(keyStr);
+       if (tmp != NULL)
+               free(tmp);
+
+       return result;
+}
+
+/*% Initializes, sets flags and then opens Berkeley databases. */
+
+static isc_result_t
+bdbhpt_opendb(DB_ENV *db_env, DBTYPE db_type, DB **db, const char *db_name,
+             char *db_file, int flags) {
+
+       int result;
+
+       /* Initialize the database. */
+       if ((result = db_create(db, db_env, 0)) != 0) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "bdbhpt could not initialize %s database. "
+                             "bdbhpt error: %s",
+                             db_name, db_strerror(result));
+               return ISC_R_FAILURE;
+       }
+
+       /* set database flags. */
+       if ((result = (*db)->set_flags(*db, flags)) != 0) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "bdbhpt could not set flags for %s database. "
+                             "bdbhpt error: %s",
+                             db_name, db_strerror(result));
+               return ISC_R_FAILURE;
+       }
+
+       /* open the database. */
+       if ((result = (*db)->open(*db, NULL, db_file, db_name, db_type,
+                                 DB_RDONLY | bdbhpt_threads, 0)) != 0) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "bdbhpt could not open %s database in %s. "
+                             "bdbhpt error: %s",
+                             db_name, db_file, db_strerror(result));
+               return ISC_R_FAILURE;
+       }
+
+       return ISC_R_SUCCESS;
+}
+
+static isc_result_t
+bdbhpt_create(const char *dlzname, unsigned int argc, char *argv[],
+             void *driverarg, void **dbdata)
+{
+       isc_result_t result;
+       int bdbhptres;
+       int bdbFlags = 0;
+       bdbhpt_instance_t *db = NULL;
+
+       UNUSED(dlzname);
+       UNUSED(driverarg);
+
+       /* verify we have 4 arg's passed to the driver */
+       if (argc != 4) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "bdbhpt driver requires at least "
+                             "3 command line args.");
+               return (ISC_R_FAILURE);
+       }
+
+       switch((char) *argv[1]) {
+               /*
+                * Transactional mode.  Highest safety - lowest speed.
+                */
+       case 'T':
+       case 't':
+               bdbFlags = DB_INIT_MPOOL | DB_INIT_LOCK |
+                          DB_INIT_LOG | DB_INIT_TXN;
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
+                             "bdbhpt driver using transactional mode.");
+               break;
+               /*
+                * Concurrent mode.  Lower safety (no rollback) -
+                * higher speed.
+                */
+       case 'C':
+       case 'c':
+               bdbFlags = DB_INIT_CDB | DB_INIT_MPOOL;
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
+                             "bdbhpt driver using concurrent mode.");
+               break;
+               /*
+                * Private mode. No inter-process communication & no locking.
+                * Lowest saftey - highest speed.
+                */
+       case 'P':
+       case 'p':
+               bdbFlags = DB_PRIVATE | DB_INIT_MPOOL;
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
+                             "bdbhpt driver using private mode.");
+               break;
+       default:
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "bdbhpt driver requires the operating mode "
+                             "be set to P or C or T.  You specified '%s'",
+                             argv[1]);
+               return (ISC_R_FAILURE);
+       }
+
+       /* allocate and zero memory for driver structure */
+       db = isc_mem_get(ns_g_mctx, sizeof(bdbhpt_instance_t));
+       if (db == NULL) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Could not allocate memory for "
+                             "database instance object.");
+               return (ISC_R_NOMEMORY);
+       }
+       memset(db, 0, sizeof(bdbhpt_instance_t));
+
+       /* attach to the memory context */
+       isc_mem_attach(ns_g_mctx, &db->mctx);
+
+       /*
+        * create bdbhpt environment
+        * Basically bdbhpt allocates and assigns memory to db->dbenv
+        */
+       bdbhptres = db_env_create(&db->dbenv, 0);
+       if (bdbhptres != 0) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "bdbhpt environment could not be created. "
+                             "bdbhpt error: %s",
+                             db_strerror(bdbhptres));
+               result = ISC_R_FAILURE;
+               goto init_cleanup;
+       }
+
+       /* open bdbhpt environment */
+       bdbhptres = db->dbenv->open(db->dbenv, argv[2],
+                                   bdbFlags | bdbhpt_threads | DB_CREATE, 0);
+       if (bdbhptres != 0) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "bdbhpt environment at '%s' could not be opened."
+                             " bdbhpt error: %s",
+                             argv[2], db_strerror(bdbhptres));
+               result = ISC_R_FAILURE;
+               goto init_cleanup;
+       }
+
+       /* open dlz_data database. */
+       result = bdbhpt_opendb(db->dbenv, DB_UNKNOWN, &db->data,
+                              dlz_data, argv[3], DB_DUP | DB_DUPSORT);
+       if (result != ISC_R_SUCCESS)
+               goto init_cleanup;
+
+       /* open dlz_xfr database. */
+       result = bdbhpt_opendb(db->dbenv, DB_UNKNOWN, &db->xfr,
+                              dlz_xfr, argv[3], DB_DUP | DB_DUPSORT);
+       if (result != ISC_R_SUCCESS)
+               goto init_cleanup;
+
+       /* open dlz_zone database. */
+       result = bdbhpt_opendb(db->dbenv, DB_UNKNOWN, &db->zone,
+                              dlz_zone, argv[3], 0);
+       if (result != ISC_R_SUCCESS)
+               goto init_cleanup;
+
+       /* open dlz_client database. */
+       result = bdbhpt_opendb(db->dbenv, DB_UNKNOWN, &db->client,
+                              dlz_client, argv[3], DB_DUP | DB_DUPSORT);
+       if (result != ISC_R_SUCCESS)
+               goto init_cleanup;
+
+       *dbdata = db;
+
+       return(ISC_R_SUCCESS);
+
+ init_cleanup:
+
+       bdbhpt_cleanup(db);
+       return result;
+}
+
+static void
+bdbhpt_destroy(void *driverarg, void *dbdata)
+{
+       UNUSED(driverarg);
+
+       bdbhpt_cleanup((bdbhpt_instance_t *) dbdata);
+}
+
+/*
+ * bdbhpt_authority not needed as authority data is returned by lookup
+ */
+static dns_sdlzmethods_t dlz_bdbhpt_methods = {
+       bdbhpt_create,
+       bdbhpt_destroy,
+       bdbhpt_findzone,
+       bdbhpt_lookup,
+       NULL,
+       bdbhpt_allnodes,
+       bdbhpt_allowzonexfr
+};
+
+/*%
+ * Wrapper around dns_sdlzregister().
+ */
+isc_result_t
+dlz_bdbhpt_init(void) {
+       isc_result_t result;
+
+       /*
+        * Write debugging message to log
+        */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                     "Registering DLZ bdbhpt driver.");
+
+       result = dns_sdlzregister("bdbhpt", &dlz_bdbhpt_methods, NULL,
+                                 DNS_SDLZFLAG_RELATIVEOWNER |
+                                 DNS_SDLZFLAG_RELATIVERDATA |
+                                 DNS_SDLZFLAG_THREADSAFE,
+                                 ns_g_mctx, &dlz_bdbhpt);
+       if (result != ISC_R_SUCCESS) {
+               UNEXPECTED_ERROR(__FILE__, __LINE__,
+                                "dns_sdlzregister() failed: %s",
+                                isc_result_totext(result));
+               result = ISC_R_UNEXPECTED;
+       }
+
+
+       return result;
+}
+
+/*%
+ * Wrapper around dns_sdlzunregister().
+ */
+void
+dlz_bdbhpt_clear(void) {
+
+       /*
+        * Write debugging message to log
+        */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                     "Unregistering DLZ bdbhpt driver.");
+
+       if (dlz_bdbhpt != NULL)
+               dns_sdlzunregister(&dlz_bdbhpt);
+}
+
+#endif
diff --git a/contrib/dlz/drivers/dlz_drivers.c b/contrib/dlz/drivers/dlz_drivers.c
new file mode 100644 (file)
index 0000000..e2c6a6e
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2005  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC 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: dlz_drivers.c,v 1.2 2005/09/05 00:10:55 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+
+#include <isc/result.h>
+
+/*
+ * Pull in declarations for this module's functions.
+ */
+
+#include <dlz/dlz_drivers.h>
+
+/*
+ * Pull in driver-specific stuff.
+ */
+
+#ifdef DLZ_STUB
+#include <dlz/dlz_stub_driver.h>
+#endif
+
+#ifdef DLZ_POSTGRES
+#include <dlz/dlz_postgres_driver.h>
+#endif
+
+#ifdef DLZ_MYSQL
+#include <dlz/dlz_mysql_driver.h>
+#endif
+
+#ifdef DLZ_FILESYSTEM
+#include <dlz/dlz_filesystem_driver.h>
+#endif
+
+#ifdef DLZ_BDB
+#include <dlz/dlz_bdb_driver.h>
+#include <dlz/dlz_bdbhpt_driver.h>
+#endif
+
+#ifdef DLZ_LDAP
+#include <dlz/dlz_ldap_driver.h>
+#endif
+
+#ifdef DLZ_ODBC
+#include <dlz/dlz_odbc_driver.h>
+#endif
+
+/*%
+ * Call init functions for all relevant DLZ drivers.
+ */
+
+isc_result_t
+dlz_drivers_init(void) {
+
+       isc_result_t result = ISC_R_SUCCESS;
+
+#ifdef DLZ_STUB
+       result = dlz_stub_init();
+       if (result != ISC_R_SUCCESS)
+               return (result);
+#endif
+
+#ifdef DLZ_POSTGRES
+       result = dlz_postgres_init();
+       if (result != ISC_R_SUCCESS)
+               return (result);
+#endif
+
+#ifdef DLZ_MYSQL
+       result = dlz_mysql_init();
+       if (result != ISC_R_SUCCESS)
+               return (result);
+#endif
+
+#ifdef DLZ_FILESYSTEM
+       result = dlz_fs_init();
+       if (result != ISC_R_SUCCESS)
+               return (result);
+#endif
+
+#ifdef DLZ_BDB
+       result = dlz_bdb_init();
+       if (result != ISC_R_SUCCESS)
+               return (result);
+       result = dlz_bdbhpt_init();
+       if (result != ISC_R_SUCCESS)
+               return (result);
+#endif
+
+#ifdef DLZ_LDAP
+       result = dlz_ldap_init();
+       if (result != ISC_R_SUCCESS)
+               return (result);
+#endif
+
+#ifdef DLZ_ODBC
+       result = dlz_odbc_init();
+       if (result != ISC_R_SUCCESS)
+               return (result);
+#endif
+
+       return (result);
+}
+
+/*%
+ * Call shutdown functions for all relevant DLZ drivers.
+ */
+
+void
+dlz_drivers_clear(void) {
+
+#ifdef DLZ_STUB
+       dlz_stub_clear();
+#endif
+
+#ifdef DLZ_POSTGRES
+        dlz_postgres_clear();
+#endif
+
+#ifdef DLZ_MYSQL
+       dlz_mysql_clear();
+#endif
+
+#ifdef DLZ_FILESYSTEM
+        dlz_fs_clear();
+#endif
+
+#ifdef DLZ_BDB
+        dlz_bdb_clear();
+        dlz_bdbhpt_clear();
+#endif
+
+#ifdef DLZ_LDAP
+        dlz_ldap_clear();
+#endif
+
+#ifdef DLZ_ODBC
+        dlz_odbc_clear();
+#endif
+
+}
diff --git a/contrib/dlz/drivers/dlz_filesystem_driver.c b/contrib/dlz/drivers/dlz_filesystem_driver.c
new file mode 100644 (file)
index 0000000..d41d5ac
--- /dev/null
@@ -0,0 +1,1048 @@
+/*
+ * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
+ *
+ * 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 STICHTING NLNET
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * STICHTING NLNET 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 development of Dynamically Loadable Zones (DLZ) for Bind 9 was
+ * conceived and contributed by Rob Butler.
+ *
+ * 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 ROB BUTLER
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * ROB BUTLER 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.
+ */
+
+/*
+ * Copyright (C) 1999-2001  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.
+ */
+
+#ifdef DLZ_FILESYSTEM
+
+#include <config.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <sys/stat.h>
+
+#include <dns/log.h>
+#include <dns/sdlz.h>
+#include <dns/result.h>
+
+#include <isc/dir.h>
+#include <isc/mem.h>
+#include <isc/platform.h>
+#include <isc/print.h>
+#include <isc/result.h>
+#include <isc/util.h>
+
+#include <named/globals.h>
+
+#include <dlz/dlz_filesystem_driver.h>
+
+static dns_sdlzimplementation_t *dlz_fs = NULL;
+
+typedef struct config_data {
+       char            *basedir;
+       int             basedirsize;
+       char            *datadir;
+       int             datadirsize;
+       char            *xfrdir;
+       int             xfrdirsize;
+       int             splitcnt;
+       char            separator;
+       char            pathsep;
+       isc_mem_t       *mctx;
+} config_data_t;
+
+typedef struct dir_entry dir_entry_t;
+
+struct dir_entry {
+       char dirpath[ISC_DIR_PATHMAX];
+       ISC_LINK(dir_entry_t)   link;
+};
+
+typedef ISC_LIST(dir_entry_t) dlist_t;
+
+/* forward reference */
+
+static void
+fs_destroy(void *driverarg, void *dbdata);
+
+/*
+ * Private methods
+ */
+
+static isc_boolean_t
+is_safe(const char *input)
+{
+       unsigned int i;
+       unsigned int len = strlen(input);
+
+        /* check that only allowed characters  are in the domain name */
+       for (i=0; i < len; i++) {
+               /* '.' is allowed, but has special requirements */
+               if (input[i] == '.') {
+                       /* '.' is not allowed as first char */
+                       if (i == 0)
+                               return ISC_FALSE;
+                       /* '..', two dots together is not allowed. */
+                       else if (input[i-1] == '.')
+                               return ISC_FALSE;
+                       /* '.' is not allowed as last char */
+                       if (i == len)
+                               return ISC_FALSE;
+                       /* only 1 dot in ok location, continue at next char */
+                       continue;
+               }
+               /* '-' is allowed, continue at next char */
+               if (input[i] == '-')
+                       continue;
+               /* 0-9 is allowed, continue at next char */
+               if (input[i] >= '0' && input[i] <= '9')
+                       continue;
+               /* A-Z uppercase is allowed, continue at next char */
+               if (input[i] >= 'A' && input[i] <= 'Z')
+                       continue;
+               /* a-z lowercase is allowed, continue at next char */
+               if (input[i] >= 'a' && input[i] <= 'z')
+                       continue;
+
+               /*
+                * colon needs to be allowed for IPV6 client
+                * addresses.  Not dangerous in domain names, as not a
+                * special char.
+                */
+               if (input[i] == ':')
+                       continue;
+
+               /*
+                * '@' needs to be allowed for in zone data.  Not
+                * dangerous in domain names, as not a special char.
+                */
+               if (input[i] == '@')
+                       continue;
+
+               /*
+                * if we reach this point we have encountered a
+                * disallowed char!
+                */
+               return ISC_FALSE;
+       }
+        /* everything ok. */
+       return ISC_TRUE;
+}
+
+static isc_result_t
+create_path_helper(char *out, const char *in, config_data_t *cd)
+{
+
+       char *tmpString;
+       char *tmpPtr;
+       int i;
+
+       tmpString = isc_mem_strdup(ns_g_mctx, in);
+       if (tmpString == NULL)
+               return (ISC_R_NOMEMORY);
+
+       /*
+        * don't forget is_safe guarantees '.' will NOT be the
+        * first/last char
+        */
+       while ((tmpPtr = strrchr(tmpString, '.')) != NULL) {
+               i = 0;
+               while (tmpPtr[i+1] != '\0') {
+                       if (cd->splitcnt < 1)
+                               strcat(out, (char *) &tmpPtr[i+1]);
+                       else
+                               strncat(out, (char *) &tmpPtr[i+1],
+                                       cd->splitcnt);
+                       strncat(out, (char *) &cd->pathsep, 1);
+                       if (cd->splitcnt == 0)
+                               break;
+                       if (strlen((char *) &tmpPtr[i+1]) <=
+                           (unsigned int) cd->splitcnt)
+                               break;
+                       i += cd->splitcnt;
+               }
+               tmpPtr[0] = '\0';
+       }
+
+       /* handle the "first" label properly */
+       i=0;
+       tmpPtr = tmpString;
+       while (tmpPtr[i] != '\0') {
+               if (cd->splitcnt < 1)
+                       strcat(out, (char *) &tmpPtr[i]);
+               else
+                       strncat(out, (char *) &tmpPtr[i], cd->splitcnt);
+               strncat(out, (char *) &cd->pathsep, 1);
+               if (cd->splitcnt == 0)
+                       break;
+               if (strlen((char *) &tmpPtr[i]) <=
+                   (unsigned int) cd->splitcnt)
+                       break;
+               i += cd->splitcnt;
+       }
+
+       isc_mem_free(ns_g_mctx, tmpString);
+       return (ISC_R_SUCCESS);
+}
+
+/*%
+ * Checks to make sure zone and host are safe.  If safe, then
+ * hashes zone and host strings to build a path.  If zone / host
+ * are not safe an error is returned.
+ */
+
+static isc_result_t
+create_path(const char *zone, const char *host, const char *client,
+           config_data_t *cd, char **path)
+{
+
+       char *tmpPath;
+       int pathsize;
+       int len;
+       isc_result_t result;
+
+       /* we require a zone & cd parameter */
+       REQUIRE(zone != NULL);
+       REQUIRE(cd != NULL);
+       /* require path to be a pointer to NULL */
+       REQUIRE(path != NULL && *path == NULL);
+       /*
+        * client and host may both be NULL, but they can't both be
+        * NON-NULL
+        */
+       REQUIRE( (host == NULL && client == NULL) ||
+                (host != NULL && client == NULL) ||
+                (host == NULL && client != NULL) );
+
+       /* if the requested zone is "unsafe", return error */
+       if (is_safe(zone) != ISC_TRUE)
+               return (ISC_R_FAILURE);
+
+       /* if host was passed, verify that it is safe */
+       if ((host != NULL) && (is_safe(host) != ISC_TRUE) )
+               return (ISC_R_FAILURE);
+
+       /* if host was passed, verify that it is safe */
+       if ((client != NULL) && (is_safe(client) != ISC_TRUE) )
+               return (ISC_R_FAILURE);
+
+       /* Determine how much memory the split up string will require */
+       if (host != NULL)
+               len = strlen(zone) + strlen(host);
+       else if (client != NULL)
+               len = strlen(zone) + strlen(client);
+       else
+               len = strlen(zone);
+
+       /*
+        * even though datadir and xfrdir will never be in the same
+        * string we only waste a few bytes by allocating for both,
+        * and then we are safe from buffer overruns.
+        */
+       pathsize = len + cd->basedirsize +
+                  cd->datadirsize + cd->xfrdirsize + 4;
+
+       /* if we are splitting names, we will need extra space. */
+       if (cd->splitcnt > 0)
+               pathsize += len/cd->splitcnt;
+
+       tmpPath = isc_mem_allocate(ns_g_mctx , pathsize * sizeof(char));
+       if (tmpPath == NULL) {
+               /* write error message */
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Filesystem driver unable to "
+                             "allocate memory in create_path().");
+               result = ISC_R_NOMEMORY;
+               goto cleanup_mem;
+       }
+
+       /*
+        * build path string.
+        * start out with base directory.
+        */
+       strcpy(tmpPath, cd->basedir);
+
+       /* add zone name - parsed properly */
+       if ((result = create_path_helper(tmpPath, zone, cd)) != ISC_R_SUCCESS)
+               goto cleanup_mem;
+
+       /*
+        * When neither client or host is passed we are building a
+        * path to see if a zone is supported.  We require that a zone
+        * path have the "data dir" directory contained within it so
+        * that we know this zone is really supported.  Otherwise,
+        * this zone may not really be supported because we are
+        * supporting a delagated sub zone.
+        *
+        * Example:
+        *
+        * We are supporting long.domain.com and using a splitcnt of
+        * 0.  the base dir is "/base-dir/" and the data dir is
+        * "/.datadir" We want to see if we are authoritative for
+        * domain.com.  Path /base-dir/com/domain/.datadir since
+        * /base-dir/com/domain/.datadir does not exist, we are not
+        * authoritative for the domain "domain.com".  However we are
+        * authoritative for the domain "long.domain.com" because the
+        * path /base-dir/com/domain/long/.datadir does exist!
+        */
+
+       /* if client is passed append xfr dir, otherwise append data dir */
+       if (client != NULL) {
+               strcat(tmpPath, cd->xfrdir);
+               strncat(tmpPath, (char *) &cd->pathsep, 1);
+               strcat(tmpPath, client);
+       } else {
+               strcat(tmpPath, cd->datadir);
+       }
+
+       /* if host not null, add it. */
+       if (host != NULL) {
+               strncat(tmpPath, (char *) &cd->pathsep, 1);
+               if ((result = create_path_helper(tmpPath, host,
+                                                cd)) != ISC_R_SUCCESS)
+                       goto cleanup_mem;
+       }
+
+       /* return the path we built. */
+       *path = tmpPath;
+
+       /* return success */
+       result = ISC_R_SUCCESS;
+
+ cleanup_mem:
+       /* cleanup memory */
+
+       /* free tmpPath memory */
+       if (tmpPath != NULL && result != ISC_R_SUCCESS)
+               isc_mem_free(ns_g_mctx, tmpPath);
+
+       /* free tmpPath memory */
+       return result;
+}
+
+static isc_result_t
+process_dir(isc_dir_t dir, void *passback, config_data_t *cd,
+           dlist_t *dir_list, unsigned int basedirlen)
+{
+
+       char tmp[ISC_DIR_PATHMAX + ISC_DIR_NAMEMAX];
+       int astPos;
+       struct stat     sb;
+       isc_result_t result = ISC_R_FAILURE;
+       char *endp;
+       char *type;
+       char *ttlStr;
+       char *data;
+       char host[ISC_DIR_NAMEMAX];
+       char *tmpString;
+       char *tmpPtr;
+       int ttl;
+       int i;
+       int len;
+       dir_entry_t *direntry;
+       isc_boolean_t foundHost;
+
+       tmp[0] = '\0'; /* set 1st byte to '\0' so strcpy works right. */
+       host[0] = '\0';
+       foundHost = ISC_FALSE;
+
+       /* copy base directory name to tmp. */
+       strcpy(tmp, dir.dirname);
+
+       /* dir.dirname will always have '*' as the last char. */
+       astPos = strlen(dir.dirname) - 1;
+
+       /* if dir_list != NULL, were are performing a zone xfr */
+       if (dir_list != NULL) {
+               /* if splitcnt == 0, determine host from path. */
+               if (cd->splitcnt == 0) {
+                       if (strlen(tmp) - 3 > basedirlen) {
+                               tmp[astPos-1] = '\0';
+                               tmpString = (char *) &tmp[basedirlen+1];
+                               /* handle filesystem's special wildcard "-"  */
+                               if (strcmp(tmpString, "-") == 0) {
+                                       strcpy(host, "*");
+                               } else {
+                                       /*
+                                        * not special wildcard -- normal name
+                                        */
+                                       while ((tmpPtr = strrchr(tmpString,
+                                                                cd->pathsep))
+                                              != NULL) {
+                                               strcat(host, tmpPtr + 1);
+                                               strcat(host, ".");
+                                               tmpPtr[0] = '\0';
+                                       }
+                                       strcat(host, tmpString);
+                               }
+
+                               foundHost = ISC_TRUE;
+                               /* set tmp again for use later */
+                               strcpy(tmp, dir.dirname);
+                       }
+               } else {
+                       /*
+                        * if splitcnt != 0 determine host from
+                        * ".host" directory entry
+                        */
+                       while (isc_dir_read(&dir) == ISC_R_SUCCESS) {
+                               if (strncasecmp(".host",
+                                               dir.entry.name, 5) == 0) {
+                                       /*
+                                        * handle filesystem's special
+                                        * wildcard "-"
+                                        */
+                                       if (strcmp((char *) &dir.entry.name[6],
+                                                  "-") == 0)
+                                               strcpy(host, "*");
+                                       else
+                                               strcpy(host,
+                                                      (char *)
+                                                      &dir.entry.name[6]);
+                                       foundHost = ISC_TRUE;
+                                       break;
+                               }
+                       }
+                       /* reset dir list for use later */
+                       isc_dir_reset(&dir);
+               } /* end of else */
+       }
+
+       while (isc_dir_read(&dir) == ISC_R_SUCCESS) {
+
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
+                             "Filesystem driver Dir name:"
+                             " '%s' Dir entry: '%s'\n",
+                             dir.dirname, dir.entry.name);
+
+               /* skip any entries starting with "." */
+               if (dir.entry.name[0] == '.')
+                       continue;
+
+               /*
+                * get rid of '*', set to NULL.  Effectively trims
+                * string from previous loop to base directory only
+                * while still leaving memory for concat to be
+                * performed next.
+                */
+
+               tmp[astPos] = '\0';
+
+               /* add name to base directory name. */
+               strcat(tmp, dir.entry.name);
+
+               /* make sure we can stat entry */
+               if (stat(tmp, &sb) == 0 ) {
+                       /* if entry is a directory */
+                       if ((sb.st_mode & S_IFDIR) != 0) {
+                               /*
+                                * if dir list is NOT NULL, add dir to
+                                * dir list
+                                */
+                               if (dir_list != NULL) {
+                                       direntry =
+                                           isc_mem_get(ns_g_mctx,
+                                                       sizeof(dir_entry_t));
+                                       if (direntry == NULL)
+                                               return (ISC_R_NOMEMORY);
+                                       strcpy(direntry->dirpath, tmp);
+                                       ISC_LINK_INIT(direntry, link);
+                                       ISC_LIST_APPEND(*dir_list, direntry,
+                                                       link);
+                                       result = ISC_R_SUCCESS;
+                               }
+                               continue;
+
+                               /*
+                                * if entry is a file be sure we do
+                                * not add entry to DNS results if we
+                                * are performing a zone xfr and we
+                                * could not find a host entry.
+                                */
+
+                       } else if (dir_list != NULL &&
+                                  foundHost == ISC_FALSE) {
+                               continue;
+                       }
+               } else /* if we cannot stat entry, skip it. */
+                       continue;
+
+               type = dir.entry.name;
+               ttlStr = strchr(type,  cd->separator);
+               if (ttlStr == NULL) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                     "Filesystem driver: "
+                                     "%s could not be parsed properly",
+                                     tmp);
+                       return ISC_R_FAILURE;
+               }
+
+               /* replace separator char with NULL to split string */
+               ttlStr[0] = '\0';
+               /* start string after NULL of previous string */
+               ttlStr = (char *) &ttlStr[1];
+
+               data = strchr(ttlStr, cd->separator);
+               if (data == NULL) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                     "Filesystem driver: "
+                                     "%s could not be parsed properly",
+                                     tmp);
+                       return ISC_R_FAILURE;
+               }
+
+               /* replace separator char with NULL to split string */
+               data[0] = '\0';
+
+               /* start string after NULL of previous string */
+               data = (char *) &data[1];
+
+               /* replace all cd->separator chars with a space. */
+               len = strlen(data);
+
+               for (i=0; i < len; i++) {
+                       if (data[i] == cd->separator)
+                               data[i] = ' ';
+               }
+
+               /* convert text to int, make sure it worked right */
+               ttl = strtol(ttlStr, &endp, 10);
+               if (*endp != '\0' || ttl < 0) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                     "Filesystem driver "
+                                     "ttl must be a postive number");
+               }
+
+               /* pass data back to Bind */
+               if (dir_list == NULL)
+                       result = dns_sdlz_putrr((dns_sdlzlookup_t *) passback,
+                                               type, ttl, data);
+               else
+                       result = dns_sdlz_putnamedrr((dns_sdlzallnodes_t *)
+                                                    passback,
+                                                    (char *) host,
+                                                    type, ttl, data);
+
+               /* if error, return error right away */
+               if (result != ISC_R_SUCCESS)
+                       return result;
+       } /* end of while loop */
+
+       return result;
+}
+
+/*
+ * SDLZ interface methods
+ */
+
+static isc_result_t
+fs_allowzonexfr(void *driverarg, void *dbdata, const char *name,
+               const char *client)
+{
+
+       isc_result_t result;
+       char *path;
+       struct stat     sb;
+       config_data_t *cd;
+       path = NULL;
+
+       UNUSED(driverarg);
+
+       cd = (config_data_t *) dbdata;
+
+       if (create_path(name, NULL, client, cd, &path) != ISC_R_SUCCESS) {
+               return (ISC_R_NOTFOUND);
+       }
+
+       if (stat(path, &sb) != 0) {
+               result = ISC_R_NOTFOUND;
+               goto complete_AXFR;
+       }
+
+       if ((sb.st_mode & S_IFREG) != 0) {
+               result = ISC_R_SUCCESS;
+               goto complete_AXFR;
+       }
+
+       result = ISC_R_NOTFOUND;
+
+ complete_AXFR:
+       isc_mem_free(ns_g_mctx, path);
+       return result;
+}
+
+static isc_result_t
+fs_allnodes(const char *zone, void *driverarg, void *dbdata,
+           dns_sdlzallnodes_t *allnodes)
+{
+
+       isc_result_t result;
+       dlist_t *dir_list;
+       config_data_t *cd;
+       char *basepath;
+       unsigned int basepathlen;
+       struct stat     sb;
+       isc_dir_t dir;
+       dir_entry_t *dir_entry;
+       dir_entry_t *next_de;
+
+       basepath = NULL;
+       dir_list = NULL;
+
+       UNUSED(driverarg);
+       UNUSED(allnodes);
+
+       cd = (config_data_t *) dbdata;
+
+       /* allocate memory for list */
+       dir_list = isc_mem_get(ns_g_mctx, sizeof(dlist_t));
+       if (dir_list == NULL) {
+               result = ISC_R_NOTFOUND;
+               goto complete_allnds;
+       }
+
+       /* initialize list */
+       ISC_LIST_INIT(*dir_list);
+
+       if (create_path(zone, NULL, NULL, cd, &basepath) != ISC_R_SUCCESS) {
+               return (ISC_R_NOTFOUND);
+       }
+
+       /* remove path separator at end of path so stat works properly */
+       basepathlen = strlen(basepath);
+
+       if (stat(basepath, &sb) != 0) {
+               result = ISC_R_NOTFOUND;
+               goto complete_allnds;
+       }
+
+       if ((sb.st_mode & S_IFDIR) == 0) {
+               result = ISC_R_NOTFOUND;
+               goto complete_allnds;
+       }
+
+       /* initialize and open directory */
+       isc_dir_init(&dir);
+       result = isc_dir_open(&dir, basepath);
+
+       /* if directory open failed, return error. */
+       if (result != ISC_R_SUCCESS) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Unable to open %s directory to read entries.",
+                             basepath);
+               result = ISC_R_FAILURE;
+               goto complete_allnds;
+       }
+
+       /* process the directory */
+       result = process_dir(dir, allnodes, cd, dir_list, basepathlen);
+
+       /* close the directory */
+       isc_dir_close(&dir);
+
+       if (result != ISC_R_SUCCESS)
+               goto complete_allnds;
+
+       /* get first dir entry from list. */
+       dir_entry = ISC_LIST_HEAD(*dir_list);
+       while (dir_entry != NULL) {
+
+               result = isc_dir_open(&dir, dir_entry->dirpath);
+               /* if directory open failed, return error. */
+               if (result != ISC_R_SUCCESS) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                     "Unable to open %s "
+                                     "directory to read entries.",
+                                     basepath);
+                       result = ISC_R_FAILURE;
+                       goto complete_allnds;
+               }
+
+               /* process the directory */
+               result = process_dir(dir, allnodes, cd, dir_list, basepathlen);
+
+               /* close the directory */
+               isc_dir_close(&dir);
+
+               if (result != ISC_R_SUCCESS)
+                       goto complete_allnds;
+
+               dir_entry = ISC_LIST_NEXT(dir_entry, link);
+       } /* end while */
+
+ complete_allnds:
+       if (dir_list != NULL) {
+               /* clean up entries from list. */
+               dir_entry = ISC_LIST_HEAD(*dir_list);
+               while (dir_entry != NULL) {
+                       next_de = ISC_LIST_NEXT(dir_entry, link);
+                       isc_mem_put(ns_g_mctx, dir_entry, sizeof(dir_entry_t));
+                       dir_entry = next_de;
+               } /* end while */
+               isc_mem_put(ns_g_mctx, dir_list, sizeof(dlist_t));
+       }
+
+       if (basepath != NULL)
+               isc_mem_free(ns_g_mctx, basepath);
+
+       return result;
+}
+
+static isc_result_t
+fs_findzone(void *driverarg, void *dbdata, const char *name)
+{
+
+       isc_result_t result;
+       char *path;
+       struct stat     sb;
+       path = NULL;
+
+       UNUSED(driverarg);
+
+       if (create_path(name, NULL, NULL, (config_data_t *) dbdata,
+                       &path) != ISC_R_SUCCESS) {
+               return (ISC_R_NOTFOUND);
+       }
+
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
+                     "Filesystem driver Findzone() Checking for path: '%s'\n",
+                     path);
+
+       if (stat(path, &sb) != 0) {
+               result = ISC_R_NOTFOUND;
+               goto complete_FZ;
+       }
+
+       if ((sb.st_mode & S_IFDIR) != 0) {
+               result = ISC_R_SUCCESS;
+               goto complete_FZ;
+       }
+
+       result = ISC_R_NOTFOUND;
+
+ complete_FZ:
+
+       isc_mem_free(ns_g_mctx, path);
+       return result;
+}
+
+static isc_result_t
+fs_lookup(const char *zone, const char *name, void *driverarg,
+         void *dbdata, dns_sdlzlookup_t *lookup)
+{
+       isc_result_t result;
+       char *path;
+       struct stat     sb;
+       isc_dir_t dir;
+       path = NULL;
+
+       UNUSED(driverarg);
+       UNUSED(lookup);
+
+       if (strcmp(name, "*") == 0)
+               /*
+                * handle filesystem's special wildcard "-"
+                */
+               result = create_path(zone, "-", NULL,
+                                    (config_data_t *) dbdata, &path);
+       else
+               result = create_path(zone, name, NULL,
+                                    (config_data_t *) dbdata, &path);
+
+       if ( result != ISC_R_SUCCESS) {
+               return (ISC_R_NOTFOUND);
+       }
+
+       /* remove path separator at end of path so stat works properly */
+       path[strlen(path)-1] = '\0';
+
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
+                     "Filesystem driver lookup() Checking for path: '%s'\n",
+                     path);
+
+
+       if (stat(path, &sb) != 0) {
+               result = ISC_R_NOTFOUND;
+               goto complete_lkup;
+       }
+
+       if ((sb.st_mode & S_IFDIR) == 0) {
+               result = ISC_R_NOTFOUND;
+               goto complete_lkup;
+       }
+
+       /* initialize and open directory */
+       isc_dir_init(&dir);
+       result = isc_dir_open(&dir, path);
+
+       /* if directory open failed, return error. */
+       if (result != ISC_R_SUCCESS) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Unable to open %s directory to read entries.",
+                             path);
+               result = ISC_R_FAILURE;
+               goto complete_lkup;
+       }
+
+       /* process any records in the directory */
+       result = process_dir(dir, lookup, (config_data_t *) dbdata, NULL, 0);
+
+       /* close the directory */
+       isc_dir_close(&dir);
+
+ complete_lkup:
+
+       isc_mem_free(ns_g_mctx, path);
+       return result;
+}
+
+static isc_result_t
+fs_create(const char *dlzname, unsigned int argc, char *argv[],
+         void *driverarg, void **dbdata)
+{
+       config_data_t *cd;
+       char *endp;
+       int len;
+       char pathsep;
+
+       UNUSED(driverarg);
+       UNUSED(dlzname);
+
+       /* we require 5 command line args. */
+       if (argc != 6) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Filesystem driver requires "
+                             "6 command line args.");
+               return (ISC_R_FAILURE);
+       }
+
+       if (strlen(argv[5]) > 1) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Filesystem driver can only "
+                             "accept a single character for separator.");
+               return (ISC_R_FAILURE);
+       }
+
+       /* verify base dir ends with '/' or '\' */
+       len = strlen(argv[1]);
+       if (argv[1][len-1] != '\\' && argv[1][len-1] != '/') {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Base dir parameter for filesystem driver "
+                             "should end with %s",
+                             "either '/' or '\\' ");
+               return (ISC_R_FAILURE);
+       }
+
+       /* determine and save path separator for later */
+       if (argv[1][len-1] == '\\')
+               pathsep = '\\';
+       else
+               pathsep = '/';
+
+       /* allocate memory for our config data */
+       cd = isc_mem_get(ns_g_mctx, sizeof(config_data_t));
+       if (cd == NULL)
+               goto no_mem;
+
+       /* zero the memory */
+       memset(cd, 0, sizeof(config_data_t));
+
+       cd->pathsep = pathsep;
+
+       /* get and store our base directory */
+       cd->basedir = isc_mem_strdup(ns_g_mctx, argv[1]);
+       if (cd->basedir == NULL)
+               goto no_mem;
+       cd->basedirsize = strlen(cd->basedir);
+
+       /* get and store our data sub-dir */
+       cd->datadir = isc_mem_strdup(ns_g_mctx, argv[2]);
+       if (cd->datadir == NULL)
+               goto no_mem;
+       cd->datadirsize = strlen(cd->datadir);
+
+       /* get and store our zone xfr sub-dir */
+       cd->xfrdir = isc_mem_strdup(ns_g_mctx, argv[3]);
+       if (cd->xfrdir == NULL)
+               goto no_mem;
+       cd->xfrdirsize = strlen(cd->xfrdir);
+
+       /* get and store our directory split count */
+       cd->splitcnt = strtol(argv[4], &endp, 10);
+       if (*endp != '\0' || cd->splitcnt < 0) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Directory split count must be zero (0) "
+                             "or a postive number");
+       }
+
+       /* get and store our separator character */
+       cd->separator = *argv[5];
+
+       /* attach config data to memory context */
+       isc_mem_attach(ns_g_mctx, &cd->mctx);
+
+       /* pass back config data */
+       *dbdata = cd;
+
+       /* return success */
+       return(ISC_R_SUCCESS);
+
+       /* handle no memory error */
+ no_mem:
+
+       /* if we allocated a config data object clean it up */
+       if (cd != NULL)
+               fs_destroy(NULL, cd);
+
+       /* write error message */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                     "Filesystem driver unable to "
+                     "allocate memory for config data.");
+
+       /* return error */
+       return (ISC_R_NOMEMORY);
+}
+
+static void
+fs_destroy(void *driverarg, void *dbdata)
+{
+       isc_mem_t *mctx;
+       config_data_t *cd;
+
+       UNUSED(driverarg);
+
+       cd = (config_data_t *) dbdata;
+
+       /*
+        * free memory for each section of config data that was
+        * allocated
+        */
+       if (cd->basedir != NULL)
+               isc_mem_free(ns_g_mctx, cd->basedir);
+
+       if (cd->datadir != NULL)
+               isc_mem_free(ns_g_mctx, cd->datadir);
+
+       if (cd->xfrdir != NULL)
+               isc_mem_free(ns_g_mctx, cd->xfrdir);
+
+       /* hold memory context to use later */
+       mctx = cd->mctx;
+
+       /* free config data memory */
+       isc_mem_put(mctx, cd, sizeof(config_data_t));
+
+       /* detach memory from context */
+       isc_mem_detach(&mctx);
+}
+
+static dns_sdlzmethods_t dlz_fs_methods = {
+       fs_create,
+       fs_destroy,
+       fs_findzone,
+       fs_lookup,
+       NULL,
+       fs_allnodes,
+       fs_allowzonexfr
+};
+
+/*%
+ * Wrapper around dns_sdlzregister().
+ */
+isc_result_t
+dlz_fs_init(void)
+{
+       isc_result_t result;
+
+       /*
+        * Write debugging message to log
+        */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                     "Registering DLZ filesystem driver.");
+
+       result = dns_sdlzregister("filesystem", &dlz_fs_methods, NULL,
+                                 DNS_SDLZFLAG_RELATIVEOWNER |
+                                 DNS_SDLZFLAG_RELATIVERDATA,
+                                 ns_g_mctx, &dlz_fs);
+       if (result != ISC_R_SUCCESS) {
+               UNEXPECTED_ERROR(__FILE__, __LINE__,
+                                "dns_sdlzregister() failed: %s",
+                                isc_result_totext(result));
+               result = ISC_R_UNEXPECTED;
+       }
+
+       return result;
+}
+
+/*%
+ * Wrapper around dns_sdlzunregister().
+ */
+void
+dlz_fs_clear(void) {
+
+       /*
+        * Write debugging message to log
+        */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                     "Unregistering DLZ filesystem driver.");
+
+       if (dlz_fs != NULL)
+               dns_sdlzunregister(&dlz_fs);
+}
+
+#endif
diff --git a/contrib/dlz/drivers/dlz_ldap_driver.c b/contrib/dlz/drivers/dlz_ldap_driver.c
new file mode 100644 (file)
index 0000000..35d7548
--- /dev/null
@@ -0,0 +1,1339 @@
+/*
+ * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
+ *
+ * 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 STICHTING NLNET
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * STICHTING NLNET 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 development of Dynamically Loadable Zones (DLZ) for Bind 9 was
+ * conceived and contributed by Rob Butler.
+ *
+ * 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 ROB BUTLER
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * ROB BUTLER 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.
+ */
+
+/*
+ * Copyright (C) 1999-2001  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.
+ */
+
+#ifdef DLZ_LDAP
+
+#include <config.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <dns/log.h>
+#include <dns/sdlz.h>
+#include <dns/result.h>
+
+#include <isc/mem.h>
+#include <isc/platform.h>
+#include <isc/print.h>
+#include <isc/result.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <named/globals.h>
+
+#include <dlz/sdlz_helper.h>
+#include <dlz/dlz_ldap_driver.h>
+
+/*
+ * Need older API functions from ldap.h.
+ */
+#define LDAP_DEPRECATED 1
+
+#include <ldap.h>
+
+#define SIMPLE "simple"
+#define KRB41 "krb41"
+#define KRB42 "krb42"
+#define V2 "v2"
+#define V3 "v3"
+
+static dns_sdlzimplementation_t *dlz_ldap = NULL;
+
+#define dbc_search_limit 30
+#define ALLNODES 1
+#define ALLOWXFR 2
+#define AUTHORITY 3
+#define FINDZONE 4
+#define LOOKUP 5
+
+/*%
+ * Structure to hold everthing needed by this "instance" of the LDAP
+ * driver remember, the driver code is only loaded once, but may have
+ * many separate instances.
+ */
+
+typedef struct {
+
+#ifdef ISC_PLATFORM_USETHREADS
+       db_list_t    *db; /*%< handle to a list of DB */
+#else
+       dbinstance_t *db; /*%< handle to db */
+#endif
+       int method;     /*%< security authentication method */
+       char *user;     /*%< who is authenticating */
+       char *cred;     /*%< password for simple authentication method */
+       int protocol;   /*%< LDAP communication protocol version */
+       char *hosts;    /*%< LDAP server hosts */
+
+} ldap_instance_t;
+
+/* forward references */
+
+static isc_result_t
+dlz_ldap_findzone(void *driverarg, void *dbdata, const char *name);
+
+static void
+dlz_ldap_destroy(void *driverarg, void *dbdata);
+
+/*
+ * Private methods
+ */
+
+/*% checks that the LDAP URL parameters make sense */
+
+static isc_result_t
+dlz_ldap_checkURL(char *URL, int attrCnt, const char *msg) {
+
+       isc_result_t result = ISC_R_SUCCESS;
+       int ldap_result;
+       LDAPURLDesc *ldap_url = NULL;
+
+       if (!ldap_is_ldap_url(URL)) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "%s query is not a valid LDAP URL", msg);
+               result = ISC_R_FAILURE;
+               goto cleanup;
+       }
+
+       ldap_result = ldap_url_parse(URL, &ldap_url);
+       if (ldap_result != LDAP_SUCCESS || ldap_url == NULL) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "parsing %s query failed", msg);
+               result = ISC_R_FAILURE;
+               goto cleanup;
+       }
+
+       if (ldap_count_values(ldap_url->lud_attrs) < attrCnt) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "%s query must specify at least "
+                             "%d attributes to return",
+                             msg, attrCnt);
+               result = ISC_R_FAILURE;
+               goto cleanup;
+       }
+
+       if (ldap_url->lud_host != NULL) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "%s query must not specify a host", msg);
+               result = ISC_R_FAILURE;
+               goto cleanup;
+       }
+
+       if (ldap_url->lud_port != 389) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "%s query must not specify a port", msg);
+               result = ISC_R_FAILURE;
+               goto cleanup;
+       }
+
+       if (ldap_url->lud_dn == NULL || strlen (ldap_url->lud_dn) < 1) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "%s query must specify a search base", msg);
+               result = ISC_R_FAILURE;
+               goto cleanup;
+       }
+
+       if (ldap_url->lud_exts != NULL || ldap_url->lud_crit_exts != 0) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "%s uses extensions. "
+                             "The driver does not support LDAP extensions.",
+                             msg);
+               result = ISC_R_FAILURE;
+               goto cleanup;
+       }
+
+ cleanup:
+
+       if (ldap_url != NULL)
+               ldap_free_urldesc(ldap_url);
+
+       return result;
+}
+/*% Connects / reconnects to LDAP server */
+
+static isc_result_t
+dlz_ldap_connect(ldap_instance_t *dbi, dbinstance_t *dbc) {
+
+       isc_result_t result;
+       int ldap_result;
+
+       /* if we have a connection, get ride of it. */
+       if (dbc->dbconn != NULL) {
+               ldap_unbind_s((LDAP *) dbc->dbconn);
+               dbc->dbconn = NULL;
+       }
+
+       /* now connect / reconnect. */
+
+       /* initialize. */
+       dbc->dbconn = ldap_init(dbi->hosts, LDAP_PORT);
+       if (dbc->dbconn == NULL)
+               return ISC_R_NOMEMORY;
+
+       /* set protocol version. */
+       ldap_result = ldap_set_option((LDAP *) dbc->dbconn,
+                                     LDAP_OPT_PROTOCOL_VERSION,
+                                     &(dbi->protocol));
+       if (ldap_result != LDAP_SUCCESS) {
+               result = ISC_R_NOPERM;
+               goto cleanup;
+       }
+
+       /* "bind" to server.  i.e. send username / pass */
+       ldap_result = ldap_bind_s((LDAP *) dbc->dbconn, dbi->user,
+                                 dbi->cred, dbi->method);
+       if (ldap_result != LDAP_SUCCESS) {
+               result = ISC_R_FAILURE;
+               goto cleanup;
+       }
+
+       return ISC_R_SUCCESS;
+
+ cleanup:
+
+       /* cleanup if failure. */
+       if (dbc->dbconn != NULL) {
+               ldap_unbind_s((LDAP *) dbc->dbconn);
+               dbc->dbconn = NULL;
+       }
+
+       return result;
+}
+
+#ifdef ISC_PLATFORM_USETHREADS
+
+
+/*%
+ * Properly cleans up a list of database instances.
+ * This function is only used when the driver is compiled for
+ * multithreaded operation.
+ */
+static void
+ldap_destroy_dblist(db_list_t *dblist)
+{
+
+       dbinstance_t *ndbi = NULL;
+       dbinstance_t *dbi = NULL;
+
+       /* get the first DBI in the list */
+       ndbi = ISC_LIST_HEAD(*dblist);
+
+       /* loop through the list */
+       while (ndbi != NULL) {
+               dbi = ndbi;
+               /* get the next DBI in the list */
+               ndbi = ISC_LIST_NEXT(dbi, link);
+               /* release DB connection */
+               if (dbi->dbconn != NULL)
+                       ldap_unbind_s((LDAP *) dbi->dbconn);
+               /* release all memory that comprised a DBI */
+               destroy_sqldbinstance(dbi);
+       }
+       /* release memory for the list structure */
+       isc_mem_put(ns_g_mctx, dblist, sizeof(db_list_t));
+}
+
+/*%
+ * Loops through the list of DB instances, attempting to lock
+ * on the mutex.  If successful, the DBI is reserved for use
+ * and the thread can perform queries against the database.
+ * If the lock fails, the next one in the list is tried.
+ * looping continues until a lock is obtained, or until
+ * the list has been searched dbc_search_limit times.
+ * This function is only used when the driver is compiled for
+ * multithreaded operation.
+ */
+
+static dbinstance_t *
+ldap_find_avail_conn(db_list_t *dblist)
+{
+       dbinstance_t *dbi = NULL;
+       dbinstance_t *head;
+       int count = 0;
+
+       /* get top of list */
+       head = dbi = ISC_LIST_HEAD(*dblist);
+
+       /* loop through list */
+       while (count < dbc_search_limit) {
+               /* try to lock on the mutex */
+               if (isc_mutex_trylock(&dbi->instance_lock) == ISC_R_SUCCESS)
+                       return dbi; /* success, return the DBI for use. */
+
+               /* not successful, keep trying */
+               dbi = ISC_LIST_NEXT(dbi, link);
+
+               /* check to see if we have gone to the top of the list. */
+               if (dbi == NULL) {
+                       count++;
+                       dbi = head;
+               }
+       }
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
+                     "LDAP driver unable to find available connection "
+                     "after searching %d times",
+                     count);
+       return NULL;
+}
+
+#endif /* ISC_PLATFORM_USETHREADS */
+
+static isc_result_t
+ldap_process_results(LDAP *dbc, LDAPMessage *msg, char ** attrs,
+                    void *ptr, isc_boolean_t allnodes)
+{
+       isc_result_t result = ISC_R_SUCCESS;
+       int i = 0;
+       int j;
+       int len;
+       char *attribute = NULL;
+       LDAPMessage *entry;
+       char *endp = NULL;
+       char *host = NULL;
+       char *type = NULL;
+       char *data = NULL;
+       char **vals = NULL;
+       int ttl;
+
+       /* make sure there are at least some attributes to process. */
+       REQUIRE(attrs != NULL || attrs[0] != NULL);
+
+       /* get the first entry to process */
+       entry = ldap_first_entry(dbc, msg);
+       if (entry == NULL) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
+                             "LDAP no entries to process.");
+               return ISC_R_FAILURE;
+       }
+
+       /* loop through all entries returned */
+       while (entry != NULL) {
+
+               /* reset for this loop */
+               ttl = 0;
+               len = 0;
+               i = 0;
+               attribute = attrs[i];
+
+               /* determine how much space we need for data string */
+               for (j=0; attrs[j] != NULL; j++) {
+                       /* get the list of values for this attribute. */
+                       vals = ldap_get_values(dbc, entry, attrs[j]);
+                       /* skip empty attributes. */
+                       if (vals == NULL || ldap_count_values(vals) < 1)
+                               continue;
+                       /*
+                        * we only use the first value.  this driver
+                        * does not support multi-valued attributes.
+                        */
+                       len = len + strlen(vals[0]) + 1;
+                       /* free vals for next loop */
+                       ldap_value_free(vals);
+               } /* end for (j=0; attrs[j] != NULL, j++) loop */
+
+               /* allocate memory for data string */
+               data = isc_mem_allocate(ns_g_mctx, len + 1);
+               if (data == NULL) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                     "LDAP driver unable to allocate memory "
+                                     "while processing results");
+                       result = ISC_R_FAILURE;
+                       goto cleanup;
+               }
+
+               /*
+                * Make sure data is null termed at the beginning so
+                * we can check if any data was stored to it later.
+                */
+               data[0] = '\0';
+
+               /* reset j to re-use below */
+               j = 0;
+
+               /* loop through the attributes in the order specified. */
+               while (attribute != NULL) {
+
+                       /* get the list of values for this attribute. */
+                       vals = ldap_get_values(dbc, entry, attribute);
+
+                       /* skip empty attributes. */
+                       if (vals == NULL || vals[0] == NULL) {
+                               /* increment attibute pointer */
+                               attribute = attrs[++i];
+                               /* start loop over */
+                               continue;
+                       }
+
+                       /*
+                        * j initially = 0.  Increment j each time we
+                        * set a field that way next loop will set
+                        * next field.
+                        */
+                       switch(j) {
+                       case 0:
+                               j++;
+                               /*
+                                * convert text to int, make sure it
+                                * worked right
+                                */
+                               ttl = strtol(vals[0], &endp, 10);
+                               if (*endp != '\0' || ttl < 0) {
+                                       isc_log_write(dns_lctx,
+                                                     DNS_LOGCATEGORY_DATABASE,
+                                                     DNS_LOGMODULE_DLZ,
+                                                     ISC_LOG_ERROR,
+                                                     "LDAP driver ttl must "
+                                                     "be a postive number");
+                                       goto cleanup;
+                               }
+                               break;
+                       case 1:
+                               j++;
+                               type = isc_mem_strdup(ns_g_mctx, vals[0]);
+                               break;
+                       case 2:
+                               j++;
+                               if (allnodes == isc_boolean_true) {
+                                       host = isc_mem_strdup(ns_g_mctx,
+                                                             vals[0]);
+                               } else {
+                                       strcpy(data, vals[0]);
+                               }
+                               break;
+                       case 3:
+                               j++;
+                               if (allnodes == isc_boolean_true) {
+                                       strcpy(data, vals[0]);
+                               } else {
+                                       strcat(data, " ");
+                                       strcat(data, vals[0]);
+                               }
+                               break;
+                       default:
+                               strcat(data, " ");
+                               strcat(data, vals[0]);
+                               break;
+                       } /* end switch(j) */
+
+                       /* free values */
+                       ldap_value_free(vals);
+                       vals = NULL;
+
+                       /* increment attibute pointer */
+                       attribute = attrs[++i];
+               }       /* end while (attribute != NULL) */
+
+               if (type == NULL) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                     "LDAP driver unable "
+                                     "to retrieve dns type");
+                       result = ISC_R_FAILURE;
+                       goto cleanup;
+               }
+               if (strlen(data) < 1) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                     "LDAP driver unable "
+                                     "to retrieve dns data");
+                       result = ISC_R_FAILURE;
+                       goto cleanup;
+               }
+               if (allnodes == isc_boolean_true) {
+                       if (strcasecmp(host, "~") == 0)
+                               result = dns_sdlz_putnamedrr(
+                                               (dns_sdlzallnodes_t *) ptr,
+                                               "*", type, ttl, data);
+                       else
+                               result = dns_sdlz_putnamedrr(
+                                               (dns_sdlzallnodes_t *) ptr,
+                                               host, type, ttl, data);
+               }
+               else
+                       result = dns_sdlz_putrr((dns_sdlzlookup_t *) ptr,
+                                               type, ttl, data);
+
+               if (result != ISC_R_SUCCESS) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                     "LDAP driver failed "
+                                     "while sending data to Bind.");
+                       goto cleanup;
+               }
+
+               /* free memory for type, data and host for next loop */
+               isc_mem_free(ns_g_mctx, type);
+               isc_mem_free(ns_g_mctx, data);
+               if (host != NULL)
+                       isc_mem_free(ns_g_mctx, host);
+
+               /* get the next entry to process */
+               entry = ldap_next_entry(dbc, entry);
+       } /* end while (entry != NULL) */
+
+ cleanup:
+
+       /* de-allocate memory */
+       if (vals != NULL)
+               ldap_value_free(vals);
+       if (host != NULL)
+               isc_mem_free(ns_g_mctx, host);
+       if (type != NULL)
+               isc_mem_free(ns_g_mctx, type);
+       if (data != NULL)
+               isc_mem_free(ns_g_mctx, data);
+
+       return result;
+}
+
+/*%
+ * This function is the real core of the driver.   Zone, record
+ * and client strings are passed in (or NULL is passed if the
+ * string is not available).  The type of query we want to run
+ * is indicated by the query flag, and the dbdata object is passed
+ * passed in to.  dbdata really holds either:
+ *             1) a list of database instances (in multithreaded mode) OR
+ *             2) a single database instance (in single threaded mode)
+ * The function will construct the query and obtain an available
+ * database instance (DBI).  It will then run the query and hopefully
+ * obtain a result set.
+ */
+static isc_result_t
+ldap_get_results(const char *zone, const char *record,
+                const char *client, unsigned int query,
+                void *dbdata, void *ptr)
+{
+       isc_result_t result;
+       dbinstance_t *dbi = NULL;
+       char *querystring = NULL;
+       LDAPURLDesc *ldap_url = NULL;
+       int ldap_result = 0;
+       LDAPMessage *ldap_msg = NULL;
+       int i;
+       int entries;
+
+       /* get db instance / connection */
+#ifdef ISC_PLATFORM_USETHREADS
+
+       /* find an available DBI from the list */
+       dbi = ldap_find_avail_conn((db_list_t *)
+                                  ((ldap_instance_t *)dbdata)->db);
+
+#else /* ISC_PLATFORM_USETHREADS */
+
+       /*
+        * only 1 DBI - no need to lock instance lock either
+        * only 1 thread in the whole process, no possible contention.
+        */
+       dbi =  (dbinstance_t *) ((ldap_instance_t *)dbdata)->db;
+
+#endif /* ISC_PLATFORM_USETHREADS */
+
+       /* if DBI is null, can't do anything else */
+       if (dbi == NULL)
+               return  ISC_R_FAILURE;
+
+       /* set fields */
+       if (zone != NULL) {
+               dbi->zone = isc_mem_strdup(ns_g_mctx, zone);
+               if (dbi->zone == NULL) {
+                       result = ISC_R_NOMEMORY;
+                       goto cleanup;
+               }
+       } else {
+               dbi->zone = NULL;
+       }
+       if (record != NULL) {
+               dbi->record = isc_mem_strdup(ns_g_mctx, record);
+               if (dbi->record == NULL) {
+                       result = ISC_R_NOMEMORY;
+                       goto cleanup;
+               }
+       } else {
+               dbi->record = NULL;
+       }
+       if (client != NULL) {
+               dbi->client = isc_mem_strdup(ns_g_mctx, client);
+               if (dbi->client == NULL) {
+                       result = ISC_R_NOMEMORY;
+                       goto cleanup;
+               }
+       } else {
+               dbi->client = NULL;
+       }
+
+       /* what type of query are we going to run? */
+       switch(query) {
+       case ALLNODES:
+               /*
+                * if the query was not passed in from the config file
+                * then we can't run it.  return not_implemented, so
+                * it's like the code for that operation was never
+                * built into the driver.... AHHH flexibility!!!
+                */
+               if (dbi->allnodes_q == NULL) {
+                       result = ISC_R_NOTIMPLEMENTED;
+                       goto cleanup;
+               } else {
+                       querystring = build_querystring(ns_g_mctx,
+                       dbi->allnodes_q);
+               }
+               break;
+       case ALLOWXFR:
+               /* same as comments as ALLNODES */
+               if (dbi->allowxfr_q == NULL) {
+                       result = ISC_R_NOTIMPLEMENTED;
+                       goto cleanup;
+               } else {
+                       querystring = build_querystring(ns_g_mctx,
+                       dbi->allowxfr_q);
+               }
+               break;
+       case AUTHORITY:
+               /* same as comments as ALLNODES */
+               if (dbi->authority_q == NULL) {
+                       result = ISC_R_NOTIMPLEMENTED;
+                       goto cleanup;
+               } else {
+                       querystring = build_querystring(ns_g_mctx,
+                       dbi->authority_q);
+               }
+               break;
+       case FINDZONE:
+               /* this is required.  It's the whole point of DLZ! */
+               if (dbi->findzone_q == NULL) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                                     "No query specified for findzone.  "
+                                     "Findzone requires a query");
+                       result = ISC_R_FAILURE;
+                       goto cleanup;
+               } else {
+                       querystring = build_querystring(ns_g_mctx,
+                       dbi->findzone_q);
+               }
+               break;
+       case LOOKUP:
+               /* this is required.  It's also a major point of DLZ! */
+               if (dbi->lookup_q == NULL) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                                     "No query specified for lookup.  "
+                                     "Lookup requires a query");
+                       result = ISC_R_FAILURE;
+                       goto cleanup;
+               } else {
+                       querystring = build_querystring(ns_g_mctx,
+                                                       dbi->lookup_q);
+               }
+               break;
+       default:
+               /*
+                * this should never happen.  If it does, the code is
+                * screwed up!
+                */
+               UNEXPECTED_ERROR(__FILE__, __LINE__,
+                                "Incorrect query flag passed to "
+                                "ldap_get_results");
+               result = ISC_R_UNEXPECTED;
+               goto cleanup;
+       }
+
+       /* if the querystring is null, Bummer, outta RAM.  UPGRADE TIME!!!   */
+       if (querystring  == NULL) {
+               result = ISC_R_NOMEMORY;
+               goto cleanup;
+       }
+
+       /*
+        * output the full query string during debug so we can see
+        * what lame error the query has.
+        */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
+                     "\nQuery String: %s\n", querystring);
+
+        /* break URL down into it's component parts, if error cleanup */
+       ldap_result = ldap_url_parse(querystring, &ldap_url);
+       if (ldap_result != LDAP_SUCCESS || ldap_url == NULL) {
+               result = ISC_R_FAILURE;
+               goto cleanup;
+       }
+
+       for (i=0; i < 3; i++) {
+
+               /*
+                * dbi->dbconn may be null if trying to reconnect on a
+                * previous query failed.
+                */
+               if (dbi->dbconn == NULL) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
+                                     "LDAP driver attempting to re-connect");
+
+                       result = dlz_ldap_connect((ldap_instance_t *) dbdata,
+                                                 dbi);
+                       if (result != ISC_R_SUCCESS) {
+                               result = ISC_R_FAILURE;
+                               continue;
+                       }
+               }
+
+               /* perform ldap search syncronously */
+               ldap_result = ldap_search_s((LDAP *) dbi->dbconn,
+                                           ldap_url->lud_dn,
+                                           ldap_url->lud_scope,
+                                           ldap_url->lud_filter,
+                                           ldap_url->lud_attrs, 0, &ldap_msg);
+
+               /*
+                * check return code.  No such object is ok, just
+                * didn't find what we wanted
+                */
+               switch(ldap_result) {
+               case LDAP_NO_SUCH_OBJECT:
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
+                                     "No object found matching "
+                                     "query requirements");
+                       result = ISC_R_NOTFOUND;
+                       goto cleanup;
+                       break;
+               case LDAP_SUCCESS:      /* on success do nothing */
+                       result = ISC_R_SUCCESS;
+                       i = 3;
+                       break;
+               case LDAP_SERVER_DOWN:
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
+                                     "LDAP driver attempting to re-connect");
+                       result = dlz_ldap_connect((ldap_instance_t *) dbdata,
+                                                 dbi);
+                       if (result != ISC_R_SUCCESS)
+                               result = ISC_R_FAILURE;
+                       break;
+               default:
+                       /*
+                        * other errors not ok.  Log error message and
+                        * get out
+                        */
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                     "LDAP error: %s",
+                                     ldap_err2string(ldap_result));
+                       result = ISC_R_FAILURE;
+                       goto cleanup;
+                       break;
+               } /* close switch(ldap_result) */
+       } /* end for (int i=0 i < 3; i++) */
+
+       if (result != ISC_R_SUCCESS)
+               goto cleanup;
+
+       switch(query) {
+       case ALLNODES:
+               result = ldap_process_results((LDAP *) dbi->dbconn, ldap_msg,
+                                             ldap_url->lud_attrs,
+                                             ptr, isc_boolean_true);
+               break;
+       case AUTHORITY:
+       case LOOKUP:
+               result = ldap_process_results((LDAP *) dbi->dbconn, ldap_msg,
+                                             ldap_url->lud_attrs,
+                                             ptr, isc_boolean_false);
+               break;
+       case ALLOWXFR:
+               entries = ldap_count_entries((LDAP *) dbi->dbconn, ldap_msg);
+               if (entries == 0)
+                       result = ISC_R_NOPERM;
+               else if (entries > 0)
+                       result = ISC_R_SUCCESS;
+               else
+                       result = ISC_R_FAILURE;
+               break;
+       case FINDZONE:
+               entries = ldap_count_entries((LDAP *) dbi->dbconn, ldap_msg);
+               if (entries == 0)
+                       result = ISC_R_NOTFOUND;
+               else if (entries > 0)
+                       result = ISC_R_SUCCESS;
+               else
+                       result = ISC_R_FAILURE;
+               break;
+       default:
+               /*
+                * this should never happen.  If it does, the code is
+                * screwed up!
+                */
+               UNEXPECTED_ERROR(__FILE__, __LINE__,
+                                "Incorrect query flag passed to "
+                                "ldap_get_results");
+               result = ISC_R_UNEXPECTED;
+       }
+
+
+ cleanup:
+       /* it's always good to cleanup after yourself */
+
+        /* if we retrieved results, free them */
+       if (ldap_msg != NULL)
+               ldap_msgfree(ldap_msg);
+
+       if (ldap_url != NULL)
+               ldap_free_urldesc(ldap_url);
+
+       /* cleanup */
+       if (dbi->zone != NULL)
+               isc_mem_free(ns_g_mctx, dbi->zone);
+       if (dbi->record != NULL)
+               isc_mem_free(ns_g_mctx, dbi->record);
+       if (dbi->client != NULL)
+               isc_mem_free(ns_g_mctx, dbi->client);
+
+#ifdef ISC_PLATFORM_USETHREADS
+
+       /* release the lock so another thread can use this dbi */
+       isc_mutex_unlock(&dbi->instance_lock);
+
+#endif /* ISC_PLATFORM_USETHREADS */
+
+        /* release query string */
+       if (querystring  != NULL)
+               isc_mem_free(ns_g_mctx, querystring );
+
+       /* return result */
+       return result;
+}
+
+/*
+ * DLZ methods
+ */
+
+static isc_result_t
+dlz_ldap_allowzonexfr(void *driverarg, void *dbdata, const char *name,
+                     const char *client)
+{
+       isc_result_t result;
+
+       UNUSED(driverarg);
+
+       /* check to see if we are authoritative for the zone first */
+       result = dlz_ldap_findzone(driverarg, dbdata, name);
+       if (result != ISC_R_SUCCESS) {
+               return result;
+       }
+
+        /* get all the zone data */
+       return ldap_get_results(name, NULL, client, ALLOWXFR, dbdata, NULL);
+}
+
+static isc_result_t
+dlz_ldap_allnodes(const char *zone, void *driverarg, void *dbdata,
+                 dns_sdlzallnodes_t *allnodes)
+{
+       UNUSED(driverarg);
+       return ldap_get_results(zone, NULL, NULL, ALLNODES, dbdata, allnodes);
+}
+
+static isc_result_t
+dlz_ldap_authority(const char *zone, void *driverarg, void *dbdata,
+                  dns_sdlzlookup_t *lookup)
+{
+       UNUSED(driverarg);
+       return ldap_get_results(zone, NULL, NULL, AUTHORITY, dbdata, lookup);
+}
+
+static isc_result_t
+dlz_ldap_findzone(void *driverarg, void *dbdata, const char *name)
+{
+       UNUSED(driverarg);
+       return ldap_get_results(name, NULL, NULL, FINDZONE, dbdata, NULL);
+}
+
+static isc_result_t
+dlz_ldap_lookup(const char *zone, const char *name, void *driverarg,
+               void *dbdata, dns_sdlzlookup_t *lookup)
+{
+       UNUSED(driverarg);
+       if (strcmp(name, "*") == 0)
+               return ldap_get_results(zone, "~", NULL,
+                                       LOOKUP, dbdata, lookup);
+       else
+               return ldap_get_results(zone, name, NULL,
+                                       LOOKUP, dbdata, lookup);
+}
+
+
+static isc_result_t
+dlz_ldap_create(const char *dlzname, unsigned int argc, char *argv[],
+               void *driverarg, void **dbdata)
+{
+
+       isc_result_t result;
+       ldap_instance_t *ldap_inst = NULL;
+       dbinstance_t *dbi = NULL;
+       int protocol;
+       int method;
+
+#ifdef ISC_PLATFORM_USETHREADS
+       /* if multi-threaded, we need a few extra variables. */
+       int dbcount;
+       char *endp;
+/* db_list_t *dblist = NULL; */
+       int i;
+
+#endif /* ISC_PLATFORM_USETHREADS */
+
+       UNUSED(dlzname);
+       UNUSED(driverarg);
+
+#ifdef ISC_PLATFORM_USETHREADS
+       /* if debugging, let user know we are multithreaded. */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
+                     "LDAP driver running multithreaded");
+#else /* ISC_PLATFORM_USETHREADS */
+       /* if debugging, let user know we are single threaded. */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
+                     "LDAP driver running single threaded");
+#endif /* ISC_PLATFORM_USETHREADS */
+
+       if (argc < 9) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "LDAP driver requires at least "
+                             "8 command line args.");
+               return (ISC_R_FAILURE);
+       }
+
+       /* no more than 13 arg's should be passed to the driver */
+       if (argc > 12) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "LDAP driver cannot accept more than "
+                             "11 command line args.");
+               return (ISC_R_FAILURE);
+       }
+
+       /* determine protocol version. */
+       if (strncasecmp(argv[2], V2, strlen(V2)) == 0) {
+               protocol = 2;
+       } else if (strncasecmp(argv[2], V3, strlen(V3)) == 0) {
+               protocol = 3;
+       } else {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "LDAP driver protocol must be either %s or %s",
+                             V2, V3);
+               return (ISC_R_FAILURE);
+       }
+
+       /* determine connection method. */
+       if (strncasecmp(argv[3], SIMPLE, strlen(SIMPLE)) == 0) {
+               method = LDAP_AUTH_SIMPLE;
+       } else if (strncasecmp(argv[3], KRB41, strlen(KRB41)) == 0) {
+               method = LDAP_AUTH_KRBV41;
+       } else if (strncasecmp(argv[3], KRB42, strlen(KRB42)) == 0) {
+               method = LDAP_AUTH_KRBV42;
+       } else {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "LDAP driver authentication method must be "
+                             "one of %s, %s or %s",
+                             SIMPLE, KRB41, KRB42);
+               return (ISC_R_FAILURE);
+       }
+
+       /* multithreaded build can have multiple DB connections */
+#ifdef ISC_PLATFORM_USETHREADS
+
+       /* check how many db connections we should create */
+       dbcount = strtol(argv[1], &endp, 10);
+       if (*endp != '\0' || dbcount < 0) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "LDAP driver database connection count "
+                             "must be positive.");
+               return (ISC_R_FAILURE);
+       }
+#endif
+
+       /* check that LDAP URL parameters make sense */
+       switch(argc) {
+       case 12:
+               result = dlz_ldap_checkURL(argv[11], 0, "allow zone transfer");
+               if (result != ISC_R_SUCCESS)
+                       return result;
+       case 11:
+               result = dlz_ldap_checkURL(argv[10], 3, "all nodes");
+               if (result != ISC_R_SUCCESS)
+                       return result;
+       case 10:
+               if (strlen(argv[9]) > 0) {
+                       result = dlz_ldap_checkURL(argv[9], 3, "authority");
+                       if (result != ISC_R_SUCCESS)
+                               return result;
+               }
+       case 9:
+               result = dlz_ldap_checkURL(argv[8], 3, "lookup");
+               if (result != ISC_R_SUCCESS)
+                       return result;
+               result = dlz_ldap_checkURL(argv[7], 0, "find zone");
+               if (result != ISC_R_SUCCESS)
+                       return result;
+               break;
+       default:
+               /* not really needed, should shut up compiler. */
+               result = ISC_R_FAILURE;
+       }
+
+       /* allocate memory for LDAP instance */
+       ldap_inst = isc_mem_get(ns_g_mctx, sizeof(ldap_instance_t));
+       if (ldap_inst == NULL)
+               return (ISC_R_NOMEMORY);
+       memset(ldap_inst, 0, sizeof(ldap_instance_t));
+
+       /* store info needed to automatically re-connect. */
+       ldap_inst->protocol = protocol;
+       ldap_inst->method = method;
+       ldap_inst->hosts = isc_mem_strdup(ns_g_mctx, argv[6]);
+       if (ldap_inst->hosts == NULL) {
+               result = ISC_R_NOMEMORY;
+               goto cleanup;
+       }
+       ldap_inst->user = isc_mem_strdup(ns_g_mctx, argv[4]);
+       if (ldap_inst->user == NULL) {
+               result = ISC_R_NOMEMORY;
+               goto cleanup;
+       }
+       ldap_inst->cred = isc_mem_strdup(ns_g_mctx, argv[5]);
+       if (ldap_inst->cred == NULL) {
+               result = ISC_R_NOMEMORY;
+               goto cleanup;
+       }
+
+#ifdef ISC_PLATFORM_USETHREADS
+       /* allocate memory for database connection list */
+       ldap_inst->db = isc_mem_get(ns_g_mctx, sizeof(db_list_t));
+       if (ldap_inst->db == NULL) {
+               result = ISC_R_NOMEMORY;
+               goto cleanup;
+       }
+
+       /* initialize DB connection list */
+       ISC_LIST_INIT(*(ldap_inst->db));
+
+       /*
+        * create the appropriate number of database instances (DBI)
+        * append each new DBI to the end of the list
+        */
+       for (i = 0; i < dbcount; i++) {
+
+#endif /* ISC_PLATFORM_USETHREADS */
+
+               /* how many queries were passed in from config file? */
+               switch(argc) {
+               case 9:
+                       result = build_sqldbinstance(ns_g_mctx, NULL, NULL,
+                                                    NULL, argv[7], argv[8],
+                                                    NULL, &dbi);
+                       break;
+               case 10:
+                       result = build_sqldbinstance(ns_g_mctx, NULL, NULL,
+                                                    argv[9], argv[7], argv[8],
+                                                    NULL, &dbi);
+                       break;
+               case 11:
+                       result = build_sqldbinstance(ns_g_mctx, argv[10], NULL,
+                                                    argv[9], argv[7], argv[8],
+                                                    NULL, &dbi);
+                       break;
+               case 12:
+                       result = build_sqldbinstance(ns_g_mctx, argv[10],
+                                                    argv[11], argv[9],
+                                                    argv[7], argv[8],
+                                                    NULL, &dbi);
+                       break;
+               default:
+                       /* not really needed, should shut up compiler. */
+                       result = ISC_R_FAILURE;
+               }
+
+               if (result == ISC_R_SUCCESS) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                                     "LDAP driver created "
+                                     "database instance object.");
+               } else { /* unsuccessful?, log err msg and cleanup. */
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                     "LDAP driver could not create "
+                                     "database instance object.");
+                       goto cleanup;
+               }
+
+#ifdef ISC_PLATFORM_USETHREADS
+               /* when multithreaded, build a list of DBI's */
+               ISC_LINK_INIT(dbi, link);
+               ISC_LIST_APPEND(*(ldap_inst->db), dbi, link);
+#else
+               /*
+                * when single threaded, hold onto the one connection
+                * instance.
+                */
+               ldap_inst->db = dbi;
+
+#endif
+               /* attempt to connect */
+               result = dlz_ldap_connect(ldap_inst, dbi);
+
+               /*
+                * if db connection cannot be created, log err msg and
+                * cleanup.
+                */
+               switch(result) {
+                       /* success, do nothing */
+               case ISC_R_SUCCESS:
+                       break;
+                       /*
+                        * no memory means ldap_init could not
+                        * allocate memory
+                        */
+               case ISC_R_NOMEMORY:
+#ifdef ISC_PLATFORM_USETHREADS
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                     "LDAP driver could not allocate memory "
+                                     "for connection number %u",
+                                     i+1);
+#else
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                     "LDAP driver could not allocate memory "
+                                     "for connection");
+#endif
+                       goto cleanup;
+                       break;
+                       /*
+                        * no perm means ldap_set_option could not set
+                        * protocol version
+                        */
+               case ISC_R_NOPERM:
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                     "LDAP driver could not "
+                                     "set protocol version.");
+                       result = ISC_R_FAILURE;
+                       goto cleanup;
+                       break;
+                       /* failure means couldn't connect to ldap server */
+               case ISC_R_FAILURE:
+#ifdef ISC_PLATFORM_USETHREADS
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                     "LDAP driver could not "
+                                     "bind connection number %u to server.",
+                                     i+1);
+#else
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                     "LDAP driver could not "
+                                     "bind connection to server.");
+#endif
+                       goto cleanup;
+                       break;
+                       /*
+                        * default should never happen.  If it does,
+                        * major errors.
+                        */
+               default:
+                       UNEXPECTED_ERROR(__FILE__, __LINE__,
+                                        "dlz_ldap_create() failed: %s",
+                                        isc_result_totext(result));
+                       result = ISC_R_UNEXPECTED;
+                       goto cleanup;
+                       break;
+               } /* end switch(result) */
+
+
+#ifdef ISC_PLATFORM_USETHREADS
+
+               /* set DBI = null for next loop through. */
+               dbi = NULL;
+       }       /* end for loop */
+
+#endif /* ISC_PLATFORM_USETHREADS */
+
+
+       /* set dbdata to the ldap_instance we created. */
+       *dbdata = ldap_inst;
+
+       /* hey, we got through all of that ok, return success. */
+       return(ISC_R_SUCCESS);
+
+ cleanup:
+
+       dlz_ldap_destroy(NULL, ldap_inst);
+
+       return(ISC_R_FAILURE);
+}
+
+void
+dlz_ldap_destroy(void *driverarg, void *dbdata)
+{
+
+       UNUSED(driverarg);
+
+       if (dbdata != NULL) {
+
+#ifdef ISC_PLATFORM_USETHREADS
+
+               /* cleanup the list of DBI's */
+               ldap_destroy_dblist((db_list_t *)
+                                   ((ldap_instance_t *)dbdata)->db);
+
+#else /* ISC_PLATFORM_USETHREADS */
+
+               /* release connection */
+               if (((ldap_instance_t *)dbdata)->db->dbconn != NULL)
+                       ldap_unbind_s((LDAP *)
+                                     ((ldap_instance_t *)dbdata)->db->dbconn);
+
+               /* destroy single DB instance */
+               destroy_sqldbinstance(((ldap_instance_t *)dbdata)->db);
+
+#endif /* ISC_PLATFORM_USETHREADS */
+
+               if (((ldap_instance_t *)dbdata)->hosts != NULL)
+                       isc_mem_free(ns_g_mctx,
+                                    ((ldap_instance_t *)dbdata)->hosts);
+
+               if (((ldap_instance_t *)dbdata)->user != NULL)
+                       isc_mem_free(ns_g_mctx,
+                                    ((ldap_instance_t *)dbdata)->user);
+
+               if (((ldap_instance_t *)dbdata)->cred != NULL)
+                       isc_mem_free(ns_g_mctx,
+                                    ((ldap_instance_t *)dbdata)->cred);
+
+               isc_mem_put(ns_g_mctx, dbdata, sizeof(ldap_instance_t));
+       }
+}
+
+static dns_sdlzmethods_t dlz_ldap_methods = {
+       dlz_ldap_create,
+       dlz_ldap_destroy,
+       dlz_ldap_findzone,
+       dlz_ldap_lookup,
+       dlz_ldap_authority,
+       dlz_ldap_allnodes,
+       dlz_ldap_allowzonexfr
+};
+
+/*%
+ * Wrapper around dns_sdlzregister().
+ */
+isc_result_t
+dlz_ldap_init(void) {
+       isc_result_t result;
+
+       /*
+        * Write debugging message to log
+        */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                     "Registering DLZ ldap driver.");
+
+       result = dns_sdlzregister("ldap", &dlz_ldap_methods, NULL,
+                                 DNS_SDLZFLAG_RELATIVEOWNER |
+                                 DNS_SDLZFLAG_RELATIVERDATA,
+                                 ns_g_mctx, &dlz_ldap);
+       if (result != ISC_R_SUCCESS) {
+               UNEXPECTED_ERROR(__FILE__, __LINE__,
+                                "dns_sdlzregister() failed: %s",
+                                isc_result_totext(result));
+               result = ISC_R_UNEXPECTED;
+       }
+
+
+       return result;
+}
+
+/*%
+ * Wrapper around dns_sdlzunregister().
+ */
+void
+dlz_ldap_clear(void) {
+
+       /*
+        * Write debugging message to log
+        */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                     "Unregistering DLZ ldap driver.");
+
+       if (dlz_ldap != NULL)
+               dns_sdlzunregister(&dlz_ldap);
+}
+
+#endif
diff --git a/contrib/dlz/drivers/dlz_mysql_driver.c b/contrib/dlz/drivers/dlz_mysql_driver.c
new file mode 100644 (file)
index 0000000..7a7867d
--- /dev/null
@@ -0,0 +1,1060 @@
+/*
+ * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
+ *
+ * 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 STICHTING NLNET
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * STICHTING NLNET 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 development of Dynamically Loadable Zones (DLZ) for Bind 9 was
+ * conceived and contributed by Rob Butler.
+ *
+ * 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 ROB BUTLER
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * ROB BUTLER 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.
+ */
+
+/*
+ * Copyright (C) 1999-2001  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.
+ */
+
+#ifdef DLZ_MYSQL
+
+#include <config.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <dns/log.h>
+#include <dns/sdlz.h>
+#include <dns/result.h>
+
+#include <isc/mem.h>
+#include <isc/platform.h>
+#include <isc/print.h>
+#include <isc/result.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <named/globals.h>
+
+#include <dlz/sdlz_helper.h>
+#include <dlz/dlz_mysql_driver.h>
+
+#include <mysql.h>
+
+static dns_sdlzimplementation_t *dlz_mysql = NULL;
+
+#define dbc_search_limit 30
+#define ALLNODES 1
+#define ALLOWXFR 2
+#define AUTHORITY 3
+#define FINDZONE 4
+#define COUNTZONE 5
+#define LOOKUP 6
+
+#define safeGet(in) in == NULL ? "" : in
+
+/*
+ * Private methods
+ */
+
+/*%
+ * Allocates memory for a new string, and then constructs the new
+ * string by "escaping" the input string.  The new string is
+ * safe to be used in queries.  This is necessary because we cannot
+ * be sure of what types of strings are passed to us, and we don't
+ * want special characters in the string causing problems.
+ */
+
+static char *
+mysqldrv_escape_string(MYSQL *mysql, const char *instr) {
+
+       char *outstr;
+       unsigned int len;
+
+       if (instr == NULL)
+               return NULL;
+
+       len = strlen(instr);
+
+       outstr = isc_mem_allocate(ns_g_mctx ,(2 * len * sizeof(char)) + 1);
+       if (outstr == NULL)
+               return NULL;
+
+       mysql_real_escape_string(mysql, outstr, instr, len);
+
+       return outstr;
+}
+
+/*%
+ * This function is the real core of the driver.   Zone, record
+ * and client strings are passed in (or NULL is passed if the
+ * string is not available).  The type of query we want to run
+ * is indicated by the query flag, and the dbdata object is passed
+ * passed in to.  dbdata really holds a single database instance.
+ * The function will construct and run the query, hopefully getting
+ * a result set.
+ */
+
+static isc_result_t
+mysql_get_resultset(const char *zone, const char *record,
+                   const char *client, unsigned int query,
+                   void *dbdata, MYSQL_RES **rs)
+{
+       isc_result_t result;
+       dbinstance_t *dbi = NULL;
+       char *querystring = NULL;
+       unsigned int i = 0;
+       unsigned int j = 0;
+       int qres = 0;
+
+       if (query != COUNTZONE)
+               REQUIRE(*rs == NULL);
+       else
+               REQUIRE(rs == NULL);
+
+       /* get db instance / connection */
+       dbi =  (dbinstance_t *) dbdata;
+
+       /* if DBI is null, can't do anything else */
+       if (dbi == NULL) {
+               result = ISC_R_FAILURE;
+               goto cleanup;
+       }
+
+       /* what type of query are we going to run? */
+       switch(query) {
+       case ALLNODES:
+               /*
+                * if the query was not passed in from the config file
+                * then we can't run it.  return not_implemented, so
+                * it's like the code for that operation was never
+                * built into the driver.... AHHH flexibility!!!
+                */
+               if (dbi->allnodes_q == NULL) {
+                       result = ISC_R_NOTIMPLEMENTED;
+                       goto cleanup;
+               }
+               break;
+       case ALLOWXFR:
+               /* same as comments as ALLNODES */
+               if (dbi->allowxfr_q == NULL) {
+                       result = ISC_R_NOTIMPLEMENTED;
+                       goto cleanup;
+               }
+               break;
+       case AUTHORITY:
+               /* same as comments as ALLNODES */
+               if (dbi->authority_q == NULL) {
+                       result = ISC_R_NOTIMPLEMENTED;
+                       goto cleanup;
+               }
+               break;
+       case FINDZONE:
+               /* this is required.  It's the whole point of DLZ! */
+               if (dbi->findzone_q == NULL) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                                     "No query specified for findzone.  "
+                                     "Findzone requires a query");
+                       result = ISC_R_FAILURE;
+                       goto cleanup;
+               }
+               break;
+       case COUNTZONE:
+               /* same as comments as ALLNODES */
+               if (dbi->countzone_q == NULL) {
+                       result = ISC_R_NOTIMPLEMENTED;
+                       goto cleanup;
+               }
+               break;
+       case LOOKUP:
+               /* this is required.  It's also a major point of DLZ! */
+               if (dbi->lookup_q == NULL) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                                     "No query specified for lookup.  "
+                                     "Lookup requires a query");
+                       result = ISC_R_FAILURE;
+                       goto cleanup;
+               }
+               break;
+       default:
+               /*
+                * this should never happen.  If it does, the code is
+                * screwed up!
+                */
+               UNEXPECTED_ERROR(__FILE__, __LINE__,
+                                "Incorrect query flag passed to "
+                                "mysql_get_resultset");
+               result = ISC_R_UNEXPECTED;
+               goto cleanup;
+       }
+
+
+       /*
+        * was a zone string passed?  If so, make it safe for use in
+        * queries.
+        */
+       if (zone != NULL) {
+               dbi->zone = mysqldrv_escape_string((MYSQL *) dbi->dbconn,
+                                                  zone);
+               if (dbi->zone == NULL) {
+                       result = ISC_R_NOMEMORY;
+                       goto cleanup;
+               }
+       } else {        /* no string passed, set the string pointer to NULL */
+               dbi->zone = NULL;
+       }
+
+       /*
+        * was a record string passed?  If so, make it safe for use in
+        * queries.
+        */
+       if (record != NULL) {
+               dbi->record = mysqldrv_escape_string((MYSQL *) dbi->dbconn,
+                                                    record);
+               if (dbi->record == NULL) {
+                       result = ISC_R_NOMEMORY;
+                       goto cleanup;
+               }
+       } else {        /* no string passed, set the string pointer to NULL */
+               dbi->record = NULL;
+       }
+
+       /*
+        * was a client string passed?  If so, make it safe for use in
+        * queries.
+        */
+       if (client != NULL) {
+               dbi->client = mysqldrv_escape_string((MYSQL *) dbi->dbconn,
+                                                    client);
+               if (dbi->client == NULL) {
+                       result = ISC_R_NOMEMORY;
+                       goto cleanup;
+               }
+       } else {        /* no string passed, set the string pointer to NULL */
+               dbi->client = NULL;
+       }
+
+       /*
+        * what type of query are we going to run?  this time we build
+        * the actual query to run.
+        */
+       switch(query) {
+       case ALLNODES:
+               querystring = build_querystring(ns_g_mctx, dbi->allnodes_q);
+               break;
+       case ALLOWXFR:
+               querystring = build_querystring(ns_g_mctx, dbi->allowxfr_q);
+               break;
+       case AUTHORITY:
+               querystring = build_querystring(ns_g_mctx, dbi->authority_q);
+               break;
+       case FINDZONE:
+               querystring = build_querystring(ns_g_mctx, dbi->findzone_q);
+               break;
+       case COUNTZONE:
+               querystring = build_querystring(ns_g_mctx, dbi->countzone_q);
+               break;
+       case LOOKUP:
+               querystring = build_querystring(ns_g_mctx, dbi->lookup_q);
+               break;
+       default:
+               /*
+                * this should never happen.  If it does, the code is
+                * screwed up!
+                */
+               UNEXPECTED_ERROR(__FILE__, __LINE__,
+                                "Incorrect query flag passed to "
+                                "mysql_get_resultset");
+               result = ISC_R_UNEXPECTED;
+               goto cleanup;
+       }
+
+       /* if the querystring is null, Bummer, outta RAM.  UPGRADE TIME!!!   */
+       if (querystring == NULL) {
+               result = ISC_R_NOMEMORY;
+               goto cleanup;
+       }
+
+       /*
+        * output the full query string during debug so we can see
+        * what lame error the query has.
+        */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
+                     "\nQuery String: %s\n", querystring);
+
+       /* attempt query up to 3 times. */
+       for (i=0; i < 3; i++) {
+               qres = mysql_query((MYSQL *) dbi->dbconn, querystring);
+               if (qres == 0)
+                       break;
+               for (j=0; mysql_ping((MYSQL *) dbi->dbconn) != 0 && j < 4; j++)
+                       ;
+       }
+
+       if (qres == 0) {
+               result = ISC_R_SUCCESS;
+               if (query != COUNTZONE) {
+                       *rs = mysql_store_result((MYSQL *) dbi->dbconn);
+                       if (*rs == NULL)
+                               result = ISC_R_FAILURE;
+               }
+       } else {
+               result = ISC_R_FAILURE;
+       }
+
+
+ cleanup:
+       /* it's always good to cleanup after yourself */
+
+       /* if we couldn't even get DBI, just return NULL */
+       if (dbi == NULL)
+               return ISC_R_FAILURE;
+
+       /* free dbi->zone string */
+       if (dbi->zone != NULL)
+               isc_mem_free(ns_g_mctx, dbi->zone);
+
+       /* free dbi->record string */
+       if (dbi->record != NULL)
+               isc_mem_free(ns_g_mctx, dbi->record);
+
+       /* free dbi->client string */
+       if (dbi->client != NULL)
+               isc_mem_free(ns_g_mctx, dbi->client);
+
+       /* release query string */
+       if (querystring  != NULL)
+               isc_mem_free(ns_g_mctx, querystring);
+
+       /* return result */
+       return result;
+}
+
+/*%
+ * The processing of result sets for lookup and authority are
+ * exactly the same.  So that functionality has been moved
+ * into this function to minimize code.
+ */
+
+static isc_result_t
+mysql_process_rs(dns_sdlzlookup_t *lookup, MYSQL_RES *rs)
+{
+       isc_result_t result = ISC_R_NOTFOUND;
+       MYSQL_ROW row;
+       unsigned int fields;
+       unsigned int j;
+       unsigned int len;
+       char *tmpString;
+       char *endp;
+       int ttl;
+
+       row = mysql_fetch_row(rs);      /* get a row from the result set */
+       fields = mysql_num_fields(rs);  /* how many columns in result set */
+       while (row != NULL) {
+               switch(fields) {
+               case 1:
+                       /*
+                        * one column in rs, it's the data field.  use
+                        * default type of A record, and default TTL
+                        * of 86400
+                        */
+                       result = dns_sdlz_putrr(lookup, "a", 86400,
+                                               safeGet(row[0]));
+                       break;
+               case 2:
+                       /*
+                        * two columns, data field, and data type.
+                        * use default TTL of 86400.
+                        */
+                       result = dns_sdlz_putrr(lookup, safeGet(row[0]), 86400,
+                                               safeGet(row[1]));
+                       break;
+               case 3:
+                       /*
+                        * three columns, all data no defaults.
+                        * convert text to int, make sure it worked
+                        * right.
+                        */
+                       ttl = strtol(safeGet(row[0]), &endp, 10);
+                       if (*endp != '\0' || ttl < 0) {
+                               isc_log_write(dns_lctx,
+                                             DNS_LOGCATEGORY_DATABASE,
+                                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                             "mysql driver ttl must be "
+                                             "a postive number");
+                       }
+                       result = dns_sdlz_putrr(lookup, safeGet(row[1]), ttl,
+                                               safeGet(row[2]));
+                       break;
+               default:
+                       /*
+                        * more than 3 fields, concatenate the last
+                        * ones together.  figure out how long to make
+                        * string.
+                        */
+                       for (j=2, len=0; j < fields; j++) {
+                               len += strlen(safeGet(row[j])) + 1;
+                       }
+                       /*
+                        * allocate string memory, allow for NULL to
+                        * term string
+                        */
+                       tmpString = isc_mem_allocate(ns_g_mctx, len + 1);
+                       if (tmpString == NULL) {
+                               /* major bummer, need more ram */
+                               isc_log_write(dns_lctx,
+                                             DNS_LOGCATEGORY_DATABASE,
+                                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                             "mysql driver unable "
+                                             "to allocate memory for "
+                                             "temporary string");
+                               mysql_free_result(rs);
+                               return (ISC_R_FAILURE); /* Yeah, I'd say! */
+                       }
+                       /* copy field to tmpString */
+                       strcpy(tmpString, safeGet(row[2]));
+
+
+                       /*
+                        * concat the rest of fields together, space
+                        * between each one.
+                        */
+                       for (j=3; j < fields; j++) {
+                               strcat(tmpString, " ");
+                               strcat(tmpString, safeGet(row[j]));
+                       }
+                       /* convert text to int, make sure it worked right */
+                       ttl = strtol(safeGet(row[0]), &endp, 10);
+                       if (*endp != '\0' || ttl < 0) {
+                               isc_log_write(dns_lctx,
+                                             DNS_LOGCATEGORY_DATABASE,
+                                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                             "mysql driver ttl must be "
+                                             "a postive number");
+                       }
+                       /* ok, now tell Bind about it. */
+                       result = dns_sdlz_putrr(lookup, safeGet(row[1]),
+                                               ttl, tmpString);
+                       /* done, get rid of this thing. */
+                       isc_mem_free(ns_g_mctx, tmpString);
+               }
+               /* I sure hope we were successful */
+               if (result != ISC_R_SUCCESS) {
+                       /* nope, get rid of the Result set, and log a msg */
+                       mysql_free_result(rs);
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                     "dns_sdlz_putrr returned error. "
+                                     "Error code was: %s",
+                                     isc_result_totext(result));
+                       return (ISC_R_FAILURE);
+               }
+               row = mysql_fetch_row(rs);      /* get next row */
+       }
+
+       /* free result set memory */
+       mysql_free_result(rs);
+
+       /* return result code */
+       return result;
+}
+
+/*
+ * SDLZ interface methods
+ */
+
+/*% determine if the zone is supported by (in) the database */
+
+static isc_result_t
+mysql_findzone(void *driverarg, void *dbdata, const char *name)
+{
+       isc_result_t result;
+       MYSQL_RES *rs = NULL;
+       my_ulonglong rows;
+
+       UNUSED(driverarg);
+
+       /* run the query and get the result set from the database. */
+       result = mysql_get_resultset(name, NULL, NULL, FINDZONE, dbdata, &rs);
+       /* if we didn't get a result set, log an err msg. */
+       if (result != ISC_R_SUCCESS || rs == NULL) {
+               if (rs != NULL)
+                       mysql_free_result(rs);
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "mysql driver unable to return "
+                             "result set for findzone query");
+               return (ISC_R_FAILURE);
+       }
+       /* count how many rows in result set */
+       rows = mysql_num_rows(rs);
+       /* get rid of result set, we are done with it. */
+       mysql_free_result(rs);
+
+       /* if we returned any rows, zone is supported. */
+       if (rows > 0) {
+               mysql_get_resultset(name, NULL, NULL, COUNTZONE, dbdata, NULL);
+               return (ISC_R_SUCCESS);
+       }
+
+       /* no rows returned, zone is not supported. */
+       return (ISC_R_NOTFOUND);
+}
+
+/*% Determine if the client is allowed to perform a zone transfer */
+static isc_result_t
+mysql_allowzonexfr(void *driverarg, void *dbdata, const char *name,
+                  const char *client)
+{
+       isc_result_t result;
+       MYSQL_RES *rs = NULL;
+       my_ulonglong rows;
+
+       UNUSED(driverarg);
+
+       /* first check if the zone is supported by the database. */
+       result = mysql_findzone(driverarg, dbdata, name);
+       if (result != ISC_R_SUCCESS)
+               return (ISC_R_NOTFOUND);
+
+       /*
+        * if we get to this point we know the zone is supported by
+        * the database the only questions now are is the zone
+        * transfer is allowed for this client and did the config file
+        * have an allow zone xfr query.
+        *
+        * Run our query, and get a result set from the database.
+        */
+       result = mysql_get_resultset(name, NULL, client, ALLOWXFR,
+                                    dbdata, &rs);
+       /* if we get "not implemented", send it along. */
+       if (result == ISC_R_NOTIMPLEMENTED)
+               return result;
+       /* if we didn't get a result set, log an err msg. */
+       if (result != ISC_R_SUCCESS || rs == NULL) {
+               if (rs != NULL)
+                       mysql_free_result(rs);
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "mysql driver unable to return "
+                             "result set for allow xfr query");
+               return (ISC_R_FAILURE);
+       }
+       /* count how many rows in result set */
+       rows = mysql_num_rows(rs);
+       /* get rid of result set, we are done with it. */
+       mysql_free_result(rs);
+
+       /* if we returned any rows, zone xfr is allowed. */
+       if (rows > 0)
+               return (ISC_R_SUCCESS);
+
+       /* no rows returned, zone xfr not allowed */
+       return (ISC_R_NOPERM);
+}
+
+/*%
+ * If the client is allowed to perform a zone transfer, the next order of
+ * business is to get all the nodes in the zone, so bind can respond to the
+ * query.
+ */
+static isc_result_t
+mysql_allnodes(const char *zone, void *driverarg, void *dbdata,
+              dns_sdlzallnodes_t *allnodes)
+{
+       isc_result_t result;
+       MYSQL_RES *rs = NULL;
+       MYSQL_ROW row;
+       unsigned int fields;
+       unsigned int j;
+       unsigned int len;
+       char *tmpString;
+       char *endp;
+       int ttl;
+
+       UNUSED(driverarg);
+
+       /* run the query and get the result set from the database. */
+       result = mysql_get_resultset(zone, NULL, NULL, ALLNODES, dbdata, &rs);
+       /* if we get "not implemented", send it along */
+       if (result == ISC_R_NOTIMPLEMENTED)
+               return result;
+       /* if we didn't get a result set, log an err msg. */
+       if (result != ISC_R_SUCCESS) {
+               if (rs != NULL)
+                       mysql_free_result(rs);
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "mysql driver unable to return "
+                             "result set for all nodes query");
+               return (ISC_R_FAILURE);
+       }
+
+       result = ISC_R_NOTFOUND;
+
+       row = mysql_fetch_row(rs);      /* get a row from the result set */
+       fields = mysql_num_fields(rs);  /* how many columns in result set */
+       while (row != NULL) {
+               if (fields < 4) {       /* gotta have at least 4 columns */
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                     "mysql driver too few fields returned "
+                                     "by all nodes query");
+               }
+               /* convert text to int, make sure it worked right  */
+               ttl = strtol(safeGet(row[0]), &endp, 10);
+               if (*endp != '\0' || ttl < 0) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                     "mysql driver ttl must be "
+                                     "a postive number");
+               }
+               if (fields == 4) {
+                       /* tell Bind about it. */
+                       result = dns_sdlz_putnamedrr(allnodes, safeGet(row[0]),
+                                                    safeGet(row[1]), ttl,
+                                                    safeGet(row[3]));
+               } else {
+                       /*
+                        * more than 4 fields, concatenate the last
+                        * ones together.  figure out how long to make
+                        * string.
+                        */
+                       for (j=3, len=0; j < fields; j++) {
+                               len += strlen(safeGet(row[j])) + 1;
+                       }
+                       /* allocate memory, allow for NULL to term string */
+                       tmpString = isc_mem_allocate(ns_g_mctx, len + 1);
+                       if (tmpString == NULL) {        /* we need more ram. */
+                               isc_log_write(dns_lctx,
+                                             DNS_LOGCATEGORY_DATABASE,
+                                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                             "mysql driver unable "
+                                             "to allocate memory for "
+                                             "temporary string");
+                               mysql_free_result(rs);
+                               return (ISC_R_FAILURE);
+                       }
+                       /* copy this field to tmpString */
+                       strcpy(tmpString, safeGet(row[3]));
+                       /* concatonate the rest, with spaces between */
+                       for (j=4; j < fields; j++) {
+                               strcat(tmpString, " ");
+                               strcat(tmpString, safeGet(row[j]));
+                       }
+                       /* tell Bind about it. */
+                       result = dns_sdlz_putnamedrr(allnodes, safeGet(row[2]),
+                                                    safeGet(row[1]),
+                                                    ttl, tmpString);
+                       isc_mem_free(ns_g_mctx, tmpString);
+               }
+               /* if we weren't successful, log err msg */
+               if (result != ISC_R_SUCCESS) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                     "dns_sdlz_putnamedrr returned error. "
+                                     "Error code was: %s",
+                                     isc_result_totext(result));
+                       result = ISC_R_FAILURE;
+                       break;
+               }
+               /* get next row from the result set */
+               row = mysql_fetch_row(rs);
+       }
+
+       /* free result set memory */
+       mysql_free_result(rs);
+
+       return result;
+}
+
+/*% if the lookup function does not return SOA or NS records for the zone,
+ * use this function to get that information for Bind.
+ */
+
+static isc_result_t
+mysql_authority(const char *zone, void *driverarg, void *dbdata,
+               dns_sdlzlookup_t *lookup)
+{
+       isc_result_t result;
+       MYSQL_RES *rs = NULL;
+
+       UNUSED(driverarg);
+
+       /* run the query and get the result set from the database. */
+       result = mysql_get_resultset(zone, NULL, NULL, AUTHORITY, dbdata, &rs);
+       /* if we get "not implemented", send it along */
+       if (result == ISC_R_NOTIMPLEMENTED)
+               return result;
+       /* if we didn't get a result set, log an err msg. */
+       if (result != ISC_R_SUCCESS) {
+               if (rs != NULL)
+                       mysql_free_result(rs);
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "mysql driver unable to return "
+                             "result set for authority query");
+               return (ISC_R_FAILURE);
+       }
+       /*
+        * lookup and authority result sets are processed in the same
+        * manner mysql_process_rs does the job for both functions.
+        */
+       return mysql_process_rs(lookup, rs);
+}
+
+/*% if zone is supported, lookup up a (or multiple) record(s) in it */
+static isc_result_t
+mysql_lookup(const char *zone, const char *name, void *driverarg,
+            void *dbdata, dns_sdlzlookup_t *lookup)
+{
+       isc_result_t result;
+       MYSQL_RES *rs = NULL;
+
+       UNUSED(driverarg);
+
+       /* run the query and get the result set from the database. */
+       result = mysql_get_resultset(zone, name, NULL, LOOKUP, dbdata, &rs);
+       /* if we didn't get a result set, log an err msg. */
+       if (result != ISC_R_SUCCESS) {
+               if (rs != NULL)
+                       mysql_free_result(rs);
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "mysql driver unable to return "
+                             "result set for lookup query");
+               return (ISC_R_FAILURE);
+       }
+       /*
+        * lookup and authority result sets are processed in the same manner
+        * mysql_process_rs does the job for both functions.
+        */
+       return mysql_process_rs(lookup, rs);
+}
+
+/*%
+ * create an instance of the driver.  Remember, only 1 copy of the driver's
+ * code is ever loaded, the driver has to remember which context it's
+ * operating in.  This is done via use of the dbdata arguement which is
+ * passed into all query functions.
+ */
+static isc_result_t
+mysql_create(const char *dlzname, unsigned int argc, char *argv[],
+            void *driverarg, void **dbdata)
+{
+       isc_result_t result;
+       dbinstance_t *dbi = NULL;
+       char *tmp = NULL;
+       char *dbname = NULL;
+       char *host = NULL;
+       char *user = NULL;
+       char *pass = NULL;
+       char *socket = NULL;
+       int port;
+       MYSQL *dbc;
+       char *endp;
+       int j;
+       unsigned int flags = 0;
+
+       UNUSED(driverarg);
+       UNUSED(dlzname);
+
+       /* verify we have at least 4 arg's passed to the driver */
+       if (argc < 4) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "mysql driver requires "
+                             "at least 4 command line args.");
+               return (ISC_R_FAILURE);
+       }
+
+       /* no more than 8 arg's should be passed to the driver */
+       if (argc > 8) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "mysql driver cannot accept "
+                             "more than 7 command line args.");
+               return (ISC_R_FAILURE);
+       }
+
+       /* parse connection string and get paramters. */
+
+       /* get db name - required */
+       dbname = getParameterValue(argv[1], "dbname=");
+       if (dbname == NULL) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "mysql driver requires a dbname parameter.");
+               result = ISC_R_FAILURE;
+               goto full_cleanup;
+       }
+
+       /* get db port.  Not required, but must be > 0 if specified */
+       tmp = getParameterValue(argv[1], "port=");
+       if (tmp == NULL) {
+               port = 0;
+       } else {
+               port = strtol(tmp, &endp, 10);
+               if (*endp != '\0' || port < 0) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                     "Mysql driver port "
+                                     "must be a positive number.");
+                       isc_mem_free(ns_g_mctx, tmp);
+                       result = ISC_R_FAILURE;
+                       goto full_cleanup;
+               }
+               isc_mem_free(ns_g_mctx, tmp);
+       }
+
+       /* how many queries were passed in from config file? */
+       switch(argc) {
+       case 4:
+               result = build_sqldbinstance(ns_g_mctx, NULL, NULL, NULL,
+                                            argv[2], argv[3], NULL, &dbi);
+               break;
+       case 5:
+               result = build_sqldbinstance(ns_g_mctx, NULL, NULL, argv[4],
+                                            argv[2], argv[3], NULL, &dbi);
+               break;
+       case 6:
+               result = build_sqldbinstance(ns_g_mctx, argv[5], NULL, argv[4],
+                                            argv[2], argv[3], NULL, &dbi);
+               break;
+       case 7:
+               result = build_sqldbinstance(ns_g_mctx, argv[5],
+                                            argv[6], argv[4],
+                                            argv[2], argv[3], NULL, &dbi);
+               break;
+       case 8:
+               result = build_sqldbinstance(ns_g_mctx, argv[5],
+                                            argv[6], argv[4],
+                                            argv[2], argv[3], argv[7], &dbi);
+               break;
+       default:
+               /* not really needed, should shut up compiler. */
+               result = ISC_R_FAILURE;
+       }
+
+       /* unsuccessful?, log err msg and cleanup. */
+       if (result != ISC_R_SUCCESS) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "mysql driver could not create "
+                             "database instance object.");
+               result = ISC_R_FAILURE;
+               goto full_cleanup;
+       }
+
+       /* create and set db connection */
+       dbi->dbconn = mysql_init(NULL);
+
+       /* if db connection cannot be created, log err msg and cleanup. */
+       if (dbi->dbconn == NULL) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "mysql driver could not allocate "
+                             "memory for database connection");
+               result = ISC_R_FAILURE;
+               goto full_cleanup;
+       }
+
+       tmp = getParameterValue(argv[1], "compress=");
+       if (tmp != NULL) {
+               if (strcasecmp(tmp, "true") == 0)
+                       flags = CLIENT_COMPRESS;
+               isc_mem_free(ns_g_mctx, tmp);
+       }
+
+       tmp = getParameterValue(argv[1], "ssl=");
+       if (tmp != NULL) {
+               if (strcasecmp(tmp, "true") == 0)
+                       flags = flags | CLIENT_SSL;
+               isc_mem_free(ns_g_mctx, tmp);
+       }
+
+       tmp = getParameterValue(argv[1], "space=");
+       if (tmp != NULL) {
+               if (strcasecmp(tmp, "ignore") == 0)
+                       flags = flags | CLIENT_IGNORE_SPACE;
+               isc_mem_free(ns_g_mctx, tmp);
+       }
+
+       dbc = NULL;
+       host = getParameterValue(argv[1], "host=");
+       user = getParameterValue(argv[1], "user=");
+       pass = getParameterValue(argv[1], "pass=");
+       socket = getParameterValue(argv[1], "socket=");
+
+       for (j=0; dbc == NULL && j < 4; j++)
+               dbc = mysql_real_connect((MYSQL *) dbi->dbconn, host,
+                                        user, pass, dbname, port, socket,
+                                        flags);
+
+       /* let user know if we couldn't connect. */
+       if (dbc == NULL) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "mysql driver failed to create "
+                             "database connection after 4 attempts");
+               result = ISC_R_FAILURE;
+               goto full_cleanup;
+       }
+
+       /* return db connection via dbdata */
+       *dbdata = dbi;
+
+       result = ISC_R_SUCCESS;
+       goto cleanup;
+
+ full_cleanup:
+
+       destroy_sqldbinstance(dbi);
+
+ cleanup:
+
+       if (dbname != NULL)
+               isc_mem_free(ns_g_mctx, dbname);
+       if (host != NULL)
+               isc_mem_free(ns_g_mctx, host);
+       if (user != NULL)
+               isc_mem_free(ns_g_mctx, user);
+       if (pass != NULL)
+               isc_mem_free(ns_g_mctx, pass);
+       if (socket != NULL)
+               isc_mem_free(ns_g_mctx, socket);
+
+
+       return result;
+}
+
+/*%
+ * destroy the driver.  Remember, only 1 copy of the driver's
+ * code is ever loaded, the driver has to remember which context it's
+ * operating in.  This is done via use of the dbdata arguement.
+ * so we really only need to clean it up since we are not using driverarg.
+ */
+
+static void
+mysql_destroy(void *driverarg, void *dbdata)
+{
+       dbinstance_t *dbi;
+
+       UNUSED(driverarg);
+
+       dbi = (dbinstance_t *) dbdata;
+
+       /* release DB connection */
+       if (dbi->dbconn != NULL)
+               mysql_close((MYSQL *) dbi->dbconn);
+
+       /* destroy DB instance */
+       destroy_sqldbinstance(dbi);
+}
+
+/* pointers to all our runtime methods. */
+/* this is used during driver registration */
+/* i.e. in dlz_mysql_init below. */
+static dns_sdlzmethods_t dlz_mysql_methods = {
+       mysql_create,
+       mysql_destroy,
+       mysql_findzone,
+       mysql_lookup,
+       mysql_authority,
+       mysql_allnodes,
+       mysql_allowzonexfr
+};
+
+/*%
+ * Wrapper around dns_sdlzregister().
+ */
+isc_result_t
+dlz_mysql_init(void) {
+       isc_result_t result;
+
+       /*
+        * Write debugging message to log
+        */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                     "Registering DLZ mysql driver.");
+
+       /* Driver is always threadsafe.  Because of the way MySQL handles
+         * threads the MySQL driver can only be used when bind is run single
+         * threaded.  Using MySQL with Bind running multi-threaded is not
+         * allowed.  When using the MySQL driver "-n1" should always be
+         * passed to Bind to guarantee single threaded operation.
+        */
+       result = dns_sdlzregister("mysql", &dlz_mysql_methods, NULL,
+                                 DNS_SDLZFLAG_RELATIVEOWNER |
+                                 DNS_SDLZFLAG_RELATIVERDATA |
+                                 DNS_SDLZFLAG_THREADSAFE,
+                                 ns_g_mctx, &dlz_mysql);
+       /* if we can't register the driver, there are big problems. */
+       if (result != ISC_R_SUCCESS) {
+               UNEXPECTED_ERROR(__FILE__, __LINE__,
+                                "dns_sdlzregister() failed: %s",
+                                isc_result_totext(result));
+               result = ISC_R_UNEXPECTED;
+       }
+
+
+       return result;
+}
+
+/*%
+ * Wrapper around dns_sdlzunregister().
+ */
+void
+dlz_mysql_clear(void) {
+
+       /*
+        * Write debugging message to log
+        */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                     "Unregistering DLZ mysql driver.");
+
+       /* unregister the driver. */
+       if (dlz_mysql != NULL)
+               dns_sdlzunregister(&dlz_mysql);
+}
+
+#endif
diff --git a/contrib/dlz/drivers/dlz_odbc_driver.c b/contrib/dlz/drivers/dlz_odbc_driver.c
new file mode 100644 (file)
index 0000000..b0ba115
--- /dev/null
@@ -0,0 +1,1568 @@
+/*
+ * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
+ *
+ * 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 STICHTING NLNET
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * STICHTING NLNET 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 development of Dynamically Loadable Zones (DLZ) for Bind 9 was
+ * conceived and contributed by Rob Butler.
+ *
+ * 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 ROB BUTLER
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * ROB BUTLER 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.
+ */
+
+/*
+ * Copyright (C) 1999-2001  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.
+ */
+
+#ifdef DLZ_ODBC
+
+#include <config.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <dns/log.h>
+#include <dns/sdlz.h>
+#include <dns/result.h>
+
+#include <isc/mem.h>
+#include <isc/platform.h>
+#include <isc/print.h>
+#include <isc/result.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <named/globals.h>
+
+#include <dlz/sdlz_helper.h>
+#include <dlz/dlz_odbc_driver.h>
+
+#include <sql.h>
+#include <sqlext.h>
+#include <sqltypes.h>
+
+static dns_sdlzimplementation_t *dlz_odbc = NULL;
+
+#define dbc_search_limit 30
+#define ALLNODES 1
+#define ALLOWXFR 2
+#define AUTHORITY 3
+#define FINDZONE 4
+#define LOOKUP 5
+
+#define sqlOK(a) ((a == SQL_SUCCESS || a == SQL_SUCCESS_WITH_INFO) ? -1 : 0)
+
+/*
+ * Private Structures
+ */
+
+/*
+ * structure to hold ODBC connection & statement
+ */
+
+typedef struct{
+       SQLHDBC   dbc;
+       SQLHSTMT  stmnt;
+} odbc_db_t;
+
+/*
+ * Structure to hold everthing needed by this "instance" of the odbc driver
+ * remember, the driver code is only loaded once, but may have many separate
+ * instances
+ */
+
+typedef struct {
+
+#ifdef ISC_PLATFORM_USETHREADS
+
+    db_list_t    *db;       /* handle to a list of DB */
+
+#else
+
+    dbinstance_t *db;       /* handle to db */
+
+#endif
+
+    SQLHENV      sql_env;  /* handle to SQL environment */
+    SQLCHAR      *dsn;
+    SQLCHAR      *user;
+    SQLCHAR      *pass;
+} odbc_instance_t;
+
+/* forward reference */
+
+static size_t
+odbc_makesafe(char *to, const char *from, size_t length);
+
+/*
+ * Private methods
+ */
+
+static SQLSMALLINT
+safeLen(void *a) {
+       if (a == NULL)
+               return 0;
+       return strlen((char *) a);
+}
+
+/*% propertly cleans up an odbc_instance_t */
+
+static void
+destroy_odbc_instance(odbc_instance_t *odbc_inst) {
+
+#ifdef ISC_PLATFORM_USETHREADS
+
+       dbinstance_t *ndbi = NULL;
+       dbinstance_t *dbi = NULL;
+
+       /* get the first DBI in the list */
+       ndbi = ISC_LIST_HEAD(*odbc_inst->db);
+
+       /* loop through the list */
+       while (ndbi != NULL) {
+               dbi = ndbi;
+               /* get the next DBI in the list */
+               ndbi = ISC_LIST_NEXT(dbi, link);
+
+               /* if we have a connection / statement object in memory */
+               if (dbi->dbconn != NULL) {
+                       /* free statement handle */
+                       if (((odbc_db_t *) (dbi->dbconn))->stmnt != NULL) {
+                               SQLFreeHandle(SQL_HANDLE_STMT,
+                                             ((odbc_db_t *)
+                                              (dbi->dbconn))->stmnt);
+                               ((odbc_db_t *) (dbi->dbconn))->stmnt = NULL;
+                       }
+
+                       /* disconnect from database & free connection handle */
+                       if (((odbc_db_t *) (dbi->dbconn))->dbc != NULL) {
+                               SQLDisconnect(((odbc_db_t *)
+                                              dbi->dbconn)->dbc);
+                               SQLFreeHandle(SQL_HANDLE_DBC,
+                                             ((odbc_db_t *)
+                                              (dbi->dbconn))->dbc);
+                               ((odbc_db_t *) (dbi->dbconn))->dbc = NULL;
+                       }
+
+                       /* free memory that held connection & statement. */
+                       isc_mem_free(ns_g_mctx, dbi->dbconn);
+               }
+               /* release all memory that comprised a DBI */
+               destroy_sqldbinstance(dbi);
+       }
+       /* release memory for the list structure */
+       isc_mem_put(ns_g_mctx, odbc_inst->db, sizeof(db_list_t));
+
+#else /* ISC_PLATFORM_USETHREADS */
+
+       /* free statement handle */
+       if (((odbc_db_t *) (odbc_inst->db->dbconn))->stmnt != NULL) {
+               SQLFreeHandle(SQL_HANDLE_STMT,
+                             ((odbc_db_t *) (odbc_inst->db->dbconn))->stmnt);
+               ((odbc_db_t *) (odbc_inst->db->dbconn))->stmnt = NULL;
+       }
+
+       /* disconnect from database, free connection handle */
+       if (((odbc_db_t *) (odbc_inst->db->dbconn))->dbc != NULL) {
+               SQLDisconnect(((odbc_db_t *) (odbc_inst->db->dbconn))->dbc);
+               SQLFreeHandle(SQL_HANDLE_DBC,
+                             ((odbc_db_t *) (odbc_inst->db->dbconn))->dbc);
+               ((odbc_db_t *) (odbc_inst->db->dbconn))->dbc = NULL;
+       }
+       /*      free mem for the odbc_db_t structure held in db */
+       if (((odbc_db_t *) odbc_inst->db->dbconn) != NULL) {
+               isc_mem_free(ns_g_mctx, odbc_inst->db->dbconn);
+               odbc_inst->db->dbconn = NULL;
+       }
+
+       if (odbc_inst->db != NULL)
+               destroy_sqldbinstance(odbc_inst->db);
+
+#endif /* ISC_PLATFORM_USETHREADS */
+
+
+       /* free sql environment */
+       if (odbc_inst->sql_env != NULL)
+               SQLFreeHandle(SQL_HANDLE_ENV, odbc_inst->sql_env);
+
+       /* free ODBC instance strings */
+       if (odbc_inst->dsn != NULL)
+               isc_mem_free(ns_g_mctx, odbc_inst->dsn);
+       if (odbc_inst->pass != NULL)
+               isc_mem_free(ns_g_mctx, odbc_inst->pass);
+       if (odbc_inst->user != NULL)
+               isc_mem_free(ns_g_mctx, odbc_inst->user);
+
+       /* free memory for odbc_inst */
+       if (odbc_inst != NULL)
+               isc_mem_put(ns_g_mctx, odbc_inst, sizeof(odbc_instance_t));
+
+}
+
+/*% Connects to database, and creates ODBC statements */
+
+static isc_result_t
+odbc_connect(odbc_instance_t *dbi, odbc_db_t **dbc) {
+
+       odbc_db_t *ndb = *dbc;
+       SQLRETURN sqlRes;
+       isc_result_t result = ISC_R_SUCCESS;
+
+       if (ndb != NULL) {
+               /*
+                * if db != null, we have to do some cleanup
+                * if statement handle != null free it
+                */
+               if (ndb->stmnt != NULL) {
+                       SQLFreeHandle(SQL_HANDLE_STMT, ndb->stmnt);
+                       ndb->stmnt = NULL;
+               }
+
+               /* if connection handle != null free it */
+               if (ndb->dbc != NULL) {
+                       SQLFreeHandle(SQL_HANDLE_DBC, ndb->dbc);
+                       ndb->dbc = NULL;
+               }
+       } else {
+               ndb = isc_mem_allocate(ns_g_mctx, sizeof(odbc_db_t));
+               if (ndb == NULL) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                     "Odbc driver unable to allocate memory");
+                       return ISC_R_NOMEMORY;
+               }
+               memset(ndb, 0, sizeof(odbc_db_t));
+       }
+
+       sqlRes = SQLAllocHandle(SQL_HANDLE_DBC, dbi->sql_env, &(ndb->dbc));
+       if (!sqlOK(sqlRes)) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Odbc driver unable to allocate memory");
+               result = ISC_R_NOMEMORY;
+               goto cleanup;
+       }
+
+       sqlRes = SQLConnect(ndb->dbc, dbi->dsn, safeLen(dbi->dsn), dbi->user,
+                           safeLen(dbi->user), dbi->pass, safeLen(dbi->pass));
+       if (!sqlOK(sqlRes)) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Odbc driver unable to connect");
+               result = ISC_R_FAILURE;
+               goto cleanup;
+       }
+
+       sqlRes = SQLAllocHandle(SQL_HANDLE_STMT, ndb->dbc, &(ndb->stmnt));
+       if (!sqlOK(sqlRes)) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Odbc driver unable to allocate memory");
+               result = ISC_R_NOMEMORY;
+               goto cleanup;
+       }
+
+       *dbc = ndb;
+
+       return ISC_R_SUCCESS;
+
+ cleanup:
+
+       if (ndb != NULL) {
+
+               /* if statement handle != null free it */
+               if (ndb->stmnt != NULL) {
+                       SQLFreeHandle(SQL_HANDLE_STMT, ndb->stmnt);
+                       ndb->stmnt = NULL;
+               }
+
+               /* if connection handle != null free it */
+               if (ndb->dbc != NULL) {
+                       SQLDisconnect(ndb->dbc);
+                       SQLFreeHandle(SQL_HANDLE_DBC, ndb->dbc);
+                       ndb->dbc = NULL;
+               }
+               /* free memory holding ndb */
+               isc_mem_free(ns_g_mctx, ndb);
+       }
+
+       return result;
+}
+
+/*%
+ * Loops through the list of DB instances, attempting to lock
+ * on the mutex.  If successful, the DBI is reserved for use
+ * and the thread can perform queries against the database.
+ * If the lock fails, the next one in the list is tried.
+ * looping continues until a lock is obtained, or until
+ * the list has been searched dbc_search_limit times.
+ * This function is only used when the driver is compiled for
+ * multithreaded operation.
+ */
+
+#ifdef ISC_PLATFORM_USETHREADS
+
+static dbinstance_t *
+odbc_find_avail_conn(db_list_t *dblist)
+{
+       dbinstance_t *dbi = NULL;
+       dbinstance_t *head;
+       int count = 0;
+
+       /* get top of list */
+       head = dbi = ISC_LIST_HEAD(*dblist);
+
+       /* loop through list */
+       while (count < dbc_search_limit) {
+               /* try to lock on the mutex */
+               if (isc_mutex_trylock(&dbi->instance_lock) == ISC_R_SUCCESS)
+                       return dbi; /* success, return the DBI for use. */
+
+               /* not successful, keep trying */
+               dbi = ISC_LIST_NEXT(dbi, link);
+
+               /* check to see if we have gone to the top of the list. */
+               if (dbi == NULL) {
+                       count++;
+                       dbi = head;
+               }
+       }
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
+                     "Odbc driver unable to find available "
+                     "connection after searching %d times",
+                     count);
+       return NULL;
+}
+
+#endif /* ISC_PLATFORM_USETHREADS */
+
+/*% Allocates memory for a new string, and then constructs the new
+ * string by "escaping" the input string.  The new string is
+ * safe to be used in queries.  This is necessary because we cannot
+ * be sure of what types of strings are passed to us, and we don't
+ * want special characters in the string causing problems.
+ */
+
+static char *
+odbc_escape_string(const char *instr) {
+
+       char *outstr;
+       unsigned int len;
+
+       if (instr == NULL)
+               return NULL;
+
+       len = strlen(instr);
+
+       outstr = isc_mem_allocate(ns_g_mctx ,(2 * len * sizeof(char)) + 1);
+       if (outstr == NULL)
+               return NULL;
+
+       odbc_makesafe(outstr, instr, len);
+
+       return outstr;
+}
+
+/* ---------------
+ * Escaping arbitrary strings to get valid SQL strings/identifiers.
+ *
+ * Replaces "\\" with "\\\\" and "'" with "''".
+ * length is the length of the buffer pointed to by
+ * from.  The buffer at to must be at least 2*length + 1 characters
+ * long.  A terminating NUL character is written.
+ *
+ * NOTICE!!!
+ * This function was borrowed directly from PostgreSQL's libpq.
+ *
+ * The copyright statements from the original file containing this
+ * function are included below:
+ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ * ---------------
+ */
+
+static size_t
+odbc_makesafe(char *to, const char *from, size_t length)
+{
+       const char *source = from;
+       char       *target = to;
+       unsigned int remaining = length;
+
+       while (remaining > 0)
+       {
+               switch (*source)
+               {
+                       case '\\':
+                               *target = '\\';
+                               target++;
+                               *target = '\\';
+                               /* target and remaining are updated below. */
+                               break;
+
+                       case '\'':
+                               *target = '\'';
+                               target++;
+                               *target = '\'';
+                               /* target and remaining are updated below. */
+                               break;
+
+                       default:
+                               *target = *source;
+                               /* target and remaining are updated below. */
+               }
+               source++;
+               target++;
+               remaining--;
+       }
+
+       /* Write the terminating NUL character. */
+       *target = '\0';
+
+       return target - to;
+}
+
+/*%
+ * This function is the real core of the driver.   Zone, record
+ * and client strings are passed in (or NULL is passed if the
+ * string is not available).  The type of query we want to run
+ * is indicated by the query flag, and the dbdata object is passed
+ * passed in to.  dbdata really holds either:
+ *             1) a list of database instances (in multithreaded mode) OR
+ *             2) a single database instance (in single threaded mode)
+ * The function will construct the query and obtain an available
+ * database instance (DBI).  It will then run the query and hopefully
+ * obtain a result set.  The data base instance that is used is returned
+ * to the caller so they can get the data from the result set from it.
+ * If successfull, it will be the responsibility of the caller to close
+ * the cursor, and unlock the mutex of the DBI when they are done with it.
+ * If not successfull, this function will perform all the cleanup.
+ */
+
+
+static isc_result_t
+odbc_get_resultset(const char *zone, const char *record,
+                  const char *client, unsigned int query,
+                  void *dbdata, dbinstance_t **r_dbi)
+{
+
+       isc_result_t result;
+       dbinstance_t *dbi = NULL;
+       char *querystring = NULL;
+       unsigned int j = 0;
+       SQLRETURN sqlRes;
+
+       REQUIRE(*r_dbi == NULL);
+
+       /* get db instance / connection */
+#ifdef ISC_PLATFORM_USETHREADS
+
+       /* find an available DBI from the list */
+       dbi = odbc_find_avail_conn(((odbc_instance_t *) dbdata)->db);
+
+#else /* ISC_PLATFORM_USETHREADS */
+
+       /*
+        * only 1 DBI - no need to lock instance lock either
+        * only 1 thread in the whole process, no possible contention.
+        */
+       dbi =  (dbinstance_t *) ((odbc_instance_t *) dbdata)->db;
+
+#endif /* ISC_PLATFORM_USETHREADS */
+
+       /* if DBI is null, can't do anything else */
+       if (dbi == NULL) {
+               result = ISC_R_FAILURE;
+               goto cleanup;
+       }
+
+       /* what type of query are we going to run? */
+       switch(query) {
+       case ALLNODES:
+               /*
+                * if the query was not passed in from the config file
+                * then we can't run it.  return not_implemented, so
+                * it's like the code for that operation was never
+                * built into the driver.... AHHH flexibility!!!
+                */
+               if (dbi->allnodes_q == NULL) {
+                       result = ISC_R_NOTIMPLEMENTED;
+                       goto cleanup;
+               }
+               break;
+       case ALLOWXFR:
+               /* same as comments as ALLNODES */
+               if (dbi->allowxfr_q == NULL) {
+                       result = ISC_R_NOTIMPLEMENTED;
+                       goto cleanup;
+               }
+               break;
+       case AUTHORITY:
+               /* same as comments as ALLNODES */
+               if (dbi->authority_q == NULL) {
+                       result = ISC_R_NOTIMPLEMENTED;
+                       goto cleanup;
+               }
+               break;
+       case FINDZONE:
+               /* this is required.  It's the whole point of DLZ! */
+               if (dbi->findzone_q == NULL) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                                     "No query specified for findzone.  "
+                                     "Findzone requires a query");
+                       result = ISC_R_FAILURE;
+                       goto cleanup;
+               }
+               break;
+       case LOOKUP:
+               /* this is required.  It's also a major point of DLZ! */
+               if (dbi->lookup_q == NULL) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                                     "No query specified for lookup.  "
+                                     "Lookup requires a query");
+                       result = ISC_R_FAILURE;
+                       goto cleanup;
+               }
+               break;
+       default:
+               /*
+                * this should never happen.  If it does, the code is
+                * screwed up!
+                */
+               UNEXPECTED_ERROR(__FILE__, __LINE__,
+                                "Incorrect query flag passed to "
+                                "odbc_get_resultset");
+               result = ISC_R_UNEXPECTED;
+               goto cleanup;
+       }
+
+
+       /*
+        * was a zone string passed?  If so, make it safe for use in
+        * queries.
+        */
+       if (zone != NULL) {
+               dbi->zone = odbc_escape_string(zone);
+               if (dbi->zone == NULL) {
+                       result = ISC_R_NOMEMORY;
+                       goto cleanup;
+               }
+       } else {        /* no string passed, set the string pointer to NULL */
+               dbi->zone = NULL;
+       }
+
+       /*
+        * was a record string passed?  If so, make it safe for use in
+        * queries.
+        */
+       if (record != NULL) {
+               dbi->record = odbc_escape_string(record);
+               if (dbi->record == NULL) {
+                       result = ISC_R_NOMEMORY;
+                       goto cleanup;
+               }
+       } else {        /* no string passed, set the string pointer to NULL */
+               dbi->record = NULL;
+       }
+
+       /*
+        * was a client string passed?  If so, make it safe for use in
+        * queries.
+        */
+       if (client != NULL) {
+               dbi->client = odbc_escape_string(client);
+               if (dbi->client == NULL) {
+                       result = ISC_R_NOMEMORY;
+                       goto cleanup;
+               }
+       } else {        /* no string passed, set the string pointer to NULL */
+               dbi->client = NULL;
+       }
+
+       /*
+        * what type of query are we going to run?
+        * this time we build the actual query to run.
+        */
+       switch(query) {
+       case ALLNODES:
+               querystring = build_querystring(ns_g_mctx, dbi->allnodes_q);
+               break;
+       case ALLOWXFR:
+               querystring = build_querystring(ns_g_mctx, dbi->allowxfr_q);
+               break;
+       case AUTHORITY:
+               querystring = build_querystring(ns_g_mctx, dbi->authority_q);
+               break;
+       case FINDZONE:
+               querystring = build_querystring(ns_g_mctx, dbi->findzone_q);
+               break;
+       case LOOKUP:
+               querystring = build_querystring(ns_g_mctx, dbi->lookup_q);
+               break;
+       default:
+               /*
+                * this should never happen.  If it does, the code is
+                * screwed up!
+                */
+               UNEXPECTED_ERROR(__FILE__, __LINE__,
+                                "Incorrect query flag passed to "
+                                "odbc_get_resultset");
+               result = ISC_R_UNEXPECTED;
+               goto cleanup;
+       }
+
+       /* if the querystring is null, Bummer, outta RAM.  UPGRADE TIME!!!   */
+       if (querystring  == NULL) {
+               result = ISC_R_NOMEMORY;
+               goto cleanup;
+       }
+
+       /* output the full query string during debug so we can see */
+       /* what lame error the query has. */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
+                     "\nQuery String: %s\n", querystring);
+
+       /* attempt query up to 3 times. */
+       for (j=0; j < 3; j++) {
+               /* try to get result set */
+               sqlRes = SQLExecDirect(((odbc_db_t *) dbi->dbconn)->stmnt,
+                                      (SQLCHAR *) querystring,
+                                      (SQLINTEGER) strlen(querystring));
+
+               /* if error, reset DB connection */
+               if (!sqlOK(sqlRes)) {
+                       /* close cursor */
+                       SQLCloseCursor(((odbc_db_t *) dbi->dbconn)->stmnt);
+                       /* attempt to reconnect */
+                       result = odbc_connect((odbc_instance_t *) dbdata,
+                                             (odbc_db_t **) &(dbi->dbconn));
+                       /* check if we reconnected */
+                       if (result != ISC_R_SUCCESS)
+                               break;
+                       /* incase this is the last time through the loop */
+                       result = ISC_R_FAILURE;
+               } else {
+                       result = ISC_R_SUCCESS;
+                       /* return dbi */
+                       *r_dbi = dbi;
+                       /* result set ok, break loop */
+                       break;
+               }
+       }       /* end for loop */
+
+ cleanup:      /* it's always good to cleanup after yourself */
+
+               /* if we couldn't even allocate DBI, just return NULL */
+       if (dbi == NULL)
+               return ISC_R_FAILURE;
+
+       /* free dbi->zone string */
+       if (dbi->zone != NULL)
+               isc_mem_free(ns_g_mctx, dbi->zone);
+
+       /* free dbi->record string */
+       if (dbi->record != NULL)
+               isc_mem_free(ns_g_mctx, dbi->record);
+
+       /* free dbi->client string */
+       if (dbi->client != NULL)
+               isc_mem_free(ns_g_mctx, dbi->client);
+
+#ifdef ISC_PLATFORM_USETHREADS
+
+       /* if we are done using this dbi, release the lock */
+       if (result != ISC_R_SUCCESS)
+               isc_mutex_unlock(&dbi->instance_lock);
+
+#endif /* ISC_PLATFORM_USETHREADS */
+
+       /* release query string */
+       if (querystring  != NULL)
+               isc_mem_free(ns_g_mctx, querystring );
+
+       /* return result */
+       return result;
+
+}
+
+/*%
+ * Gets a single field from the ODBC statement.  The memory for the
+ * returned data is dynamically allocated.  If this method is successful
+ * it is the reponsibility of the caller to free the memory using
+ * isc_mem_free(ns_g_mctx, *ptr);
+ */
+
+static isc_result_t
+odbc_getField(SQLHSTMT *stmnt, SQLSMALLINT field, char **data) {
+
+       SQLINTEGER size;
+
+       REQUIRE(data != NULL && *data == NULL);
+
+       if (sqlOK(SQLColAttribute(stmnt, field, SQL_DESC_DISPLAY_SIZE,
+                                 NULL, 0, NULL, &size)) && size > 0) {
+               *data = isc_mem_allocate(ns_g_mctx, size + 1);
+               if (data != NULL) {
+                       if (sqlOK(SQLGetData(stmnt, field, SQL_C_CHAR,
+                                            *data, size + 1,&size)))
+                               return ISC_R_SUCCESS;
+                       isc_mem_free(ns_g_mctx, *data);
+               }
+       }
+       return ISC_R_FAILURE;
+}
+
+/*%
+ * Gets multiple fields from the ODBC statement.  The memory for the
+ * returned data is dynamically allocated.  If this method is successful
+ * it is the reponsibility of the caller to free the memory using
+ * isc_mem_free(ns_g_mctx, *ptr);
+ */
+
+static isc_result_t
+odbc_getManyFields(SQLHSTMT *stmnt, SQLSMALLINT startField,
+                  SQLSMALLINT endField, char **retData) {
+
+       isc_result_t result;
+       SQLINTEGER size;
+       int totSize = 0;
+       SQLSMALLINT i;
+       int j = 0;
+       char *data;
+
+       REQUIRE(retData != NULL && *retData == NULL);
+       REQUIRE(startField > 0 && startField <= endField);
+
+       /* determine how large the data is */
+       for (i=startField; i <= endField; i++)
+               if (sqlOK(SQLColAttribute(stmnt, i, SQL_DESC_DISPLAY_SIZE,
+                                         NULL, 0, NULL, &size)) && size > 0) {
+                       /* always allow for a " " (space) character */
+                       totSize += (size + 1);
+                       /* after the data item */
+               }
+
+       if (totSize < 1)
+               return ISC_R_FAILURE;
+
+       /* allow for a "\n" at the end of the string/ */
+       data = isc_mem_allocate(ns_g_mctx, ++totSize);
+       if (data == NULL)
+               return ISC_R_NOMEMORY;
+
+       result = ISC_R_FAILURE;
+
+       /* get the data and concat all fields into a large string */
+       for (i=startField; i <= endField; i++) {
+               if (sqlOK(SQLGetData(stmnt, i, SQL_C_CHAR, &(data[j]),
+                                    totSize - j, &size))) {
+                       if (size > 0) {
+                               j += size;
+                               data[j++] = ' ';
+                               data[j] = '\0';
+                               result = ISC_R_SUCCESS;
+                       }
+               } else {
+                       isc_mem_free(ns_g_mctx, data);
+                       return ISC_R_FAILURE;
+               }
+       }
+
+       if (result != ISC_R_SUCCESS) {
+               isc_mem_free(ns_g_mctx, data);
+               return result;
+       }
+
+       *retData = data;
+       return ISC_R_SUCCESS;
+
+}
+
+/*%
+ * The processing of result sets for lookup and authority are
+ * exactly the same.  So that functionality has been moved
+ * into this function to minimize code.
+ */
+
+static isc_result_t
+odbc_process_rs(dns_sdlzlookup_t *lookup, dbinstance_t *dbi)
+{
+
+
+       isc_result_t result;
+       SQLSMALLINT fields;
+       SQLHSTMT  *stmnt;
+       char *ttl_s;
+       char *type;
+       char *data;
+       char *endp;
+       int ttl;
+
+       REQUIRE(dbi != NULL);
+
+       stmnt = ((odbc_db_t *) (dbi->dbconn))->stmnt;
+
+       /* get number of columns */
+       if (!sqlOK(SQLNumResultCols(stmnt, &fields))) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Odbc driver unable to process result set");
+               result = ISC_R_FAILURE;
+               goto process_rs_cleanup;
+       }
+
+       /* get things ready for processing */
+       result = ISC_R_FAILURE;
+
+       while (sqlOK(SQLFetch(stmnt))) {
+
+               /* set to null for next pass through */
+               data = type = ttl_s = NULL;
+
+               switch(fields) {
+               case 1:
+                       /*
+                        * one column in rs, it's the data field.  use
+                        * default type of A record, and default TTL
+                        * of 86400.  attempt to get data, & tell bind
+                        * about it.
+                        */
+                       if ((result = odbc_getField(stmnt, 1,
+                                                   &data)) == ISC_R_SUCCESS) {
+                               result = dns_sdlz_putrr(lookup, "a",
+                                                       86400, data);
+                       }
+                       break;
+               case 2:
+                       /*
+                        * two columns, data field, and data type.
+                        * use default TTL of 86400.  attempt to get
+                        * DNS type & data, then tell bind about it.
+                        */
+                       if ((result = odbc_getField(stmnt, 1,
+                                                   &type)) == ISC_R_SUCCESS &&
+                           (result = odbc_getField(stmnt, 2,
+                                                   &data)) == ISC_R_SUCCESS) {
+                               result = dns_sdlz_putrr(lookup, type,
+                                                       86400, data);
+                       }
+                       break;
+               default:
+                       /*
+                        * 3 fields or more, concatenate the last ones
+                        * together.  attempt to get DNS ttl, type,
+                        * data then tell Bind about them.
+                        */
+                       if ((result = odbc_getField(stmnt, 1, &ttl_s))
+                               == ISC_R_SUCCESS &&
+                           (result = odbc_getField(stmnt, 2, &type))
+                               == ISC_R_SUCCESS &&
+                           (result = odbc_getManyFields(stmnt, 3,
+                                                        fields, &data))
+                               == ISC_R_SUCCESS) {
+                               /* try to convert ttl string to int */
+                               ttl = strtol(ttl_s, &endp, 10);
+                               /* failure converting ttl. */
+                               if (*endp != '\0' || ttl < 0) {
+                                       isc_log_write(dns_lctx,
+                                                     DNS_LOGCATEGORY_DATABASE,
+                                                     DNS_LOGMODULE_DLZ,
+                                                     ISC_LOG_ERROR,
+                                                     "Odbc driver ttl must "
+                                                     "be a postive number");
+                                       result = ISC_R_FAILURE;
+                               } else {
+                                       /*
+                                        * successful converting TTL,
+                                        * tell Bind everything
+                                        */
+                                       result = dns_sdlz_putrr(lookup, type,
+                                                               ttl, data);
+                               }
+                       } /* closes bid if () */
+               } /* closes switch(fields) */
+
+               /* clean up mem */
+               if (ttl_s != NULL)
+                       isc_mem_free(ns_g_mctx, ttl_s);
+               if (type != NULL)
+                       isc_mem_free(ns_g_mctx, type);
+               if (data != NULL)
+                       isc_mem_free(ns_g_mctx, data);
+
+               /* I sure hope we were successful */
+               if (result != ISC_R_SUCCESS) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                     "dns_sdlz_putrr returned error. "
+                                     "Error code was: %s",
+                                     isc_result_totext(result));
+                       result = ISC_R_FAILURE;
+                       goto process_rs_cleanup;
+               }
+       } /* closes while loop */
+
+ process_rs_cleanup:
+
+       /* close cursor */
+       SQLCloseCursor(((odbc_db_t *) (dbi->dbconn))->stmnt);
+
+#ifdef ISC_PLATFORM_USETHREADS
+
+       /* free lock on dbi so someone else can use it. */
+       isc_mutex_unlock(&dbi->instance_lock);
+
+#endif
+
+       return result;
+}
+
+/*
+ * SDLZ interface methods
+ */
+
+/*% determine if the zone is supported by (in) the database */
+
+static isc_result_t
+odbc_findzone(void *driverarg, void *dbdata, const char *name)
+{
+
+       isc_result_t result;
+       dbinstance_t *dbi = NULL;
+
+       UNUSED(driverarg);
+
+       /* run the query and get the result set from the database. */
+       /* if result != ISC_R_SUCCESS cursor and mutex already cleaned up. */
+       /* so we don't have to do it here. */
+       result = odbc_get_resultset(name, NULL, NULL, FINDZONE, dbdata, &dbi);
+
+       /* Check that we got a result set with data */
+       if (result == ISC_R_SUCCESS &&
+           !sqlOK(SQLFetch(((odbc_db_t *) (dbi->dbconn))->stmnt))) {
+               result = ISC_R_NOTFOUND;
+       }
+
+       if (dbi != NULL) {
+               /* get rid of result set, we are done with it. */
+               SQLCloseCursor(((odbc_db_t *) (dbi->dbconn))->stmnt);
+
+#ifdef ISC_PLATFORM_USETHREADS
+
+               /* free lock on dbi so someone else can use it. */
+               isc_mutex_unlock(&dbi->instance_lock);
+#endif
+       }
+
+       return result;
+}
+
+/*% Determine if the client is allowed to perform a zone transfer */
+static isc_result_t
+odbc_allowzonexfr(void *driverarg, void *dbdata, const char *name,
+                 const char *client)
+{
+       isc_result_t result;
+       dbinstance_t *dbi = NULL;
+
+       UNUSED(driverarg);
+
+       /* first check if the zone is supported by the database. */
+       result = odbc_findzone(driverarg, dbdata, name);
+       if (result != ISC_R_SUCCESS)
+               return (ISC_R_NOTFOUND);
+
+       /*
+        * if we get to this point we know the zone is supported by
+        * the database.  the only questions now are is the zone
+        * transfer is allowed for this client and did the config file
+        * have an allow zone xfr query
+        *
+        * Run our query, and get a result set from the database.  if
+        * result != ISC_R_SUCCESS cursor and mutex already cleaned
+        * up, so we don't have to do it here.
+        */
+       result = odbc_get_resultset(name, NULL, client, ALLOWXFR,
+                                   dbdata, &dbi);
+
+       /* if we get "not implemented", send it along. */
+       if (result == ISC_R_NOTIMPLEMENTED)
+               return result;
+
+       /* Check that we got a result set with data */
+       if (result == ISC_R_SUCCESS &&
+           !sqlOK(SQLFetch(((odbc_db_t *) (dbi->dbconn))->stmnt))) {
+               result = ISC_R_NOPERM;
+       }
+
+       if (dbi != NULL) {
+               /* get rid of result set, we are done with it. */
+               SQLCloseCursor(((odbc_db_t *) (dbi->dbconn))->stmnt);
+
+#ifdef ISC_PLATFORM_USETHREADS
+
+               /* free lock on dbi so someone else can use it. */
+               isc_mutex_unlock(&dbi->instance_lock);
+#endif
+
+       }
+
+       return result;
+}
+
+/*%
+ * If the client is allowed to perform a zone transfer, the next order of
+ * business is to get all the nodes in the zone, so bind can respond to the
+ * query.
+ */
+
+static isc_result_t
+odbc_allnodes(const char *zone, void *driverarg, void *dbdata,
+             dns_sdlzallnodes_t *allnodes)
+{
+
+       isc_result_t result;
+       dbinstance_t *dbi = NULL;
+       SQLHSTMT  *stmnt;
+       SQLSMALLINT fields;
+       char *data;
+       char *type;
+       char *ttl_s;
+       int ttl;
+       char *host;
+       char *endp;
+
+       UNUSED(driverarg);
+
+       /* run the query and get the result set from the database. */
+       result = odbc_get_resultset(zone, NULL, NULL, ALLNODES, dbdata, &dbi);
+
+       /* if we get "not implemented", send it along */
+       if (result == ISC_R_NOTIMPLEMENTED)
+               return result;
+
+       /* if we didn't get a result set, log an err msg. */
+       if (result != ISC_R_SUCCESS) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Odbc driver unable to return "
+                             "result set for all nodes query");
+               return (ISC_R_FAILURE);
+       }
+
+       stmnt = ((odbc_db_t *) (dbi->dbconn))->stmnt;
+
+       /* get number of columns */
+       if (!sqlOK(SQLNumResultCols(stmnt, &fields))) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Odbc driver unable to process result set");
+               result = ISC_R_FAILURE;
+               goto allnodes_cleanup;
+       }
+
+       if (fields < 4) {       /* gotta have at least 4 columns */
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Odbc driver too few fields returned by "
+                             "all nodes query");
+               result = ISC_R_FAILURE;
+               goto allnodes_cleanup;
+       }
+
+       /* get things ready for processing */
+       result = ISC_R_FAILURE;
+
+       while (sqlOK(SQLFetch(stmnt))) {
+
+               /* set to null for next pass through */
+               data = host = type = ttl_s = NULL;
+
+               /*
+                * attempt to get DNS ttl, type, host, data then tell
+                * Bind about them
+                */
+               if ((result = odbc_getField(stmnt, 1,
+                                           &ttl_s)) == ISC_R_SUCCESS &&
+                   (result = odbc_getField(stmnt, 2,
+                                           &type)) == ISC_R_SUCCESS &&
+                   (result = odbc_getField(stmnt, 3,
+                                           &host)) == ISC_R_SUCCESS &&
+                   (result = odbc_getManyFields(stmnt, 4, fields,
+                                                &data)) == ISC_R_SUCCESS) {
+                       /* convert ttl string to int */
+                       ttl = strtol(ttl_s, &endp, 10);
+                       /* failure converting ttl. */
+                       if (*endp != '\0' || ttl < 0) {
+                               isc_log_write(dns_lctx,
+                                             DNS_LOGCATEGORY_DATABASE,
+                                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                             "Odbc driver ttl must be "
+                                             "a postive number");
+                               result = ISC_R_FAILURE;
+                       } else {
+                               /* successful converting TTL, tell Bind  */
+                               result = dns_sdlz_putnamedrr(allnodes, host,
+                                                            type, ttl, data);
+                       }
+               } /* closes big if () */
+
+               /* clean up mem */
+               if (ttl_s != NULL)
+                       isc_mem_free(ns_g_mctx, ttl_s);
+               if (type != NULL)
+                       isc_mem_free(ns_g_mctx, type);
+               if (host != NULL)
+                       isc_mem_free(ns_g_mctx, host);
+               if (data != NULL)
+                       isc_mem_free(ns_g_mctx, data);
+
+               /* if we weren't successful, log err msg */
+               if (result != ISC_R_SUCCESS) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                     "dns_sdlz_putnamedrr returned error. "
+                                     "Error code was: %s",
+                                     isc_result_totext(result));
+                       result = ISC_R_FAILURE;
+                       goto allnodes_cleanup;
+               }
+       } /* closes while loop */
+
+ allnodes_cleanup:
+
+       /* close cursor */
+       SQLCloseCursor(((odbc_db_t *) (dbi->dbconn))->stmnt);
+
+#ifdef ISC_PLATFORM_USETHREADS
+
+       /* free lock on dbi so someone else can use it. */
+       isc_mutex_unlock(&dbi->instance_lock);
+
+#endif
+
+       return result;
+}
+
+/*%
+ * if the lookup function does not return SOA or NS records for the zone,
+ * use this function to get that information for Bind.
+ */
+
+static isc_result_t
+odbc_authority(const char *zone, void *driverarg, void *dbdata,
+              dns_sdlzlookup_t *lookup)
+{
+       isc_result_t result;
+       dbinstance_t *dbi = NULL;
+
+       UNUSED(driverarg);
+
+       /* run the query and get the result set from the database. */
+       result = odbc_get_resultset(zone, NULL, NULL, AUTHORITY, dbdata, &dbi);
+       /* if we get "not implemented", send it along */
+       if (result == ISC_R_NOTIMPLEMENTED)
+               return result;
+       /* if we didn't get a result set, log an err msg. */
+       if (result != ISC_R_SUCCESS) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Odbc driver unable to return "
+                             "result set for authority query");
+               return (ISC_R_FAILURE);
+       }
+       /* lookup and authority result sets are processed in the same manner */
+       /* odbc_process_rs does the job for both functions. */
+       return odbc_process_rs(lookup, dbi);
+}
+
+/*% if zone is supported, lookup up a (or multiple) record(s) in it */
+
+static isc_result_t
+odbc_lookup(const char *zone, const char *name, void *driverarg,
+           void *dbdata, dns_sdlzlookup_t *lookup)
+{
+       isc_result_t result;
+       dbinstance_t *dbi = NULL;
+
+       UNUSED(driverarg);
+
+       /* run the query and get the result set from the database. */
+       result = odbc_get_resultset(zone, name, NULL, LOOKUP, dbdata, &dbi);
+       /* if we didn't get a result set, log an err msg. */
+       if (result != ISC_R_SUCCESS) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Odbc driver unable to return "
+                             "result set for lookup query");
+               return (ISC_R_FAILURE);
+       }
+       /* lookup and authority result sets are processed in the same manner */
+       /* odbc_process_rs does the job for both functions. */
+       return odbc_process_rs(lookup, dbi);
+}
+
+/*%
+ * create an instance of the driver.  Remember, only 1 copy of the driver's
+ * code is ever loaded, the driver has to remember which context it's
+ * operating in.  This is done via use of the dbdata arguement which is
+ * passed into all query functions.
+ */
+static isc_result_t
+odbc_create(const char *dlzname, unsigned int argc, char *argv[],
+           void *driverarg, void **dbdata)
+{
+       isc_result_t result;
+       odbc_instance_t *odbc_inst = NULL;
+       dbinstance_t *db = NULL;
+       SQLRETURN sqlRes;
+
+#ifdef ISC_PLATFORM_USETHREADS
+       /* if multi-threaded, we need a few extra variables. */
+       int dbcount;
+       int i;
+       char *endp;
+
+#endif /* ISC_PLATFORM_USETHREADS */
+
+       UNUSED(dlzname);
+       UNUSED(driverarg);
+
+#ifdef ISC_PLATFORM_USETHREADS
+       /* if debugging, let user know we are multithreaded. */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
+                     "Odbc driver running multithreaded");
+#else /* ISC_PLATFORM_USETHREADS */
+       /* if debugging, let user know we are single threaded. */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
+                     "Odbc driver running single threaded");
+#endif /* ISC_PLATFORM_USETHREADS */
+
+       /* verify we have at least 5 arg's passed to the driver */
+       if (argc < 5) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Odbc driver requires at least "
+                             "4 command line args.");
+               return (ISC_R_FAILURE);
+       }
+
+       /* no more than 8 arg's should be passed to the driver */
+       if (argc > 8) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Odbc driver cannot accept more than "
+                             "7 command line args.");
+               return (ISC_R_FAILURE);
+       }
+
+       /* multithreaded build can have multiple DB connections */
+#ifdef ISC_PLATFORM_USETHREADS
+
+       /* check how many db connections we should create */
+       dbcount = strtol(argv[1], &endp, 10);
+       if (*endp != '\0' || dbcount < 0) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Odbc driver database connection count "
+                             "must be positive.");
+               return (ISC_R_FAILURE);
+       }
+
+#endif /* ISC_PLATFORM_USETHREADS */
+
+       /* allocate memory for odbc instance */
+       odbc_inst = isc_mem_get(ns_g_mctx, sizeof(odbc_instance_t));
+       if (odbc_inst == NULL)
+               return (ISC_R_NOMEMORY);
+       memset(odbc_inst, 0, sizeof(odbc_instance_t));
+
+       /* parse connection string and get paramters. */
+
+       /* get odbc database dsn - required */
+       odbc_inst->dsn = (SQLCHAR *) getParameterValue(argv[2],
+                                                      "dsn=");
+       if (odbc_inst->dsn == NULL) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "odbc driver requires a dns parameter.");
+               result = ISC_R_FAILURE;
+               goto cleanup;
+       }
+       /* get odbc database username */
+       /* if no username was passed, set odbc_inst.user = NULL; */
+       odbc_inst->user = (SQLCHAR *) getParameterValue(argv[2],
+                                                       "user=");
+
+       /* get odbc database password */
+       /* if no password was passed, set odbc_inst.pass = NULL; */
+       odbc_inst->pass = (SQLCHAR *) getParameterValue(argv[2], "pass=");
+
+       /* create odbc environment & set environment to ODBC V3 */
+       if (odbc_inst->sql_env == NULL) {
+               /* create environment handle */
+               sqlRes = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE,
+                                       &(odbc_inst->sql_env));
+               if (!sqlOK(sqlRes)) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
+                                     "Odbc driver unable to allocate memory");
+                       result = ISC_R_NOMEMORY;
+                       goto cleanup;
+               }
+               /*set ODBC version = 3 */
+               sqlRes = SQLSetEnvAttr(odbc_inst->sql_env,
+                                      SQL_ATTR_ODBC_VERSION,
+                                      (void *) SQL_OV_ODBC3, 0);
+               if (!sqlOK(sqlRes)) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
+                                     "Unable to configure ODBC environment");
+                       result = ISC_R_NOMEMORY;
+                       goto cleanup;
+               }
+       }
+
+#ifdef ISC_PLATFORM_USETHREADS
+
+       /* allocate memory for database connection list */
+       odbc_inst->db = isc_mem_get(ns_g_mctx, sizeof(db_list_t));
+       if (odbc_inst->db == NULL) {
+               result = ISC_R_NOMEMORY;
+               goto cleanup;
+       }
+
+
+       /* initialize DB connection list */
+       ISC_LIST_INIT(*odbc_inst->db);
+
+       /* create the appropriate number of database instances (DBI) */
+       /* append each new DBI to the end of the list */
+       for (i=0; i < dbcount; i++) {
+
+#endif /* ISC_PLATFORM_USETHREADS */
+
+               /* how many queries were passed in from config file? */
+               switch(argc) {
+               case 5:
+                       result = build_sqldbinstance(ns_g_mctx, NULL, NULL,
+                                                    NULL, argv[3], argv[4],
+                                                    NULL, &db);
+                       break;
+               case 6:
+                       result = build_sqldbinstance(ns_g_mctx, NULL, NULL,
+                                                    argv[5], argv[3], argv[4],
+                                                    NULL, &db);
+                       break;
+               case 7:
+                       result = build_sqldbinstance(ns_g_mctx, argv[6], NULL,
+                                                    argv[5], argv[3], argv[4],
+                                                    NULL, &db);
+                       break;
+               case 8:
+                       result = build_sqldbinstance(ns_g_mctx, argv[6],
+                                                    argv[7], argv[5], argv[3],
+                                                    argv[4], NULL, &db);
+                       break;
+               default:
+                       /* not really needed, should shut up compiler. */
+                       result = ISC_R_FAILURE;
+               }
+
+               /* unsuccessful?, log err msg and cleanup. */
+               if (result != ISC_R_SUCCESS) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                     "Odbc driver could not create "
+                                     "database instance object.");
+                       goto cleanup;
+               }
+
+#ifdef ISC_PLATFORM_USETHREADS
+
+               /* when multithreaded, build a list of DBI's */
+               ISC_LINK_INIT(db, link);
+               ISC_LIST_APPEND(*odbc_inst->db, db, link);
+
+#endif
+
+               result = odbc_connect(odbc_inst, (odbc_db_t **) &(db->dbconn));
+
+               if (result != ISC_R_SUCCESS) {
+
+#ifdef ISC_PLATFORM_USETHREADS
+
+                       /*
+                        * if multi threaded, let user know which
+                        * connection failed.  user could be
+                        * attempting to create 10 db connections and
+                        * for some reason the db backend only allows
+                        * 9.
+                        */
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                     "Odbc driver failed to create database "
+                                     "connection number %u after 3 attempts",
+                                     i+1);
+#else
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                     "Odbc driver failed to create database "
+                                     "connection after 3 attempts");
+#endif
+                       goto cleanup;
+               }
+
+#ifdef ISC_PLATFORM_USETHREADS
+
+               /* set DB = null for next loop through. */
+               db = NULL;
+
+       }       /* end for loop */
+
+#else
+       /* tell odbc_inst about the db connection we just created. */
+       odbc_inst->db = db;
+
+#endif
+
+       /* set dbdata to the odbc_instance we created. */
+       *dbdata = odbc_inst;
+
+       /* hey, we got through all of that ok, return success. */
+       return(ISC_R_SUCCESS);
+
+ cleanup:
+
+       destroy_odbc_instance(odbc_inst);
+
+       return result;
+}
+
+/*%
+ * destroy an instance of the driver.  Remember, only 1 copy of the driver's
+ * code is ever loaded, the driver has to remember which context it's
+ * operating in.  This is done via use of the dbdata arguement.
+ * so we really only need to clean it up since we are not using driverarg.
+ */
+
+static void
+odbc_destroy(void *driverarg, void *dbdata)
+{
+       UNUSED(driverarg);
+
+       destroy_odbc_instance((odbc_instance_t *) dbdata);
+}
+
+
+/* pointers to all our runtime methods. */
+/* this is used during driver registration */
+/* i.e. in dlz_odbc_init below. */
+static dns_sdlzmethods_t dlz_odbc_methods = {
+       odbc_create,
+       odbc_destroy,
+       odbc_findzone,
+       odbc_lookup,
+       odbc_authority,
+       odbc_allnodes,
+       odbc_allowzonexfr
+};
+
+/*%
+ * Wrapper around dns_sdlzregister().
+ */
+isc_result_t
+dlz_odbc_init(void) {
+       isc_result_t result;
+
+       /*
+        * Write debugging message to log
+        */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                     "Registering DLZ odbc driver.");
+
+       /*
+        * Driver is always threadsafe.  When multithreaded all
+        * functions use multithreaded code.  When not multithreaded,
+        * all functions can only be entered once, but only 1 thread
+        * of operation is available in Bind.  So everything is still
+        * threadsafe.
+        */
+       result = dns_sdlzregister("odbc", &dlz_odbc_methods, NULL,
+                                 DNS_SDLZFLAG_RELATIVEOWNER |
+                                 DNS_SDLZFLAG_RELATIVERDATA |
+                                 DNS_SDLZFLAG_THREADSAFE,
+                                 ns_g_mctx, &dlz_odbc);
+       /* if we can't register the driver, there are big problems. */
+       if (result != ISC_R_SUCCESS) {
+               UNEXPECTED_ERROR(__FILE__, __LINE__,
+                                "dns_sdlzregister() failed: %s",
+                                isc_result_totext(result));
+               result = ISC_R_UNEXPECTED;
+       }
+
+
+       return result;
+}
+
+/*%
+ * Wrapper around dns_sdlzunregister().
+ */
+void
+dlz_odbc_clear(void) {
+
+       /*
+        * Write debugging message to log
+        */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                     "Unregistering DLZ odbc driver.");
+
+       /* unregister the driver. */
+       if (dlz_odbc != NULL)
+               dns_sdlzunregister(&dlz_odbc);
+}
+
+#endif
diff --git a/contrib/dlz/drivers/dlz_postgres_driver.c b/contrib/dlz/drivers/dlz_postgres_driver.c
new file mode 100644 (file)
index 0000000..7f3b292
--- /dev/null
@@ -0,0 +1,1342 @@
+/*
+ * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
+ *
+ * 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 STICHTING NLNET
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * STICHTING NLNET 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 development of Dynamically Loadable Zones (DLZ) for Bind 9 was
+ * conceived and contributed by Rob Butler.
+ *
+ * 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 ROB BUTLER
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * ROB BUTLER 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.
+ */
+
+/*
+ * Copyright (C) 1999-2001  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.
+ */
+
+#ifdef DLZ_POSTGRES
+
+#include <config.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <dns/log.h>
+#include <dns/sdlz.h>
+#include <dns/result.h>
+
+#include <isc/mem.h>
+#include <isc/platform.h>
+#include <isc/print.h>
+#include <isc/result.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <named/globals.h>
+
+#include <dlz/sdlz_helper.h>
+#include <dlz/dlz_postgres_driver.h>
+
+/* temporarily include time. */
+#include <time.h>
+
+#include <libpq-fe.h>
+
+static dns_sdlzimplementation_t *dlz_postgres = NULL;
+
+#define dbc_search_limit 30
+#define ALLNODES 1
+#define ALLOWXFR 2
+#define AUTHORITY 3
+#define FINDZONE 4
+#define LOOKUP 5
+
+/*
+ * Private methods
+ */
+
+/* ---------------
+ * Escaping arbitrary strings to get valid SQL strings/identifiers.
+ *
+ * Replaces "\\" with "\\\\" and "'" with "''".
+ * length is the length of the buffer pointed to by
+ * from.  The buffer at to must be at least 2*length + 1 characters
+ * long.  A terminating NUL character is written.
+ *
+ * NOTICE!!!
+ * This function was borrowed directly from PostgreSQL's libpq.
+ * The function was originally called PQescapeString and renamed
+ * to postgres_makesafe to avoid a naming collision.
+ * PQescapeString is a new function made available in Postgres 7.2.
+ * For some reason the function is not properly exported on Win32
+ * builds making the function unavailable on Windows.  Also, since
+ * this function is new it would require building this driver with
+ * the libpq 7.2.  By borrowing this function the Windows problem
+ * is solved, and the dependence on libpq 7.2 is removed.  Libpq is
+ * still required of course, but an older version should work now too.
+ *
+ * The copyright statements from the original file containing this
+ * function are included below:
+ * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1994, Regents of the University of California
+ * ---------------
+ */
+
+static size_t
+postgres_makesafe(char *to, const char *from, size_t length)
+{
+       const char *source = from;
+       char       *target = to;
+       unsigned int remaining = length;
+
+       while (remaining > 0)
+       {
+               switch (*source)
+               {
+               case '\\':
+                       *target = '\\';
+                       target++;
+                       *target = '\\';
+                       /* target and remaining are updated below. */
+                       break;
+
+               case '\'':
+                       *target = '\'';
+                       target++;
+                       *target = '\'';
+                       /* target and remaining are updated below. */
+                       break;
+
+               default:
+                       *target = *source;
+                       /* target and remaining are updated below. */
+               }
+               source++;
+               target++;
+               remaining--;
+       }
+
+       /* Write the terminating NUL character. */
+       *target = '\0';
+
+       return target - to;
+}
+
+#ifdef ISC_PLATFORM_USETHREADS
+
+/*%
+ * Properly cleans up a list of database instances.
+ * This function is only used when the driver is compiled for
+ * multithreaded operation.
+ */
+static void
+postgres_destroy_dblist(db_list_t *dblist)
+{
+
+       dbinstance_t *ndbi = NULL;
+       dbinstance_t *dbi = NULL;
+
+       /* get the first DBI in the list */
+       ndbi = ISC_LIST_HEAD(*dblist);
+
+       /* loop through the list */
+       while (ndbi != NULL) {
+               dbi = ndbi;
+               /* get the next DBI in the list */
+               ndbi = ISC_LIST_NEXT(dbi, link);
+               /* release DB connection */
+               if (dbi->dbconn != NULL)
+                       PQfinish((PGconn *) dbi->dbconn);
+               /* release all memory that comprised a DBI */
+               destroy_sqldbinstance(dbi);
+       }
+       /* release memory for the list structure */
+       isc_mem_put(ns_g_mctx, dblist, sizeof(db_list_t));
+}
+
+/*%
+ * Loops through the list of DB instances, attempting to lock
+ * on the mutex.  If successful, the DBI is reserved for use
+ * and the thread can perform queries against the database.
+ * If the lock fails, the next one in the list is tried.
+ * looping continues until a lock is obtained, or until
+ * the list has been searched dbc_search_limit times.
+ * This function is only used when the driver is compiled for
+ * multithreaded operation.
+ */
+
+static dbinstance_t *
+postgres_find_avail_conn(db_list_t *dblist)
+{
+       dbinstance_t *dbi = NULL;
+       dbinstance_t *head;
+       int count = 0;
+
+       /* get top of list */
+       head = dbi = ISC_LIST_HEAD(*dblist);
+
+       /* loop through list */
+       while (count < dbc_search_limit) {
+               /* try to lock on the mutex */
+               if (isc_mutex_trylock(&dbi->instance_lock) == ISC_R_SUCCESS)
+                       return dbi; /* success, return the DBI for use. */
+
+               /* not successful, keep trying */
+               dbi = ISC_LIST_NEXT(dbi, link);
+
+               /* check to see if we have gone to the top of the list. */
+               if (dbi == NULL) {
+                       count++;
+                       dbi = head;
+               }
+       }
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
+                     "Postgres driver unable to find available connection "
+                     "after searching %d times",
+                     count);
+       return NULL;
+}
+
+#endif /* ISC_PLATFORM_USETHREADS */
+
+/*%
+ * Allocates memory for a new string, and then constructs the new
+ * string by "escaping" the input string.  The new string is
+ * safe to be used in queries.  This is necessary because we cannot
+ * be sure of what types of strings are passed to us, and we don't
+ * want special characters in the string causing problems.
+ */
+
+static char *
+postgres_escape_string(const char *instr) {
+
+       char *outstr;
+       unsigned int len;
+
+       if (instr == NULL)
+               return NULL;
+
+       len = strlen(instr);
+
+       outstr = isc_mem_allocate(ns_g_mctx ,(2 * len * sizeof(char)) + 1);
+       if (outstr == NULL)
+               return NULL;
+
+       postgres_makesafe(outstr, instr, len);
+       /* PQescapeString(outstr, instr, len); */
+
+       return outstr;
+}
+
+/*%
+ * This function is the real core of the driver.   Zone, record
+ * and client strings are passed in (or NULL is passed if the
+ * string is not available).  The type of query we want to run
+ * is indicated by the query flag, and the dbdata object is passed
+ * passed in to.  dbdata really holds either:
+ *             1) a list of database instances (in multithreaded mode) OR
+ *             2) a single database instance (in single threaded mode)
+ * The function will construct the query and obtain an available
+ * database instance (DBI).  It will then run the query and hopefully
+ * obtain a result set.  Postgres is nice, in that once the result
+ * set is returned, we can make the db connection available for another
+ * thread to use, while this thread continues on.  So, the DBI is made
+ * available ASAP by unlocking the instance_lock after we have cleaned
+ * it up properly.
+ */
+static isc_result_t
+postgres_get_resultset(const char *zone, const char *record,
+                      const char *client, unsigned int query,
+                      void *dbdata, PGresult **rs)
+{
+       isc_result_t result;
+       dbinstance_t *dbi = NULL;
+       char *querystring = NULL;
+       unsigned int i = 0;
+       unsigned int j = 0;
+
+       /* temporarily get a unique thread # */
+       unsigned int dlz_thread_num = 1+(int) (1000.0*rand()/(RAND_MAX+1.0));
+
+       REQUIRE(*rs == NULL);
+
+       /* temporary logging message */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                     "%d Getting DBI", dlz_thread_num);
+
+       /* get db instance / connection */
+#ifdef ISC_PLATFORM_USETHREADS
+
+       /* find an available DBI from the list */
+       dbi = postgres_find_avail_conn((db_list_t *) dbdata);
+
+#else /* ISC_PLATFORM_USETHREADS */
+
+       /*
+        * only 1 DBI - no need to lock instance lock either
+        * only 1 thread in the whole process, no possible contention.
+        */
+       dbi =  (dbinstance_t *) dbdata;
+
+#endif /* ISC_PLATFORM_USETHREADS */
+
+       /* temporary logging message */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                     "%d Got DBI - checking query", dlz_thread_num);
+
+       /* if DBI is null, can't do anything else */
+       if (dbi == NULL) {
+               result = ISC_R_FAILURE;
+               goto cleanup;
+       }
+
+       /* what type of query are we going to run? */
+       switch(query) {
+       case ALLNODES:
+               /*
+                * if the query was not passed in from the config file
+                * then we can't run it.  return not_implemented, so
+                * it's like the code for that operation was never
+                * built into the driver.... AHHH flexibility!!!
+                */
+               if (dbi->allnodes_q == NULL) {
+                       result = ISC_R_NOTIMPLEMENTED;
+                       goto cleanup;
+               }
+               break;
+       case ALLOWXFR:
+               /* same as comments as ALLNODES */
+               if (dbi->allowxfr_q == NULL) {
+                       result = ISC_R_NOTIMPLEMENTED;
+                       goto cleanup;
+               }
+               break;
+       case AUTHORITY:
+               /* same as comments as ALLNODES */
+               if (dbi->authority_q == NULL) {
+                       result = ISC_R_NOTIMPLEMENTED;
+                       goto cleanup;
+               }
+               break;
+       case FINDZONE:
+               /* this is required.  It's the whole point of DLZ! */
+               if (dbi->findzone_q == NULL) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                                     "No query specified for findzone.  "
+                                     "Findzone requires a query");
+                       result = ISC_R_FAILURE;
+                       goto cleanup;
+               }
+               break;
+       case LOOKUP:
+               /* this is required.  It's also a major point of DLZ! */
+               if (dbi->lookup_q == NULL) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                                     "No query specified for lookup.  "
+                                     "Lookup requires a query");
+                       result = ISC_R_FAILURE;
+                       goto cleanup;
+               }
+               break;
+       default:
+               /*
+                * this should never happen.  If it does, the code is
+                * screwed up!
+                */
+               UNEXPECTED_ERROR(__FILE__, __LINE__,
+                                "Incorrect query flag passed to "
+                                "postgres_get_resultset");
+               result = ISC_R_UNEXPECTED;
+               goto cleanup;
+       }
+
+       /* temporary logging message */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                     "%d checked query", dlz_thread_num);
+
+       /*
+        * was a zone string passed?  If so, make it safe for use in
+        * queries.
+        */
+       if (zone != NULL) {
+               dbi->zone = postgres_escape_string(zone);
+               if (dbi->zone == NULL) {
+                       result = ISC_R_NOMEMORY;
+                       goto cleanup;
+               }
+       } else {        /* no string passed, set the string pointer to NULL */
+               dbi->zone = NULL;
+       }
+
+       /* temporary logging message */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                     "%d did zone", dlz_thread_num);
+
+       /*
+        * was a record string passed?  If so, make it safe for use in
+        * queries.
+        */
+       if (record != NULL) {
+               dbi->record = postgres_escape_string(record);
+               if (dbi->record == NULL) {
+                       result = ISC_R_NOMEMORY;
+                       goto cleanup;
+               }
+       } else {        /* no string passed, set the string pointer to NULL */
+               dbi->record = NULL;
+       }
+
+
+       /* temporary logging message */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                     "%d did record", dlz_thread_num);
+
+       /*
+        * was a client string passed?  If so, make it safe for use in
+        * queries.
+        */
+       if (client != NULL) {
+               dbi->client = postgres_escape_string(client);
+               if (dbi->client == NULL) {
+                       result = ISC_R_NOMEMORY;
+                       goto cleanup;
+               }
+       } else {        /* no string passed, set the string pointer to NULL */
+               dbi->client = NULL;
+       }
+
+       /* temporary logging message */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+       DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                     "%d did client", dlz_thread_num);
+
+       /*
+        * what type of query are we going to run?
+        * this time we build the actual query to run.
+        */
+       switch(query) {
+       case ALLNODES:
+               querystring = build_querystring(ns_g_mctx, dbi->allnodes_q);
+               break;
+       case ALLOWXFR:
+               querystring = build_querystring(ns_g_mctx, dbi->allowxfr_q);
+               break;
+       case AUTHORITY:
+               querystring = build_querystring(ns_g_mctx, dbi->authority_q);
+               break;
+       case FINDZONE:
+               querystring = build_querystring(ns_g_mctx, dbi->findzone_q);
+               break;
+       case LOOKUP:
+               querystring = build_querystring(ns_g_mctx, dbi->lookup_q);
+               break;
+       default:
+               /*
+                * this should never happen.  If it does, the code is
+                * screwed up!
+                */
+               UNEXPECTED_ERROR(__FILE__, __LINE__,
+                                "Incorrect query flag passed to "
+                                "postgres_get_resultset");
+               result = ISC_R_UNEXPECTED;
+               goto cleanup;
+       }
+
+       /* temporary logging message */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                     "%d built query", dlz_thread_num);
+
+       /* if the querystring is null, Bummer, outta RAM.  UPGRADE TIME!!!   */
+       if (querystring  == NULL) {
+               result = ISC_R_NOMEMORY;
+               goto cleanup;
+       }
+
+       /* temporary logging message */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                     "%d query is '%s'", dlz_thread_num, querystring);
+
+       /*
+        * output the full query string during debug so we can see
+        * what lame error the query has.
+        */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
+                     "\nQuery String: %s\n", querystring);
+
+       /* attempt query up to 3 times. */
+       for (j=0; j < 3; j++) {
+               /* temporary logging message */
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "%d executing query for %d time",
+                             dlz_thread_num, j);
+               /* try to get result set */
+               *rs = PQexec((PGconn *)dbi->dbconn, querystring );
+               result = ISC_R_SUCCESS;
+               /*
+                * if result set is null, reset DB connection, max 3
+                * attempts.
+                */
+               for (i=0; *rs == NULL && i < 3; i++) {
+                       /* temporary logging message */
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                     "%d resetting connection",
+                                     dlz_thread_num);
+                       result = ISC_R_FAILURE;
+                       PQreset((PGconn *) dbi->dbconn);
+                       /* connection ok, break inner loop */
+                       if (PQstatus((PGconn *) dbi->dbconn) == CONNECTION_OK)
+                               break;
+               }
+               /* result set ok, break outter loop */
+               if (PQresultStatus(*rs) == PGRES_TUPLES_OK) {
+                       /* temporary logging message */
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                     "%d rs ok", dlz_thread_num);
+                       break;
+               } else {
+                       /* we got a result set object, but it's not right. */
+                       /* temporary logging message */
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                     "%d clearing rs", dlz_thread_num);
+                       PQclear(*rs);   /* get rid of it */
+                       /* in case this was the last attempt */
+                       result = ISC_R_FAILURE;
+               }
+       }
+
+ cleanup:
+       /* it's always good to cleanup after yourself */
+
+       /* temporary logging message */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                     "%d cleaning up", dlz_thread_num);
+
+       /* if we couldn't even allocate DBI, just return NULL */
+       if (dbi == NULL)
+               return ISC_R_FAILURE;
+
+       /* free dbi->zone string */
+       if (dbi->zone != NULL)
+               isc_mem_free(ns_g_mctx, dbi->zone);
+
+       /* free dbi->record string */
+       if (dbi->record != NULL)
+               isc_mem_free(ns_g_mctx, dbi->record);
+
+       /* free dbi->client string */
+       if (dbi->client != NULL)
+               isc_mem_free(ns_g_mctx, dbi->client);
+
+#ifdef ISC_PLATFORM_USETHREADS
+
+       /* temporary logging message */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                     "%d unlocking mutex", dlz_thread_num);
+
+       /* release the lock so another thread can use this dbi */
+       isc_mutex_unlock(&dbi->instance_lock);
+
+#endif /* ISC_PLATFORM_USETHREADS */
+
+       /* release query string */
+       if (querystring  != NULL)
+               isc_mem_free(ns_g_mctx, querystring );
+
+       /* temporary logging message */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                     "%d returning", dlz_thread_num);
+
+       /* return result */
+       return result;
+}
+
+/*%
+ * The processing of result sets for lookup and authority are
+ * exactly the same.  So that functionality has been moved
+ * into this function to minimize code.
+ */
+
+static isc_result_t
+postgres_process_rs(dns_sdlzlookup_t *lookup, PGresult *rs)
+{
+       isc_result_t result;
+       unsigned int i;
+       unsigned int rows;
+       unsigned int fields;
+       unsigned int j;
+       unsigned int len;
+       char *tmpString;
+       char *endp;
+       int ttl;
+
+       rows = PQntuples(rs);   /* how many rows in result set */
+       fields = PQnfields(rs); /* how many columns in result set */
+       for (i=0; i < rows; i++) {
+               switch(fields) {
+               case 1:
+                       /*
+                        * one column in rs, it's the data field.  use
+                        * default type of A record, and default TTL
+                        * of 86400
+                        */
+                       result = dns_sdlz_putrr(lookup, "a", 86400,
+                                               PQgetvalue(rs, i, 0));
+                       break;
+               case 2:
+                       /* two columns, data field, and data type.
+                        * use default TTL of 86400.
+                        */
+                       result = dns_sdlz_putrr(lookup, PQgetvalue(rs, i, 0),
+                                               86400, PQgetvalue(rs, i, 1));
+                       break;
+               case 3:
+                       /* three columns, all data no defaults.
+                        * convert text to int, make sure it worked
+                        * right.
+                        */
+                       ttl = strtol(PQgetvalue(rs, i, 0), &endp, 10);
+                       if (*endp != '\0' || ttl < 0) {
+                               isc_log_write(dns_lctx,
+                                             DNS_LOGCATEGORY_DATABASE,
+                                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                             "Postgres driver ttl must be "
+                                             "a positive number");
+                       }
+                       result = dns_sdlz_putrr(lookup, PQgetvalue(rs, i, 1),
+                                               ttl, PQgetvalue(rs, i, 2));
+                       break;
+               default:
+                       /*
+                        * more than 3 fields, concatenate the last
+                        * ones together.  figure out how long to make
+                        * string
+                        */
+                       for (j=2, len=0; j < fields; j++) {
+                               len += strlen(PQgetvalue(rs, i, j)) + 1;
+                       }
+                       /*
+                        * allocate string memory, allow for NULL to
+                        * term string
+                        */
+                       tmpString = isc_mem_allocate(ns_g_mctx, len + 1);
+                       if (tmpString == NULL) {
+                               /* major bummer, need more ram */
+                               isc_log_write(dns_lctx,
+                                             DNS_LOGCATEGORY_DATABASE,
+                                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                             "Postgres driver unable to "
+                                             "allocate memory for "
+                                             "temporary string");
+                               PQclear(rs);
+                               return (ISC_R_FAILURE); /* Yeah, I'd say! */
+                       }
+                       /* copy field to tmpString */
+                       strcpy(tmpString, PQgetvalue(rs, i, 2));
+                       /*
+                        * concat the rest of fields together, space
+                        * between each one.
+                        */
+                       for (j=3; j < fields; j++) {
+                               strcat(tmpString, " ");
+                               strcat(tmpString, PQgetvalue(rs, i, j));
+                       }
+                       /* convert text to int, make sure it worked right */
+                       ttl = strtol(PQgetvalue(rs, i, 0), &endp, 10);
+                       if (*endp != '\0' || ttl < 0) {
+                               isc_log_write(dns_lctx,
+                                             DNS_LOGCATEGORY_DATABASE,
+                                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                             "Postgres driver ttl must be "
+                                             "a postive number");
+                       }
+                       /* ok, now tell Bind about it. */
+                       result = dns_sdlz_putrr(lookup, PQgetvalue(rs, i, 1),
+                                               ttl, tmpString);
+                       /* done, get rid of this thing. */
+                       isc_mem_free(ns_g_mctx, tmpString);
+               }
+               /* I sure hope we were successful */
+               if (result != ISC_R_SUCCESS) {
+                       /* nope, get rid of the Result set, and log a msg */
+                       PQclear(rs);
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                     "dns_sdlz_putrr returned error. "
+                                     "Error code was: %s",
+                                     isc_result_totext(result));
+                       return (ISC_R_FAILURE);
+               }
+       }
+
+       /* free result set memory */
+       PQclear(rs);
+
+       /* if we did return results, we are successful */
+       if (rows > 0)
+               return (ISC_R_SUCCESS);
+
+       /* empty result set, no data found */
+       return (ISC_R_NOTFOUND);
+}
+
+/*
+ * SDLZ interface methods
+ */
+
+/*% determine if the zone is supported by (in) the database */
+
+static isc_result_t
+postgres_findzone(void *driverarg, void *dbdata, const char *name)
+{
+       isc_result_t result;
+       PGresult *rs = NULL;
+       unsigned int rows;
+       UNUSED(driverarg);
+
+       /* run the query and get the result set from the database. */
+       result = postgres_get_resultset(name, NULL, NULL,
+                                       FINDZONE, dbdata, &rs);
+       /* if we didn't get a result set, log an err msg. */
+       if (result != ISC_R_SUCCESS) {
+               if (rs != NULL)
+                       PQclear(rs);
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Postgres driver unable to return "
+                             "result set for findzone query");
+               return (ISC_R_FAILURE);
+       }
+       /* count how many rows in result set */
+       rows = PQntuples(rs);
+       /* get rid of result set, we are done with it. */
+       PQclear(rs);
+
+       /* if we returned any rows, zone is supported. */
+       if (rows > 0)
+               return (ISC_R_SUCCESS);
+
+       /* no rows returned, zone is not supported. */
+       return (ISC_R_NOTFOUND);
+}
+
+/*% Determine if the client is allowed to perform a zone transfer */
+static isc_result_t
+postgres_allowzonexfr(void *driverarg, void *dbdata, const char *name,
+                     const char *client)
+{
+       isc_result_t result;
+       PGresult *rs = NULL;
+       unsigned int rows;
+       UNUSED(driverarg);
+
+       /* first check if the zone is supported by the database. */
+       result = postgres_findzone(driverarg, dbdata, name);
+       if (result != ISC_R_SUCCESS)
+               return (ISC_R_NOTFOUND);
+
+       /*
+        * if we get to this point we know the zone is supported by
+        * the database the only questions now are is the zone
+        * transfer is allowed for this client and did the config file
+        * have an allow zone xfr query.
+        *
+        * Run our query, and get a result set from the database.
+        */
+       result = postgres_get_resultset(name, NULL, client,
+                                       ALLOWXFR, dbdata, &rs);
+       /* if we get "not implemented", send it along. */
+       if (result == ISC_R_NOTIMPLEMENTED)
+               return result;
+       /* if we didn't get a result set, log an err msg. */
+       if (result != ISC_R_SUCCESS) {
+               if (rs != NULL)
+                       PQclear(rs);
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Postgres driver unable to return "
+                             "result set for allow xfr query");
+               return (ISC_R_FAILURE);
+       }
+       /* count how many rows in result set */
+       rows = PQntuples(rs);
+       /* get rid of result set, we are done with it. */
+       PQclear(rs);
+
+       /* if we returned any rows, zone xfr is allowed. */
+       if (rows > 0)
+               return (ISC_R_SUCCESS);
+
+       /* no rows returned, zone xfr not allowed */
+       return (ISC_R_NOPERM);
+}
+
+/*%
+ * If the client is allowed to perform a zone transfer, the next order of
+ * business is to get all the nodes in the zone, so bind can respond to the
+ * query.
+ */
+static isc_result_t
+postgres_allnodes(const char *zone, void *driverarg, void *dbdata,
+                 dns_sdlzallnodes_t *allnodes)
+{
+       isc_result_t result;
+       PGresult *rs = NULL;
+       unsigned int i;
+       unsigned int rows;
+       unsigned int fields;
+       unsigned int j;
+       unsigned int len;
+       char *tmpString;
+       char *endp;
+       int ttl;
+
+       UNUSED(driverarg);
+
+       /* run the query and get the result set from the database. */
+       result = postgres_get_resultset(zone, NULL, NULL,
+                                       ALLNODES, dbdata, &rs);
+       /* if we get "not implemented", send it along */
+       if (result == ISC_R_NOTIMPLEMENTED)
+               return result;
+       /* if we didn't get a result set, log an err msg. */
+       if (result != ISC_R_SUCCESS) {
+               if (rs != NULL)
+                       PQclear(rs);
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Postgres driver unable to return "
+                             "result set for all nodes query");
+               return (ISC_R_FAILURE);
+       }
+
+       rows = PQntuples(rs);   /* how many rows in result set */
+       fields = PQnfields(rs); /* how many columns in result set */
+       for (i=0; i < rows; i++) {
+               if (fields < 4) {       /* gotta have at least 4 columns */
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                     "Postgres driver too few fields "
+                                     "returned by all nodes query");
+               }
+               /* convert text to int, make sure it worked right  */
+               ttl = strtol(PQgetvalue(rs, i, 0), &endp, 10);
+               if (*endp != '\0' || ttl < 0) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                     "Postgres driver ttl must be "
+                                     "a postive number");
+               }
+               if (fields == 4) {
+                       /* tell Bind about it. */
+                       result = dns_sdlz_putnamedrr(allnodes,
+                                                    PQgetvalue(rs, i, 2),
+                                                    PQgetvalue(rs, i, 1),
+                                                    ttl,
+                                                    PQgetvalue(rs, i, 3));
+               } else {
+                       /*
+                        * more than 4 fields, concatonat the last
+                        * ones together.  figure out how long to make
+                        * string
+                        */
+                       for (j=3, len=0; j < fields; j++) {
+                               len += strlen(PQgetvalue(rs, i, j)) + 1;
+                       }
+                       /* allocate memory, allow for NULL to term string */
+                       tmpString = isc_mem_allocate(ns_g_mctx, len + 1);
+                       if (tmpString == NULL) {        /* we need more ram. */
+                               isc_log_write(dns_lctx,
+                                             DNS_LOGCATEGORY_DATABASE,
+                                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                             "Postgres driver unable to "
+                                             "allocate memory for "
+                                             "temporary string");
+                               PQclear(rs);
+                               return (ISC_R_FAILURE);
+                       }
+                       /* copy this field to tmpString */
+                       strcpy(tmpString, PQgetvalue(rs, i, 3));
+                       /* concatonate the rest, with spaces between */
+                       for (j=4; j < fields; j++) {
+                               strcat(tmpString, " ");
+                               strcat(tmpString, PQgetvalue(rs, i, j));
+                       }
+                       /* tell Bind about it. */
+                       result = dns_sdlz_putnamedrr(allnodes,
+                                                    PQgetvalue(rs, i, 2),
+                                                    PQgetvalue(rs, i, 1),
+                                                    ttl, tmpString);
+                       isc_mem_free(ns_g_mctx, tmpString);
+               }
+               /* if we weren't successful, log err msg */
+               if (result != ISC_R_SUCCESS) {
+                       PQclear(rs);
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                     "dns_sdlz_putnamedrr returned error. "
+                                     "Error code was: %s",
+                                     isc_result_totext(result));
+                       return (ISC_R_FAILURE);
+               }
+       }
+
+       /* free result set memory */
+       PQclear(rs);
+
+       /* if we did return results, we are successful */
+       if (rows > 0)
+               return (ISC_R_SUCCESS);
+
+       /* empty result set, no data found */
+       return (ISC_R_NOTFOUND);
+}
+
+/*%
+ * if the lookup function does not return SOA or NS records for the zone,
+ * use this function to get that information for Bind.
+ */
+
+static isc_result_t
+postgres_authority(const char *zone, void *driverarg, void *dbdata,
+                  dns_sdlzlookup_t *lookup)
+{
+       isc_result_t result;
+       PGresult *rs = NULL;
+
+       UNUSED(driverarg);
+
+       /* run the query and get the result set from the database. */
+       result = postgres_get_resultset(zone, NULL, NULL,
+                                       AUTHORITY, dbdata, &rs);
+       /* if we get "not implemented", send it along */
+       if (result == ISC_R_NOTIMPLEMENTED)
+               return result;
+       /* if we didn't get a result set, log an err msg. */
+       if (result != ISC_R_SUCCESS) {
+               if (rs != NULL)
+                       PQclear(rs);
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Postgres driver unable to return "
+                             "result set for authority query");
+               return (ISC_R_FAILURE);
+       }
+       /*
+        * lookup and authority result sets are processed in the same
+        * manner postgres_process_rs does the job for both
+        * functions.
+        */
+       return postgres_process_rs(lookup, rs);
+}
+
+/*% if zone is supported, lookup up a (or multiple) record(s) in it */
+static isc_result_t
+postgres_lookup(const char *zone, const char *name, void *driverarg,
+               void *dbdata, dns_sdlzlookup_t *lookup)
+{
+       isc_result_t result;
+       PGresult *rs = NULL;
+
+       UNUSED(driverarg);
+
+       /* run the query and get the result set from the database. */
+       result = postgres_get_resultset(zone, name, NULL, LOOKUP, dbdata, &rs);
+       /* if we didn't get a result set, log an err msg. */
+       if (result != ISC_R_SUCCESS) {
+               if (rs != NULL)
+                       PQclear(rs);
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Postgres driver unable to "
+                             "return result set for lookup query");
+               return (ISC_R_FAILURE);
+       }
+       /*
+        * lookup and authority result sets are processed in the same
+        * manner postgres_process_rs does the job for both functions.
+        */
+       return postgres_process_rs(lookup, rs);
+}
+
+/*%
+ * create an instance of the driver.  Remember, only 1 copy of the driver's
+ * code is ever loaded, the driver has to remember which context it's
+ * operating in.  This is done via use of the dbdata arguement which is
+ * passed into all query functions.
+ */
+static isc_result_t
+postgres_create(const char *dlzname, unsigned int argc, char *argv[],
+               void *driverarg, void **dbdata)
+{
+       isc_result_t result;
+       dbinstance_t *dbi = NULL;
+       unsigned int j;
+
+#ifdef ISC_PLATFORM_USETHREADS
+       /* if multi-threaded, we need a few extra variables. */
+       int dbcount;
+       db_list_t *dblist = NULL;
+       int i;
+       char *endp;
+
+#endif /* ISC_PLATFORM_USETHREADS */
+
+       UNUSED(driverarg);
+       UNUSED(dlzname);
+
+/* seed random # generator */
+       srand( (unsigned)time( NULL ) );
+
+
+#ifdef ISC_PLATFORM_USETHREADS
+       /* if debugging, let user know we are multithreaded. */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
+                     "Postgres driver running multithreaded");
+#else /* ISC_PLATFORM_USETHREADS */
+       /* if debugging, let user know we are single threaded. */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(1),
+                     "Postgres driver running single threaded");
+#endif /* ISC_PLATFORM_USETHREADS */
+
+       /* verify we have at least 5 arg's passed to the driver */
+       if (argc < 5) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Postgres driver requires at least "
+                             "4 command line args.");
+               return (ISC_R_FAILURE);
+       }
+
+       /* no more than 8 arg's should be passed to the driver */
+       if (argc > 8) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Postgres driver cannot accept more than "
+                             "7 command line args.");
+               return (ISC_R_FAILURE);
+       }
+
+       /* multithreaded build can have multiple DB connections */
+#ifdef ISC_PLATFORM_USETHREADS
+
+       /* check how many db connections we should create */
+       dbcount = strtol(argv[1], &endp, 10);
+       if (*endp != '\0' || dbcount < 0) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Postgres driver database connection count "
+                             "must be positive.");
+               return (ISC_R_FAILURE);
+       }
+
+       /* allocate memory for database connection list */
+       dblist = isc_mem_get(ns_g_mctx, sizeof(db_list_t));
+       if (dblist == NULL)
+               return (ISC_R_NOMEMORY);
+
+       /* initialize DB connection list */
+       ISC_LIST_INIT(*dblist);
+
+       /*
+        * create the appropriate number of database instances (DBI)
+        * append each new DBI to the end of the list
+        */
+       for (i=0; i < dbcount; i++) {
+
+#endif /* ISC_PLATFORM_USETHREADS */
+
+               /* how many queries were passed in from config file? */
+               switch(argc) {
+               case 5:
+                       result = build_sqldbinstance(ns_g_mctx, NULL, NULL,
+                                                    NULL, argv[3], argv[4],
+                                                    NULL, &dbi);
+                       break;
+               case 6:
+                       result = build_sqldbinstance(ns_g_mctx, NULL, NULL,
+                                                    argv[5], argv[3], argv[4],
+                                                    NULL, &dbi);
+                       break;
+               case 7:
+                       result = build_sqldbinstance(ns_g_mctx, argv[6], NULL,
+                                                    argv[5], argv[3], argv[4],
+                                                    NULL, &dbi);
+                       break;
+               case 8:
+                       result = build_sqldbinstance(ns_g_mctx, argv[6],
+                                                    argv[7], argv[5], argv[3],
+                                                    argv[4], NULL, &dbi);
+                       break;
+               default:
+                       /* not really needed, should shut up compiler. */
+                       result = ISC_R_FAILURE;
+               }
+
+
+               if (result == ISC_R_SUCCESS) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                                     "Postgres driver created database "
+                                     "instance object.");
+               } else { /* unsuccessful?, log err msg and cleanup. */
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                     "Postgres driver could not create "
+                                     "database instance object.");
+                       goto cleanup;
+               }
+
+#ifdef ISC_PLATFORM_USETHREADS
+
+               /* when multithreaded, build a list of DBI's */
+               ISC_LINK_INIT(dbi, link);
+               ISC_LIST_APPEND(*dblist, dbi, link);
+
+#endif
+
+               /* create and set db connection */
+               dbi->dbconn = PQconnectdb(argv[2]);
+               /*
+                * if db connection cannot be created, log err msg and
+                * cleanup.
+                */
+               if (dbi->dbconn == NULL) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                     "Postgres driver could not allocate "
+                                     "memory for database connection");
+                       goto cleanup;
+               }
+
+               /* if we cannot connect the first time, try 3 more times. */
+               for (j = 0;
+                    PQstatus((PGconn *) dbi->dbconn) != CONNECTION_OK &&
+                            j < 3;
+                    j++)
+                       PQreset((PGconn *) dbi->dbconn);
+
+
+#ifdef ISC_PLATFORM_USETHREADS
+
+               /*
+                * if multi threaded, let user know which connection
+                * failed.  user could be attempting to create 10 db
+                * connections and for some reason the db backend only
+                * allows 9
+                */
+               if (PQstatus((PGconn *) dbi->dbconn) != CONNECTION_OK) {
+                       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                                     DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                                     "Postgres driver failed to create "
+                                     "database connection number %u "
+                                     "after 4 attempts",
+                                     i + 1);
+                       goto cleanup;
+               }
+
+               /* set DBI = null for next loop through. */
+               dbi = NULL;
+       }       /* end for loop */
+
+               /* set dbdata to the list we created. */
+       *dbdata = dblist;
+
+#else /* ISC_PLATFORM_USETHREADS */
+       /* if single threaded, just let user know we couldn't connect. */
+       if (PQstatus((PGconn *) dbi->dbconn) != CONNECTION_OK) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Postgres driver failed to create database "
+                             "connection after 4 attempts");
+               goto cleanup;
+       }
+
+       /*
+        * single threaded build can only use 1 db connection, return
+        * it via dbdata
+        */
+       *dbdata = dbi;
+
+#endif /* ISC_PLATFORM_USETHREADS */
+
+       /* hey, we got through all of that ok, return success. */
+       return(ISC_R_SUCCESS);
+
+ cleanup:
+
+#ifdef ISC_PLATFORM_USETHREADS
+       /*
+        * if multithreaded, we could fail because only 1 connection
+        * couldn't be made.  We should cleanup the other successful
+        * connections properly.
+        */
+       postgres_destroy_dblist(dblist);
+
+#else /* ISC_PLATFORM_USETHREADS */
+       if (dbi != NULL)
+               destroy_sqldbinstance(dbi);
+
+#endif /* ISC_PLATFORM_USETHREADS */
+       return(ISC_R_FAILURE);
+}
+
+/*%
+ * destroy an instance of the driver.  Remember, only 1 copy of the driver's
+ * code is ever loaded, the driver has to remember which context it's
+ * operating in.  This is done via use of the dbdata arguement.
+ * so we really only need to clean it up since we are not using driverarg.
+ */
+static void
+postgres_destroy(void *driverarg, void *dbdata)
+{
+
+#ifdef ISC_PLATFORM_USETHREADS
+
+       UNUSED(driverarg);
+       /* cleanup the list of DBI's */
+       postgres_destroy_dblist((db_list_t *) dbdata);
+
+#else /* ISC_PLATFORM_USETHREADS */
+
+       dbinstance_t *dbi;
+
+       UNUSED(driverarg);
+
+       dbi = (dbinstance_t *) dbdata;
+
+       /* release DB connection */
+       if (dbi->dbconn != NULL)
+               PQfinish((PGconn *) dbi->dbconn);
+
+       /* destroy single DB instance */
+       destroy_sqldbinstance(dbi);
+
+#endif /* ISC_PLATFORM_USETHREADS */
+}
+
+/* pointers to all our runtime methods. */
+/* this is used during driver registration */
+/* i.e. in dlz_postgres_init below. */
+static dns_sdlzmethods_t dlz_postgres_methods = {
+       postgres_create,
+       postgres_destroy,
+       postgres_findzone,
+       postgres_lookup,
+       postgres_authority,
+       postgres_allnodes,
+       postgres_allowzonexfr
+};
+
+/*%
+ * Wrapper around dns_sdlzregister().
+ */
+isc_result_t
+dlz_postgres_init(void) {
+       isc_result_t result;
+
+       /*
+        * Write debugging message to log
+        */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                     "Registering DLZ postgres driver.");
+
+       /*
+        * Driver is always threadsafe.  When multithreaded all
+        * functions use multithreaded code.  When not multithreaded,
+        * all functions can only be entered once, but only 1 thread
+        * of operation is available in Bind.  So everything is still
+        * threadsafe.
+        */
+       result = dns_sdlzregister("postgres", &dlz_postgres_methods, NULL,
+                                 DNS_SDLZFLAG_RELATIVEOWNER |
+                                 DNS_SDLZFLAG_RELATIVERDATA |
+                                 DNS_SDLZFLAG_THREADSAFE,
+                                 ns_g_mctx, &dlz_postgres);
+       /* if we can't register the driver, there are big problems. */
+       if (result != ISC_R_SUCCESS) {
+               UNEXPECTED_ERROR(__FILE__, __LINE__,
+                                "dns_sdlzregister() failed: %s",
+                                isc_result_totext(result));
+               result = ISC_R_UNEXPECTED;
+       }
+
+
+       return result;
+}
+
+/*%
+ * Wrapper around dns_sdlzunregister().
+ */
+void
+dlz_postgres_clear(void) {
+
+       /*
+        * Write debugging message to log
+        */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                     "Unregistering DLZ postgres driver.");
+
+       /* unregister the driver. */
+       if (dlz_postgres != NULL)
+               dns_sdlzunregister(&dlz_postgres);
+}
+
+#endif
diff --git a/contrib/dlz/drivers/dlz_stub_driver.c b/contrib/dlz/drivers/dlz_stub_driver.c
new file mode 100644 (file)
index 0000000..f9a3bb8
--- /dev/null
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
+ *
+ * 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 STICHTING NLNET
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * STICHTING NLNET 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 development of Dynamically Loadable Zones (DLZ) for Bind 9 was
+ * conceived and contributed by Rob Butler.
+ *
+ * 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 ROB BUTLER
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * ROB BUTLER 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.
+ */
+
+/*
+ * Copyright (C) 1999-2001  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.
+ */
+
+#ifdef DLZ_STUB
+
+#include <config.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include <dns/log.h>
+#include <dns/sdlz.h>
+#include <dns/result.h>
+
+#include <isc/mem.h>
+#include <isc/print.h>
+#include <isc/result.h>
+#include <isc/util.h>
+
+#include <named/globals.h>
+
+#include <dlz/dlz_stub_driver.h>
+
+static dns_sdlzimplementation_t *dlz_stub = NULL;
+
+typedef struct config_data {
+       char            *myzone;
+       char            *myname;
+       char            *myip;
+       isc_mem_t       *mctx;
+} config_data_t;
+
+/*
+ * SDLZ methods
+ */
+
+static isc_result_t
+stub_dlz_allnodes(const char *zone, void *driverarg, void *dbdata,
+                 dns_sdlzallnodes_t *allnodes)
+{
+       config_data_t *cd;
+       isc_result_t result;
+
+       UNUSED(zone);
+       UNUSED(driverarg);
+
+       cd = (config_data_t *) dbdata;
+
+       result = dns_sdlz_putnamedrr(allnodes, cd->myname, "soa", 86400,
+                                    "web root.localhost. "
+                                    "0 28800 7200 604800 86400");
+       if (result != ISC_R_SUCCESS)
+               return (ISC_R_FAILURE);
+       result = dns_sdlz_putnamedrr(allnodes, "ns", "ns", 86400, cd->myname);
+       if (result != ISC_R_SUCCESS)
+               return (ISC_R_FAILURE);
+       result = dns_sdlz_putnamedrr(allnodes, cd->myname, "a", 1, cd->myip);
+       if (result != ISC_R_SUCCESS)
+               return (ISC_R_FAILURE);
+       return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+stub_dlz_allowzonexfr(void *driverarg, void *dbdata, const char *name,
+                     const char *client)
+{
+       UNUSED(driverarg);
+       UNUSED(dbdata);
+       UNUSED(name);
+       UNUSED(client);
+       return ISC_R_SUCCESS;
+}
+
+static isc_result_t
+stub_dlz_authority(const char *zone, void *driverarg, void *dbdata,
+                  dns_sdlzlookup_t *lookup)
+{
+       isc_result_t result;
+       config_data_t *cd;
+
+       UNUSED(driverarg);
+
+       cd = (config_data_t *) dbdata;
+
+       if (strcmp(zone, cd->myzone) == 0) {
+               result = dns_sdlz_putsoa(lookup, cd->myname,
+                                        "root.localhost.", 0);
+               if (result != ISC_R_SUCCESS)
+                       return (ISC_R_FAILURE);
+
+               result = dns_sdlz_putrr(lookup, "ns", 86400, cd->myname);
+               if (result != ISC_R_SUCCESS)
+                       return (ISC_R_FAILURE);
+
+               return (ISC_R_SUCCESS);
+       }
+       return (ISC_R_NOTFOUND);
+}
+
+static isc_result_t
+stub_dlz_findzonedb(void *driverarg, void *dbdata, const char *name)
+{
+
+       config_data_t *cd;
+
+       UNUSED(driverarg);
+
+       cd = (config_data_t *) dbdata;
+
+       /* Write info message to log */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                     "dlz_stub findzone looking for '%s'", name);
+
+       if (strcmp(cd->myzone, name) == 0)
+               return (ISC_R_SUCCESS);
+       else
+               return (ISC_R_SUCCESS);
+}
+
+
+static isc_result_t
+stub_dlz_lookup(const char *zone, const char *name, void *driverarg,
+               void *dbdata, dns_sdlzlookup_t *lookup)
+{
+       isc_result_t result;
+       config_data_t *cd;
+
+       UNUSED(zone);
+       UNUSED(driverarg);
+
+       cd = (config_data_t *) dbdata;
+
+       if (strcmp(name, cd->myname) == 0) {
+               result = dns_sdlz_putrr(lookup, "a", 1, cd->myip);
+               if (result != ISC_R_SUCCESS)
+                       return (ISC_R_FAILURE);
+
+               return (ISC_R_SUCCESS);
+       }
+       return (ISC_R_FAILURE);
+
+}
+
+
+static isc_result_t
+stub_dlz_create(const char *dlzname, unsigned int argc, char *argv[],
+               void *driverarg, void **dbdata)
+{
+
+       config_data_t *cd;
+
+       UNUSED(driverarg);
+
+       if (argc < 4)
+               return (ISC_R_FAILURE);
+       /*
+        * Write info message to log
+        */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
+                     "Loading '%s' using DLZ_stub driver. "
+                     "Zone: %s, Name: %s IP: %s",
+                     dlzname, argv[1], argv[2], argv[3]);
+
+       cd = isc_mem_get(ns_g_mctx, sizeof(config_data_t));
+       if ((cd) == NULL) {
+               return (ISC_R_NOMEMORY);
+       }
+
+       memset(cd, 0, sizeof(config_data_t));
+
+       cd->myzone = isc_mem_strdup(ns_g_mctx, argv[1]);
+       if (cd->myzone == NULL) {
+               isc_mem_put(ns_g_mctx, cd, sizeof(config_data_t));
+               return (ISC_R_NOMEMORY);
+       }
+
+       cd->myname = isc_mem_strdup(ns_g_mctx, argv[2]);
+       if (cd->myname == NULL) {
+               isc_mem_put(ns_g_mctx, cd, sizeof(config_data_t));
+               isc_mem_free(ns_g_mctx, cd->myzone);
+               return (ISC_R_NOMEMORY);
+       }
+
+       cd->myip = isc_mem_strdup(ns_g_mctx, argv[3]);
+       if (cd->myip == NULL) {
+               isc_mem_put(ns_g_mctx, cd, sizeof(config_data_t));
+               isc_mem_free(ns_g_mctx, cd->myname);
+               isc_mem_free(ns_g_mctx, cd->myzone);
+               return (ISC_R_NOMEMORY);
+       }
+
+       isc_mem_attach(ns_g_mctx, &cd->mctx);
+
+       *dbdata = cd;
+
+       return(ISC_R_SUCCESS);
+}
+
+static void
+stub_dlz_destroy(void *driverarg, void *dbdata)
+{
+       config_data_t *cd;
+       isc_mem_t *mctx;
+
+       UNUSED(driverarg);
+
+       cd = (config_data_t *) dbdata;
+
+       /*
+        * Write debugging message to log
+        */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                     "Unloading DLZ_stub driver.");
+
+       isc_mem_free(ns_g_mctx, cd->myzone);
+       isc_mem_free(ns_g_mctx, cd->myname);
+       isc_mem_free(ns_g_mctx, cd->myip);
+       mctx = cd->mctx;
+       isc_mem_put(mctx, cd, sizeof(config_data_t));
+       isc_mem_detach(&mctx);
+}
+
+static dns_sdlzmethods_t dlz_stub_methods = {
+       stub_dlz_create,
+       stub_dlz_destroy,
+       stub_dlz_findzonedb,
+       stub_dlz_lookup,
+       stub_dlz_authority,
+       stub_dlz_allnodes,
+       stub_dlz_allowzonexfr
+};
+
+/*%
+ * Wrapper around dns_sdlzregister().
+ */
+isc_result_t
+dlz_stub_init(void) {
+       isc_result_t result;
+
+       /*
+        * Write debugging message to log
+        */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                     "Registering DLZ_stub driver.");
+
+       result = dns_sdlzregister("dlz_stub", &dlz_stub_methods, NULL,
+                                 DNS_SDLZFLAG_RELATIVEOWNER |
+                                 DNS_SDLZFLAG_RELATIVERDATA,
+                                 ns_g_mctx, &dlz_stub);
+       if (result != ISC_R_SUCCESS) {
+               UNEXPECTED_ERROR(__FILE__, __LINE__,
+                                "dns_sdlzregister() failed: %s",
+                                isc_result_totext(result));
+               result = ISC_R_UNEXPECTED;
+       }
+
+
+       return result;
+}
+
+/*
+ * Wrapper around dns_sdlzunregister().
+ */
+void
+dlz_stub_clear(void) {
+
+       /*
+        * Write debugging message to log
+        */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                     "Unregistering DLZ_stub driver.");
+
+       if (dlz_stub != NULL)
+               dns_sdlzunregister(&dlz_stub);
+}
+
+#endif
diff --git a/contrib/dlz/drivers/include/dlz/dlz_bdb_driver.h b/contrib/dlz/drivers/include/dlz/dlz_bdb_driver.h
new file mode 100644 (file)
index 0000000..1d706fc
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
+ *
+ * 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 STICHTING NLNET
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * STICHTING NLNET 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 development of Dynamically Loadable Zones (DLZ) for Bind 9 was
+ * conceived and contributed by Rob Butler.
+ *
+ * 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 ROB BUTLER
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * ROB BUTLER 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.
+ */
+
+#ifndef DLZ_BDB_DRIVER_H
+#define DLZ_BDB_DRIVER_H
+
+isc_result_t
+dlz_bdb_init(void);
+
+void
+dlz_bdb_clear(void);
+
+#endif
diff --git a/contrib/dlz/drivers/include/dlz/dlz_bdbhpt_driver.h b/contrib/dlz/drivers/include/dlz/dlz_bdbhpt_driver.h
new file mode 100644 (file)
index 0000000..0ccbc8d
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
+ *
+ * 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 STICHTING NLNET
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * STICHTING NLNET 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 development of Dynamically Loadable Zones (DLZ) for Bind 9 was
+ * conceived and contributed by Rob Butler.
+ *
+ * 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 ROB BUTLER
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * ROB BUTLER 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.
+ */
+
+#ifndef DLZ_BDBHPT_DRIVER_H
+#define DLZ_BDBHPT_DRIVER_H
+
+isc_result_t
+dlz_bdbhpt_init(void);
+
+void
+dlz_bdbhpt_clear(void);
+
+#endif
diff --git a/contrib/dlz/drivers/include/dlz/dlz_drivers.h b/contrib/dlz/drivers/include/dlz/dlz_drivers.h
new file mode 100644 (file)
index 0000000..dce7cd2
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2005  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC 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: dlz_drivers.h,v 1.2 2005/09/05 00:10:58 marka Exp $ */
+
+#ifndef DLZ_DRIVERS_H
+#define DLZ_DRIVERS_H 1
+
+/*! \file */
+
+isc_result_t
+dlz_drivers_init(void);
+
+void
+dlz_drivers_clear(void);
+
+#endif /* DLZ_DRIVERS_H */
diff --git a/contrib/dlz/drivers/include/dlz/dlz_filesystem_driver.h b/contrib/dlz/drivers/include/dlz/dlz_filesystem_driver.h
new file mode 100644 (file)
index 0000000..627427d
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
+ *
+ * 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 STICHTING NLNET
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * STICHTING NLNET 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 development of Dynamically Loadable Zones (DLZ) for Bind 9 was
+ * conceived and contributed by Rob Butler.
+ *
+ * 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 ROB BUTLER
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * ROB BUTLER 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.
+ */
+
+#ifndef DLZ_FILESYSTEM_DRIVER_H
+#define DLZ_FILESYSTEM_DRIVER_H
+
+isc_result_t
+dlz_fs_init(void);
+
+void
+dlz_fs_clear(void);
+
+#endif
diff --git a/contrib/dlz/drivers/include/dlz/dlz_ldap_driver.h b/contrib/dlz/drivers/include/dlz/dlz_ldap_driver.h
new file mode 100644 (file)
index 0000000..51efbf4
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
+ *
+ * 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 STICHTING NLNET
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * STICHTING NLNET 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 development of Dynamically Loadable Zones (DLZ) for Bind 9 was
+ * conceived and contributed by Rob Butler.
+ *
+ * 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 ROB BUTLER
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * ROB BUTLER 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.
+ */
+
+#ifndef DLZ_LDAP_DRIVER_H
+#define DLZ_LDAP_DRIVER_H
+
+isc_result_t
+dlz_ldap_init(void);
+
+void
+dlz_ldap_clear(void);
+
+#endif
diff --git a/contrib/dlz/drivers/include/dlz/dlz_mysql_driver.h b/contrib/dlz/drivers/include/dlz/dlz_mysql_driver.h
new file mode 100644 (file)
index 0000000..b742838
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
+ *
+ * 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 STICHTING NLNET
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * STICHTING NLNET 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 development of Dynamically Loadable Zones (DLZ) for Bind 9 was
+ * conceived and contributed by Rob Butler.
+ *
+ * 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 ROB BUTLER
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * ROB BUTLER 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.
+ */
+
+#ifndef DLZ_MYSQL_DRIVER_H
+#define DLZ_MYSQL_DRIVER_H
+
+isc_result_t
+dlz_mysql_init(void);
+
+void
+dlz_mysql_clear(void);
+
+#endif
diff --git a/contrib/dlz/drivers/include/dlz/dlz_odbc_driver.h b/contrib/dlz/drivers/include/dlz/dlz_odbc_driver.h
new file mode 100644 (file)
index 0000000..5a072d8
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
+ *
+ * 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 STICHTING NLNET
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * STICHTING NLNET 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 development of Dynamically Loadable Zones (DLZ) for Bind 9 was
+ * conceived and contributed by Rob Butler.
+ *
+ * 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 ROB BUTLER
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * ROB BUTLER 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.
+ */
+
+#ifndef DLZ_ODBC_DRIVER_H
+#define DLZ_ODBC_DRIVER_H
+
+isc_result_t
+dlz_odbc_init(void);
+
+void
+dlz_odbc_clear(void);
+
+#endif
diff --git a/contrib/dlz/drivers/include/dlz/dlz_postgres_driver.h b/contrib/dlz/drivers/include/dlz/dlz_postgres_driver.h
new file mode 100644 (file)
index 0000000..3416dce
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
+ *
+ * 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 STICHTING NLNET
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * STICHTING NLNET 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 development of Dynamically Loadable Zones (DLZ) for Bind 9 was
+ * conceived and contributed by Rob Butler.
+ *
+ * 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 ROB BUTLER
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * ROB BUTLER 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.
+ */
+
+#ifndef DLZ_POSTGRES_DRIVER_H
+#define DLZ_POSTGRES_DRIVER_H
+
+isc_result_t
+dlz_postgres_init(void);
+
+void
+dlz_postgres_clear(void);
+
+#endif
diff --git a/contrib/dlz/drivers/include/dlz/dlz_stub_driver.h b/contrib/dlz/drivers/include/dlz/dlz_stub_driver.h
new file mode 100644 (file)
index 0000000..a517c69
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
+ *
+ * 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 STICHTING NLNET
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * STICHTING NLNET 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 development of Dynamically Loadable Zones (DLZ) for Bind 9 was
+ * conceived and contributed by Rob Butler.
+ *
+ * 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 ROB BUTLER
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * ROB BUTLER 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.
+ */
+
+#ifndef DLZ_STUB_DRIVER_H
+#define DLZ_STUB_DRIVER_H
+
+isc_result_t
+dlz_stub_init(void);
+
+void
+dlz_stub_clear(void);
+
+#endif
diff --git a/contrib/dlz/drivers/include/dlz/sdlz_helper.h b/contrib/dlz/drivers/include/dlz/sdlz_helper.h
new file mode 100644 (file)
index 0000000..764a05a
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
+ *
+ * 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 STICHTING NLNET
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * STICHTING NLNET 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 development of Dynamically Loadable Zones (DLZ) for Bind 9 was
+ * conceived and contributed by Rob Butler.
+ *
+ * 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 ROB BUTLER
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * ROB BUTLER 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.
+ */
+
+#ifndef SDLZHELPER_H
+#define SDLZHELPER_H
+
+/*
+ * Types
+ */
+#define SDLZH_REQUIRE_CLIENT   0x01
+#define SDLZH_REQUIRE_QUERY    0x02
+#define SDLZH_REQUIRE_RECORD   0x04
+#define SDLZH_REQUIRE_ZONE     0x08
+
+typedef struct query_segment query_segment_t;
+typedef ISC_LIST(query_segment_t) query_list_t;
+typedef struct dbinstance dbinstance_t;
+typedef ISC_LIST(dbinstance_t) db_list_t;
+typedef struct driverinstance driverinstance_t;
+
+/*%
+ * a query segment is all the text between our special tokens
+ * special tokens are %zone%, %record%, %client%
+ */
+struct query_segment {
+       void                            *sql;
+       unsigned int                    strlen;
+       isc_boolean_t                   direct;
+       ISC_LINK(query_segment_t)       link;
+};
+
+/*%
+ * a database instance contains everything we need for running
+ * a query against the database.  Using it each separate thread
+ * can dynamically construct a query and execute it against the
+ * database.  The "instance_lock" and locking code in the driver's
+ * make sure no two threads try to use the same DBI at a time.
+ */
+struct dbinstance {
+       void                    *dbconn;
+       query_list_t            *allnodes_q;
+       query_list_t            *allowxfr_q;
+       query_list_t            *authority_q;
+       query_list_t            *findzone_q;
+       query_list_t            *lookup_q;
+       query_list_t            *countzone_q;
+       char                    *query_buf;
+       char                    *zone;
+       char                    *record;
+       char                    *client;
+       isc_mem_t               *mctx;
+       isc_mutex_t             instance_lock;
+       ISC_LINK(dbinstance_t)  link;
+};
+
+/*
+ * Method declarations
+ */
+
+/* see the code in sdlz_helper.c for more information on these methods */
+
+char *
+sdlzh_build_querystring(isc_mem_t *mctx, query_list_t *querylist);
+
+isc_result_t
+sdlzh_build_sqldbinstance(isc_mem_t *mctx, const char *allnodes_str,
+                         const char *allowxfr_str, const char *authority_str,
+                         const char *findzone_str, const char *lookup_str,
+                         const char *countzone_str, dbinstance_t **dbi);
+
+void
+sdlzh_destroy_sqldbinstance(dbinstance_t *dbi);
+
+char *
+sdlzh_get_parameter_value(isc_mem_t *mctx, const char *input, const char* key);
+
+/* Compatability with existing DLZ drivers */
+
+#define        build_querystring       sdlzh_build_querystring
+#define        build_sqldbinstance     sdlzh_build_sqldbinstance
+#define        destroy_sqldbinstance   sdlzh_destroy_sqldbinstance
+
+#define        getParameterValue(x,y)  sdlzh_get_parameter_value(ns_g_mctx, (x), (y))
+
+#endif /* SDLZHELPER_H */
diff --git a/contrib/dlz/drivers/rules.in b/contrib/dlz/drivers/rules.in
new file mode 100644 (file)
index 0000000..9caabcb
--- /dev/null
@@ -0,0 +1,47 @@
+# Copyright (C) 2005  Internet Systems Consortium, Inc. ("ISC")
+#
+# 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 ISC DISCLAIMS ALL WARRANTIES WITH
+# REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS.  IN NO EVENT SHALL ISC 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: rules.in,v 1.2 2005/09/05 00:10:57 marka Exp $
+
+dlz_drivers.@O@: ${DLZ_DRIVER_DIR}/dlz_drivers.c ${DLZ_DRIVER_DIR}/include/dlz/dlz_drivers.h
+       ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} -c ${DLZ_DRIVER_DIR}/dlz_drivers.c
+
+sdlz_helper.@O@: ${DLZ_DRIVER_DIR}/sdlz_helper.c ${DLZ_DRIVER_DIR}/include/dlz/sdlz_helper.h
+       ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} -c ${DLZ_DRIVER_DIR}/sdlz_helper.c
+
+
+dlz_bdb_driver.@O@: ${DLZ_DRIVER_DIR}/dlz_bdb_driver.c ${DLZ_DRIVER_DIR}/include/dlz/dlz_bdb_driver.h
+       ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} -c ${DLZ_DRIVER_DIR}/dlz_bdb_driver.c
+
+dlz_bdbhpt_driver.@O@: ${DLZ_DRIVER_DIR}/dlz_bdbhpt_driver.c ${DLZ_DRIVER_DIR}/include/dlz/dlz_bdbhpt_driver.h
+       ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} -c ${DLZ_DRIVER_DIR}/dlz_bdbhpt_driver.c
+
+dlz_filesystem_driver.@O@: ${DLZ_DRIVER_DIR}/dlz_filesystem_driver.c ${DLZ_DRIVER_DIR}/include/dlz/dlz_filesystem_driver.h
+       ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} -c ${DLZ_DRIVER_DIR}/dlz_filesystem_driver.c
+
+dlz_ldap_driver.@O@: ${DLZ_DRIVER_DIR}/dlz_ldap_driver.c ${DLZ_DRIVER_DIR}/include/dlz/dlz_ldap_driver.h
+       ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} -c ${DLZ_DRIVER_DIR}/dlz_ldap_driver.c
+
+dlz_mysql_driver.@O@: ${DLZ_DRIVER_DIR}/dlz_mysql_driver.c ${DLZ_DRIVER_DIR}/include/dlz/dlz_mysql_driver.h
+       ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} -c ${DLZ_DRIVER_DIR}/dlz_mysql_driver.c
+
+dlz_odbc_driver.@O@: ${DLZ_DRIVER_DIR}/dlz_odbc_driver.c ${DLZ_DRIVER_DIR}/include/dlz/dlz_odbc_driver.h
+       ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} -c ${DLZ_DRIVER_DIR}/dlz_odbc_driver.c
+
+dlz_postgres_driver.@O@: ${DLZ_DRIVER_DIR}/dlz_postgres_driver.c ${DLZ_DRIVER_DIR}/include/dlz/dlz_postgres_driver.h
+       ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} -c ${DLZ_DRIVER_DIR}/dlz_postgres_driver.c
+
+dlz_stub_driver.@O@: ${DLZ_DRIVER_DIR}/dlz_stub_driver.c ${DLZ_DRIVER_DIR}/include/dlz/dlz_stub_driver.h
+       ${LIBTOOL_MODE_COMPILE} ${CC} ${ALL_CFLAGS} -c ${DLZ_DRIVER_DIR}/dlz_stub_driver.c
+
diff --git a/contrib/dlz/drivers/sdlz_helper.c b/contrib/dlz/drivers/sdlz_helper.c
new file mode 100644 (file)
index 0000000..ea33db1
--- /dev/null
@@ -0,0 +1,531 @@
+/*
+ * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
+ *
+ * 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 STICHTING NLNET
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * STICHTING NLNET 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 development of Dynamically Loadable Zones (DLZ) for Bind 9 was
+ * conceived and contributed by Rob Butler.
+ *
+ * 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 ROB BUTLER
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * ROB BUTLER 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.
+ */
+
+/*
+ * Copyright (C) 1999-2001  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.
+ */
+
+#ifdef DLZ
+
+#include <config.h>
+
+#include <dns/log.h>
+#include <dns/result.h>
+
+#include <isc/mem.h>
+#include <isc/result.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+#include <dlz/sdlz_helper.h>
+
+/*
+ * sdlz helper methods
+ */
+
+/*%
+ * properly destroys a querylist by de-allocating the
+ * memory for each query segment, and then the list itself
+ */
+
+static void
+destroy_querylist(isc_mem_t *mctx, query_list_t **querylist)
+{
+       query_segment_t *tseg = NULL;
+       query_segment_t *nseg = NULL;
+
+       REQUIRE(mctx != NULL);
+
+       /* if query list is null, nothing to do */
+       if (*querylist == NULL)
+               return;
+
+       /* start at the top of the list */
+       nseg = ISC_LIST_HEAD(**querylist);
+       while (nseg != NULL) {  /* loop, until end of list */
+               tseg = nseg;
+               /*
+                * free the query segment's text string but only if it
+                * was really a query segment, and not a pointer to
+                * %zone%, or %record%, or %client%
+               */
+               if (tseg->sql != NULL && tseg->direct == isc_boolean_true)
+                       isc_mem_free(mctx, tseg->sql);
+               /* get the next query segment, before we destroy this one. */
+               nseg = ISC_LIST_NEXT(nseg, link);
+               /* deallocate this query segment. */
+               isc_mem_put(mctx, tseg, sizeof(query_segment_t));
+       }
+       /* deallocate the query segment list */
+       isc_mem_put(mctx, *querylist, sizeof(query_list_t));
+}
+
+/*% constructs a query list by parsing a string into query segments */
+static isc_result_t
+build_querylist(isc_mem_t *mctx, const char *query_str, char **zone,
+               char **record, char **client, query_list_t **querylist,
+               unsigned int flags)
+{
+       isc_result_t result;
+       isc_boolean_t foundzone = isc_boolean_false;
+       isc_boolean_t foundrecord = isc_boolean_false;
+       isc_boolean_t foundclient = isc_boolean_false;
+       char *temp_str = NULL;
+       char *right_str = NULL;
+       query_list_t *tql;
+       query_segment_t *tseg = NULL;
+
+       REQUIRE(querylist != NULL && *querylist == NULL);
+       REQUIRE(mctx != NULL);
+
+       /* if query string is null, or zero length */
+       if (query_str == NULL || strlen(query_str) < 1) {
+               if ((flags & SDLZH_REQUIRE_QUERY) == 0)
+                       /* we don't need it were ok. */
+                       return (ISC_R_SUCCESS);
+               else
+                       /* we did need it, PROBLEM!!! */
+                       return (ISC_R_FAILURE);
+       }
+
+       /* allocate memory for query list */
+       tql = isc_mem_get(mctx, sizeof(query_list_t));
+       /* couldn't allocate memory.  Problem!! */
+       if (tql == NULL)
+               return (ISC_R_NOMEMORY);
+
+       /* initialize the query segment list */
+       ISC_LIST_INIT(*tql);
+
+       /* make a copy of query_str so we can chop it up */
+       temp_str = right_str = isc_mem_strdup(mctx, query_str);
+       /* couldn't make a copy, problem!! */
+       if (right_str == NULL) {
+               result = ISC_R_NOMEMORY;
+               goto cleanup;
+       }
+
+       /* loop through the string and chop it up */
+       while (right_str != NULL) {
+               /* allocate memory for tseg */
+               tseg = isc_mem_get(mctx, sizeof(query_segment_t));
+               if (tseg  == NULL) {    /* no memory, clean everything up. */
+                       result = ISC_R_NOMEMORY;
+                       goto cleanup;
+               }
+               tseg->sql = NULL;
+               tseg->direct = isc_boolean_false;
+               /* initialize the query segment link */
+               ISC_LINK_INIT(tseg, link);
+               /* append the query segment to the list */
+               ISC_LIST_APPEND(*tql, tseg, link);
+
+               /*
+                * split string at the first "%". set query segment to
+                * left portion
+                */
+               tseg->sql = isc_mem_strdup(mctx,
+                                          isc_string_separate(&right_str,
+                                                              "%"));
+               if (tseg->sql == NULL) {
+                       /* no memory, clean everything up. */
+                       result = ISC_R_NOMEMORY;
+                       goto cleanup;
+               }
+               /* tseg->sql points directly to a string. */
+               tseg->direct = isc_boolean_true;
+               tseg->strlen = strlen(tseg->sql);
+
+               /* check if we encountered "%zone%" token */
+               if (strcasecmp(tseg->sql, "zone") == 0) {
+                       /*
+                        * we don't really need, or want the "zone"
+                        * text, so get rid of it.
+                        */
+                       isc_mem_free(mctx, tseg->sql);
+                       /* set tseg->sql to in-direct zone string */
+                       tseg->sql = (char**) zone;
+                       tseg->strlen = 0;
+                       /* tseg->sql points in-directly to a string */
+                       tseg->direct = isc_boolean_false;
+                       foundzone = isc_boolean_true;
+                       /* check if we encountered "%record%" token */
+               } else if (strcasecmp(tseg->sql, "record") == 0) {
+                       /*
+                        * we don't really need, or want the "record"
+                        * text, so get rid of it.
+                        */
+                       isc_mem_free(mctx, tseg->sql);
+                       /* set tseg->sql to in-direct record string */
+                       tseg->sql = (char**) record;
+                       tseg->strlen = 0;
+                       /* tseg->sql points in-directly poinsts to a string */
+                       tseg->direct = isc_boolean_false;
+                       foundrecord = isc_boolean_true;
+                       /* check if we encountered "%client%" token */
+               } else if (strcasecmp(tseg->sql, "client") == 0) {
+                       /*
+                        * we don't really need, or want the "client"
+                        * text, so get rid of it.
+                        */
+                       isc_mem_free(mctx, tseg->sql);
+                       /* set tseg->sql to in-direct record string */
+                       tseg->sql = (char**) client;
+                       tseg->strlen = 0;
+                       /* tseg->sql points in-directly poinsts to a string */
+                       tseg->direct = isc_boolean_false;
+                       foundclient = isc_boolean_true;
+               }
+       }
+
+       /* we don't need temp_str any more */
+       isc_mem_free(mctx, temp_str);
+       /*
+        * add checks later to verify zone and record are found if
+        * necessary.
+        */
+
+       /* if this query requires %client%, make sure we found it */
+       if (((flags & SDLZH_REQUIRE_CLIENT) != 0) && (!foundclient) ) {
+               /* Write error message to log */
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Required token %%client%% not found.");
+               result = ISC_R_FAILURE;
+               goto flag_fail;
+       }
+
+       /* if this query requires %record%, make sure we found it */
+       if (((flags & SDLZH_REQUIRE_RECORD) != 0) && (!foundrecord) ) {
+               /* Write error message to log */
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Required token %%record%% not found.");
+               result = ISC_R_FAILURE;
+               goto flag_fail;
+       }
+
+       /* if this query requires %zone%, make sure we found it */
+       if (((flags & SDLZH_REQUIRE_ZONE) != 0) && (!foundzone) ) {
+               /* Write error message to log */
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Required token %%zone%% not found.");
+               result = ISC_R_FAILURE;
+               goto flag_fail;
+       }
+
+       /* pass back the query list */
+       *querylist = (query_list_t *) tql;
+
+       /* return success */
+       return (ISC_R_SUCCESS);
+
+ cleanup:
+       /* get rid of temp_str */
+       if (temp_str != NULL)
+               isc_mem_free(mctx, temp_str);
+
+ flag_fail:
+       /* get rid of what was build of the query list */
+       if (tql != NULL)
+               destroy_querylist(mctx, &tql);
+       return result;
+}
+
+/*%
+ * build a query string from query segments, and dynamic segments
+ * dynamic segments replace where the tokens %zone%, %record%, %client%
+ * used to be in our queries from named.conf
+ */
+char *
+sdlzh_build_querystring(isc_mem_t *mctx, query_list_t *querylist)
+{
+       query_segment_t *tseg = NULL;
+       unsigned int length = 0;
+       char *qs = NULL;
+
+       REQUIRE(mctx != NULL);
+       REQUIRE(querylist != NULL);
+
+       /* start at the top of the list */
+       tseg = ISC_LIST_HEAD(*querylist);
+       while (tseg != NULL) {
+               /*
+                * if this is a query segment, use the
+                * precalculated string length
+                */
+               if (tseg->direct == isc_boolean_true)
+                       length += tseg->strlen;
+               else    /* calculate string length for dynamic segments. */
+                       length += strlen(* (char**) tseg->sql);
+               /* get the next segment */
+               tseg = ISC_LIST_NEXT(tseg, link);
+       }
+
+       /* allocate memory for the string */
+       qs = isc_mem_allocate(mctx, length + 1);
+       /* couldn't allocate memory,  We need more ram! */
+       if (qs == NULL)
+               return NULL;
+
+       /* start at the top of the list again */
+       tseg = ISC_LIST_HEAD(*querylist);
+       /* copy the first item in the list to the query string */
+       if (tseg->direct == isc_boolean_true)   /* query segment */
+               strcpy(qs, tseg->sql);
+       else
+               strcpy(qs, * (char**) tseg->sql); /* dynamic segment */
+
+       /* concatonate the rest of the segments */
+       while ((tseg = ISC_LIST_NEXT(tseg, link)) != NULL) {
+               if (tseg->direct == isc_boolean_true)
+                       /* query segments */
+                       strcat(qs, tseg->sql);
+               else
+                       /* dynamic segments */
+                       strcat(qs, * (char**) tseg->sql);
+       }
+
+       return qs;
+}
+
+/*% constructs a sql dbinstance (DBI) */
+isc_result_t
+sdlzh_build_sqldbinstance(isc_mem_t *mctx, const char *allnodes_str,
+                        const char *allowxfr_str, const char *authority_str,
+                        const char *findzone_str, const char *lookup_str,
+                        const char *countzone_str, dbinstance_t **dbi)
+{
+
+       isc_result_t result;
+       dbinstance_t *db = NULL;
+
+       REQUIRE(dbi != NULL && *dbi == NULL);
+       REQUIRE(mctx != NULL);
+
+       /* allocate and zero memory for driver structure */
+       db = isc_mem_get(mctx, sizeof(dbinstance_t));
+       if (db == NULL) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Could not allocate memory for "
+                             "database instance object.");
+               return (ISC_R_NOMEMORY);
+       }
+       memset(db, 0, sizeof(dbinstance_t));
+       db->dbconn = NULL;
+       db->client = NULL;
+       db->record = NULL;
+       db->zone = NULL;
+       db->mctx = NULL;
+       db->query_buf = NULL;
+       db->allnodes_q = NULL;
+       db->allowxfr_q = NULL;
+       db->authority_q = NULL;
+       db->findzone_q = NULL;
+       db->countzone_q = NULL;
+       db->lookup_q = NULL;
+
+       /* attach to the memory context */
+       isc_mem_attach(mctx, &db->mctx);
+
+       /* initialize the reference count mutex */
+       result = isc_mutex_init(&db->instance_lock);
+       if (result != ISC_R_SUCCESS) {
+               UNEXPECTED_ERROR(__FILE__, __LINE__,
+                                "isc_mutex_init() failed: %s",
+                                isc_result_totext(result));
+               goto cleanup;
+       }
+
+       /* build the all nodes query list */
+       result = build_querylist(mctx, allnodes_str, &db->zone,
+                                &db->record, &db->client,
+                                &db->allnodes_q, SDLZH_REQUIRE_ZONE);
+       /* if unsuccessful, log err msg and cleanup */
+       if (result != ISC_R_SUCCESS) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Could not build all nodes query list");
+               goto cleanup;
+       }
+
+       /* build the allow zone transfer query list */
+       result = build_querylist(mctx, allowxfr_str, &db->zone,
+                                &db->record, &db->client,
+                                &db->allowxfr_q,
+                                SDLZH_REQUIRE_ZONE | SDLZH_REQUIRE_CLIENT);
+       /* if unsuccessful, log err msg and cleanup */
+       if (result != ISC_R_SUCCESS) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Could not build allow xfr query list");
+               goto cleanup;
+       }
+
+       /* build the authority query, query list */
+       result = build_querylist(mctx, authority_str, &db->zone,
+                                &db->record, &db->client,
+                                &db->authority_q, SDLZH_REQUIRE_ZONE);
+       /* if unsuccessful, log err msg and cleanup */
+       if (result != ISC_R_SUCCESS) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Could not build authority query list");
+               goto cleanup;
+       }
+
+       /* build findzone query, query list */
+       result = build_querylist(mctx, findzone_str, &db->zone,
+                                &db->record, &db->client,
+                                &db->findzone_q, SDLZH_REQUIRE_ZONE);
+       /* if unsuccessful, log err msg and cleanup */
+       if (result != ISC_R_SUCCESS) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Could not build find zone query list");
+               goto cleanup;
+       }
+
+       /* build countzone query, query list */
+       result = build_querylist(mctx, countzone_str, &db->zone,
+                                &db->record, &db->client,
+                                &db->countzone_q, SDLZH_REQUIRE_ZONE);
+       /* if unsuccessful, log err msg and cleanup */
+       if (result != ISC_R_SUCCESS) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Could not build count zone query list");
+               goto cleanup;
+       }
+
+       /* build lookup query, query list */
+       result = build_querylist(mctx, lookup_str, &db->zone,
+                                &db->record, &db->client,
+                                &db->lookup_q, SDLZH_REQUIRE_RECORD);
+       /* if unsuccessful, log err msg and cleanup */
+       if (result != ISC_R_SUCCESS) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "Could not build lookup query list");
+               goto cleanup;
+       }
+
+       /* pass back the db instance */
+       *dbi = (dbinstance_t *) db;
+
+       /* return success */
+       return (ISC_R_SUCCESS);
+
+ cleanup:
+       /* destroy whatever was build of the db instance */
+       destroy_sqldbinstance(db);
+       /* return failure */
+       return (ISC_R_FAILURE);
+}
+
+void
+sdlzh_destroy_sqldbinstance(dbinstance_t *dbi)
+{
+       isc_mem_t *mctx;
+
+       /* save mctx for later */
+       mctx = dbi->mctx;
+
+       /* destroy any query lists we created */
+       destroy_querylist(mctx, &dbi->allnodes_q);
+       destroy_querylist(mctx, &dbi->allowxfr_q);
+       destroy_querylist(mctx, &dbi->authority_q);
+       destroy_querylist(mctx, &dbi->findzone_q);
+       destroy_querylist(mctx, &dbi->countzone_q);
+       destroy_querylist(mctx, &dbi->lookup_q);
+
+       /* get rid of the mutex */
+       isc_mutex_destroy(&dbi->instance_lock);
+
+       /* return, and detach the memory */
+       isc_mem_put(mctx, dbi, sizeof(dbinstance_t));
+       isc_mem_detach(&mctx);
+}
+
+char *
+sdlzh_get_parameter_value(isc_mem_t *mctx, const char *input, const char* key)
+{
+       int keylen;
+       char *keystart;
+       char value[255];
+       int i;
+
+       if (key == NULL || input == NULL || strlen(input) < 1)
+               return NULL;
+
+       keylen = strlen(key);
+
+       if (keylen < 1)
+               return NULL;
+
+       keystart = strstr(input, key);
+
+       if (keystart == NULL)
+               return NULL;
+
+       REQUIRE(mctx != NULL);
+
+       for (i = 0; i < 255; i++) {
+               value[i] = keystart[keylen + i];
+               if (value[i] == ' ' || value[i] == '\0') {
+                       value[i] = '\0';
+                       break;
+               }
+       }
+
+       return isc_mem_strdup(mctx, value);
+}
+
+#endif
index ed7c667436cd0ec5635f3bbb10ca0917e35a8b49..e35ee77962e09fc4a3b71edf730c4b1c35da7787 100644 (file)
@@ -13,7 +13,7 @@
 # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 # PERFORMANCE OF THIS SOFTWARE.
 
-# $Id: Makefile.in,v 1.151 2005/07/18 23:59:36 marka Exp $
+# $Id: Makefile.in,v 1.152 2005/09/05 00:11:01 marka Exp $
 
 srcdir =       @srcdir@
 VPATH =                @srcdir@
@@ -52,14 +52,15 @@ DSTOBJS =   dst_api.@O@ dst_lib.@O@ dst_parse.@O@ dst_result.@O@ \
 DNSOBJS =      acache.@O@ acl.@O@ adb.@O@ byaddr.@O@ \
                cache.@O@ callbacks.@O@ compress.@O@ \
                db.@O@ dbiterator.@O@ dbtable.@O@ diff.@O@ dispatch.@O@ \
-               dnssec.@O@ ds.@O@ forward.@O@ journal.@O@ keytable.@O@ \
+               dlz.@O@ dnssec.@O@ ds.@O@ forward.@O@ journal.@O@ keytable.@O@ \
                lib.@O@ log.@O@ lookup.@O@ \
                master.@O@ masterdump.@O@ message.@O@ \
                name.@O@ ncache.@O@ nsec.@O@ order.@O@ peer.@O@ portlist.@O@ \
                rbt.@O@ rbtdb.@O@ rbtdb64.@O@ rcode.@O@ rdata.@O@ \
                rdatalist.@O@ \
                rdataset.@O@ rdatasetiter.@O@ rdataslab.@O@ request.@O@ \
-               resolver.@O@ result.@O@ rootns.@O@ sdb.@O@ soa.@O@ ssu.@O@ \
+               resolver.@O@ result.@O@ rootns.@O@ sdb.@O@ sdlz.@O@ \
+               soa.@O@ ssu.@O@ \
                stats.@O@ tcpmsg.@O@ time.@O@ timer.@O@ tkey.@O@ \
                tsig.@O@ ttl.@O@ validator.@O@ \
                version.@O@ view.@O@ xfrin.@O@ zone.@O@ zonekey.@O@ zt.@O@
@@ -76,14 +77,15 @@ DSTSRCS =   dst_api.c dst_lib.c dst_parse.c \
 DNSSRCS =      acache.c acl.c adb.c byaddr.c \
                cache.c callbacks.c compress.c \
                db.c dbiterator.c dbtable.c diff.c dispatch.c \
-               dnssec.c ds.c forward.c journal.c keytable.c \
+               dlz.c dnssec.c ds.c forward.c journal.c keytable.c \
                lib.c log.c lookup.c \
                master.c masterdump.c message.c \
                name.c ncache.c nsec.c order.c peer.c portlist.c \
                rbt.c rbtdb.c rbtdb64.c rcode.c rdata.c \
                rdatalist.c \
                rdataset.c rdatasetiter.c rdataslab.c request.c \
-               resolver.c result.c rootns.c sdb.c soa.c ssu.c \
+               resolver.c result.c rootns.c sdb.c sdlz.c \
+               soa.c ssu.c \
                stats.c tcpmsg.c time.c timer.c tkey.c \
                tsig.c ttl.c validator.c \
                version.c view.c xfrin.c zone.c zonekey.c zt.c ${OTHERSRCS}
diff --git a/lib/dns/dlz.c b/lib/dns/dlz.c
new file mode 100644 (file)
index 0000000..ffd2436
--- /dev/null
@@ -0,0 +1,510 @@
+/*
+ * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
+ *
+ * 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 STICHTING NLNET
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * STICHTING NLNET 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 development of Dynamically Loadable Zones (DLZ) for Bind 9 was
+ * conceived and contributed by Rob Butler.
+ *
+ * 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 ROB BUTLER
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * ROB BUTLER 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.
+ */
+
+/*
+ * Copyright (C) 1999-2001  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: dlz.c,v 1.2 2005/09/05 00:11:01 marka Exp $ */
+
+/*! \file */
+
+/***
+ *** Imports
+ ***/
+
+#include <config.h>
+
+#include <dns/fixedname.h>
+#include <dns/log.h>
+#include <dns/master.h>
+#include <dns/dlz.h>
+
+
+#include <isc/buffer.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/once.h>
+#include <isc/rwlock.h>
+#include <isc/string.h>
+#include <isc/util.h>
+
+/***
+ *** Supported DLZ DB Implementations Registry
+ ***/
+
+static ISC_LIST(dns_dlzimplementation_t) dlz_implementations;
+static isc_rwlock_t dlz_implock;
+static isc_once_t once = ISC_ONCE_INIT;
+
+static void
+dlz_initialize(void) {
+       RUNTIME_CHECK(isc_rwlock_init(&dlz_implock, 0, 0) == ISC_R_SUCCESS);
+       ISC_LIST_INIT(dlz_implementations);
+}
+
+/*%
+ * Searches the dlz_implementations list for a driver matching name.
+ */
+static inline dns_dlzimplementation_t *
+dlz_impfind(const char *name) {
+       dns_dlzimplementation_t *imp;
+
+       for (imp = ISC_LIST_HEAD(dlz_implementations);
+            imp != NULL;
+            imp = ISC_LIST_NEXT(imp, link))
+               if (strcasecmp(name, imp->name) == 0)
+                       return (imp);
+       return (NULL);
+}
+
+/***
+ *** Basic DLZ Methods
+ ***/
+
+isc_result_t
+dns_dlzallowzonexfr(dns_view_t *view, dns_name_t *name,
+                   isc_sockaddr_t *clientaddr, dns_db_t **dbp)
+{
+       isc_result_t result;
+       dns_dlzallowzonexfr_t allowzonexfr;
+       dns_dlzdb_t *dlzdatabase;
+
+       /*
+        * Performs checks to make sure data is as we expect it to be.
+        */
+       REQUIRE(DNS_DLZ_VALID(view->dlzdatabase));
+       REQUIRE(name != NULL);
+       REQUIRE(dbp != NULL && *dbp == NULL);
+
+       /* ask driver if the zone is supported */
+       dlzdatabase = view->dlzdatabase;
+       allowzonexfr = dlzdatabase->implementation->methods->allowzonexfr;
+       result = (*allowzonexfr)(dlzdatabase->implementation->driverarg,
+                                dlzdatabase->dbdata, dlzdatabase->mctx,
+                                view->rdclass, name, clientaddr, dbp);
+
+       if (result == ISC_R_NOTIMPLEMENTED)
+               return (ISC_R_NOTFOUND);
+       return (result);
+}
+
+isc_result_t
+dns_dlzcreate(isc_mem_t *mctx, const char *dlzname, const char *drivername,
+             unsigned int argc, char *argv[], dns_dlzdb_t **dbp)
+{
+       dns_dlzimplementation_t *impinfo;
+       isc_result_t result;
+
+       /*
+        * initialize the dlz_implementations list, this is guaranteed
+        * to only really happen once.
+        */
+       RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS);
+
+       /*
+        * Performs checks to make sure data is as we expect it to be.
+        */
+       REQUIRE(dbp != NULL && *dbp == NULL);
+       REQUIRE(dlzname != NULL);
+       REQUIRE(drivername != NULL);
+       REQUIRE(mctx != NULL);
+
+       /* write log message */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_INFO,
+                     "Loading '%s' using driver %s", dlzname, drivername);
+
+       /* lock the dlz_implementations list so we can search it. */
+       RWLOCK(&dlz_implock, isc_rwlocktype_read);
+
+       /* search for the driver implementation  */
+       impinfo = dlz_impfind(drivername);
+       if (impinfo == NULL) {
+               RWUNLOCK(&dlz_implock, isc_rwlocktype_read);
+
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "unsupported DLZ database driver '%s'."
+                             "  %s not loaded.",
+                             drivername, dlzname);
+
+               return (ISC_R_NOTFOUND);
+       }
+
+       /* Allocate memory to hold the DLZ database driver */
+       (*dbp) = isc_mem_get(mctx, sizeof(dns_dlzdb_t));
+       if ((*dbp) == NULL) {
+               RWUNLOCK(&dlz_implock, isc_rwlocktype_read);
+               return (ISC_R_NOMEMORY);
+       }
+
+       /* Make sure memory region is set to all 0's */
+       memset((*dbp), 0, sizeof(dns_dlzdb_t));
+
+       (*dbp)->implementation = impinfo;
+
+       /* Create a new database using implementation 'drivername'. */
+       result = ((impinfo->methods->create)(mctx, dlzname, argc, argv,
+                                            impinfo->driverarg,
+                                            &(*dbp)->dbdata));
+
+       /* mark the DLZ driver as valid */
+       if (result == ISC_R_SUCCESS) {
+               RWUNLOCK(&dlz_implock, isc_rwlocktype_read);
+               (*dbp)->magic = DNS_DLZ_MAGIC;
+               isc_mem_attach(mctx, &(*dbp)->mctx);
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                             "DLZ driver loaded successfully.");
+               return (ISC_R_SUCCESS);
+       } else {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "DLZ driver failed to load.");
+       }
+
+       /* impinfo->methods->create failed. */
+       RWUNLOCK(&dlz_implock, isc_rwlocktype_read);
+       isc_mem_put(mctx, (*dbp), sizeof(dns_dlzdb_t));
+       return (result);
+}
+
+void
+dns_dlzdestroy(dns_dlzdb_t **dbp) {
+       isc_mem_t *mctx;
+       dns_dlzdestroy_t destroy;
+
+       /* Write debugging message to log */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                     "Unloading DLZ driver.");
+
+       /*
+        * Perform checks to make sure data is as we expect it to be.
+        */
+       REQUIRE(dbp != NULL && DNS_DLZ_VALID(*dbp));
+
+       /* call the drivers destroy method */
+       if ((*dbp) != NULL) {
+               mctx = (*dbp)->mctx;
+               destroy = (*dbp)->implementation->methods->destroy;
+               (*destroy)((*dbp)->implementation->driverarg,(*dbp)->dbdata);
+               /* return memory */
+               isc_mem_put(mctx, (*dbp), sizeof(dns_dlzdb_t));
+               isc_mem_detach(&mctx);
+       }
+
+       *dbp = NULL;
+}
+
+
+isc_result_t
+dns_dlzfindzone(dns_view_t *view, dns_name_t *name, unsigned int minlabels,
+               dns_db_t **dbp)
+{
+       dns_fixedname_t fname;
+       dns_name_t *zonename;
+       unsigned int namelabels;
+       unsigned int i;
+       isc_result_t result;
+       dns_dlzfindzone_t findzone;
+       dns_dlzdb_t *dlzdatabase;
+
+       /*
+        * Performs checks to make sure data is as we expect it to be.
+        */
+       REQUIRE(DNS_DLZ_VALID(view->dlzdatabase));
+       REQUIRE(name != NULL);
+       REQUIRE(dbp != NULL && *dbp == NULL);
+
+       /* setup a "fixed" dns name */
+       dns_fixedname_init(&fname);
+       zonename = dns_fixedname_name(&fname);
+
+       /* count the number of labels in the name */
+       namelabels = dns_name_countlabels(name);
+
+       /*
+        * loop through starting with the longest domain name and
+        * trying shorter names portions of the name until we find a
+        * match, have an error, or are below the 'minlabels'
+        * threshold.  minlabels is 0, if the standard database didn't
+        * have a zone name match.  Otherwise minlables is the number
+        * of labels in that name.  We need to beat that for a
+        * "better" match for the DLZ database to be authoritative
+        * instead of the standard database.
+        */
+       for (i = namelabels; i > minlabels && i > 1; i--) {
+               if (i == namelabels) {
+                       result = dns_name_copy(name, zonename, NULL);
+                       if (result != ISC_R_SUCCESS)
+                               return (result);
+               } else
+                       dns_name_split(name, i, NULL, zonename);
+
+               /* ask SDLZ driver if the zone is supported */
+               dlzdatabase = view->dlzdatabase;
+               findzone = dlzdatabase->implementation->methods->findzone;
+               result = (*findzone)(dlzdatabase->implementation->driverarg,
+                                    dlzdatabase->dbdata, dlzdatabase->mctx,
+                                    view->rdclass, zonename, dbp);
+               if (result != ISC_R_NOTFOUND)
+                       return (result);
+       }
+       return (ISC_R_NOTFOUND);
+}
+
+/*%
+ * Registers a DLZ driver.  This basically just adds the dlz
+ * driver to the list of available drivers in the dlz_implementations list.
+ */
+isc_result_t
+dns_dlzregister(const char *drivername, const dns_dlzmethods_t *methods,
+               void *driverarg, isc_mem_t *mctx,
+               dns_dlzimplementation_t **dlzimp)
+{
+
+       dns_dlzimplementation_t *dlz_imp;
+
+       /* Write debugging message to log */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                     "Registering DLZ driver '%s'", drivername);
+
+       /*
+        * Performs checks to make sure data is as we expect it to be.
+        */
+       REQUIRE(drivername != NULL);
+       REQUIRE(methods != NULL);
+       REQUIRE(methods->create != NULL);
+       REQUIRE(methods->destroy != NULL);
+       REQUIRE(methods->findzone != NULL);
+       REQUIRE(mctx != NULL);
+       REQUIRE(dlzimp != NULL && *dlzimp == NULL);
+
+       /*
+        * initialize the dlz_implementations list, this is guaranteed
+        * to only really happen once.
+        */
+       RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS);
+
+       /* lock the dlz_implementations list so we can modify it. */
+       RWLOCK(&dlz_implock, isc_rwlocktype_write);
+
+       /*
+        * check that another already registered driver isn't using
+        * the same name
+        */
+       dlz_imp = dlz_impfind(drivername);
+       if (dlz_imp != NULL) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                             "DLZ Driver '%s' already registered",
+                             drivername);
+               RWUNLOCK(&dlz_implock, isc_rwlocktype_write);
+               return (ISC_R_EXISTS);
+       }
+
+       /*
+        * Allocate memory for a dlz_implementation object.  Error if
+        * we cannot.
+        */
+       dlz_imp = isc_mem_get(mctx, sizeof(dns_dlzimplementation_t));
+       if (dlz_imp == NULL) {
+               RWUNLOCK(&dlz_implock, isc_rwlocktype_write);
+               return (ISC_R_NOMEMORY);
+       }
+
+       /* Make sure memory region is set to all 0's */
+       memset(dlz_imp, 0, sizeof(dns_dlzimplementation_t));
+
+       /* Store the data passed into this method */
+       dlz_imp->name = drivername;
+       dlz_imp->methods = methods;
+       dlz_imp->mctx = NULL;
+       dlz_imp->driverarg = driverarg;
+
+       /* attach the new dlz_implementation object to a memory context */
+       isc_mem_attach(mctx, &dlz_imp->mctx);
+
+       /*
+        * prepare the dlz_implementation object to be put in a list,
+        * and append it to the list
+        */
+       ISC_LINK_INIT(dlz_imp, link);
+       ISC_LIST_APPEND(dlz_implementations, dlz_imp, link);
+
+       /* Unlock the dlz_implementations list.  */
+       RWUNLOCK(&dlz_implock, isc_rwlocktype_write);
+
+       /* Pass back the dlz_implementation that we created. */
+       *dlzimp = dlz_imp;
+
+       return (ISC_R_SUCCESS);
+}
+
+/*%
+ * Helper function for dns_dlzstrtoargv().
+ * Pardon the gratuitous recursion.
+ */
+static isc_result_t
+dns_dlzstrtoargvsub(isc_mem_t *mctx, char *s, unsigned int *argcp,
+                   char ***argvp, unsigned int n)
+{
+       isc_result_t result;
+
+ restart:
+       /* Discard leading whitespace. */
+       while (*s == ' ' || *s == '\t')
+               s++;
+
+       if (*s == '\0') {
+               /* We have reached the end of the string. */
+               *argcp = n;
+               *argvp = isc_mem_get(mctx, n * sizeof(char *));
+               if (*argvp == NULL)
+                       return (ISC_R_NOMEMORY);
+       } else {
+               char *p = s;
+               while (*p != ' ' && *p != '\t' && *p != '\0' && *p != '{') {
+                       if (*p == '\n') {
+                               *p = ' ';
+                               goto restart;
+                       }
+                       p++;
+               }
+
+               /* do "grouping", items between { and } are one arg */
+               if (*p == '{') {
+                       char *t = p;
+                       /*
+                        * shift all characters to left by 1 to get rid of '{'
+                        */
+                       while (*t != '\0') {
+                               t++;
+                               *(t-1) = *t;
+                       }
+                       while (*p != '\0' && *p != '}') {
+                               p++;
+                       }
+                       /* get rid of '}' character */
+                       if (*p == '}') {
+                               *p = '\0';
+                               p++;
+                       }
+                       /* normal case, no "grouping" */
+               } else if (*p != '\0')
+                       *p++ = '\0';
+
+               result = dns_dlzstrtoargvsub(mctx, p, argcp, argvp, n + 1);
+               if (result != ISC_R_SUCCESS)
+                       return (result);
+               (*argvp)[n] = s;
+       }
+       return (ISC_R_SUCCESS);
+}
+
+/*%
+ * Tokenize the string "s" into whitespace-separated words,
+ * return the number of words in '*argcp' and an array
+ * of pointers to the words in '*argvp'.  The caller
+ * must free the array using isc_mem_put().  The string
+ * is modified in-place.
+ */
+isc_result_t
+dns_dlzstrtoargv(isc_mem_t *mctx, char *s,
+                unsigned int *argcp, char ***argvp)
+{
+       return(dns_dlzstrtoargvsub(mctx, s, argcp, argvp, 0));
+}
+
+/*%
+ * Unregisters a DLZ driver.  This basically just removes the dlz
+ * driver from the list of available drivers in the dlz_implementations list.
+ */
+void
+dns_dlzunregister(dns_dlzimplementation_t **dlzimp) {
+       dns_dlzimplementation_t *dlz_imp;
+       isc_mem_t *mctx;
+
+       /* Write debugging message to log */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                     "Unregistering DLZ driver.");
+
+       /*
+        * Performs checks to make sure data is as we expect it to be.
+        */
+       REQUIRE(dlzimp != NULL && *dlzimp != NULL);
+
+       /*
+        * initialize the dlz_implementations list, this is guaranteed
+        * to only really happen once.
+        */
+       RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS);
+
+       dlz_imp = *dlzimp;
+
+       /* lock the dlz_implementations list so we can modify it. */
+       RWLOCK(&dlz_implock, isc_rwlocktype_write);
+
+       /* remove the dlz_implementation object from the list */
+       ISC_LIST_UNLINK(dlz_implementations, dlz_imp, link);
+       mctx = dlz_imp->mctx;
+
+       /*
+        * return the memory back to the available memory pool and
+        * remove it from the memory context.
+        */
+       isc_mem_put(mctx, dlz_imp, sizeof(dns_dlzimplementation_t));
+       isc_mem_detach(&mctx);
+
+       /* Unlock the dlz_implementations list. */
+       RWUNLOCK(&dlz_implock, isc_rwlocktype_write);
+}
diff --git a/lib/dns/include/dns/dlz.h b/lib/dns/include/dns/dlz.h
new file mode 100644 (file)
index 0000000..e228ea2
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
+ *
+ * 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 STICHTING NLNET
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * STICHTING NLNET 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 development of Dynamically Loadable Zones (DLZ) for Bind 9 was
+ * conceived and contributed by Rob Butler.
+ *
+ * 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 ROB BUTLER
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * ROB BUTLER 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.
+ */
+
+/*
+ * Copyright (C) 1999-2001  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: dlz.h,v 1.2 2005/09/05 00:11:02 marka Exp $ */
+
+/*! \file */
+
+#ifndef DLZ_H
+#define DLZ_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*
+ * DLZ Interface
+ *
+ * The DLZ interface allows zones to be looked up using a driver instead of
+ * Bind's default in memory zone table.
+ *
+ *
+ * Reliability:
+ *     No anticipated impact.
+ *
+ * Resources:
+ *
+ * Security:
+ *     No anticipated impact.
+ *
+ * Standards:
+ *     None.
+ */
+
+/*****
+ ***** Imports
+ *****/
+
+#include <dns/name.h>
+#include <dns/types.h>
+#include <dns/view.h>
+
+#include <isc/lang.h>
+
+ISC_LANG_BEGINDECLS
+
+/***
+ *** Types
+ ***/
+
+#define DNS_DLZ_MAGIC          ISC_MAGIC('D','L','Z','D')
+#define DNS_DLZ_VALID(dlz)     ISC_MAGIC_VALID(dlz, DNS_DLZ_MAGIC)
+
+typedef isc_result_t
+(*dns_dlzallowzonexfr_t)(void *driverarg, void *dbdata, isc_mem_t *mctx,
+                        dns_rdataclass_t rdclass, dns_name_t *name,
+                        isc_sockaddr_t *clientaddr,
+                        dns_db_t **dbp);
+
+/*%<
+ * Method prototype.  Drivers implementing the DLZ interface MUST
+ * supply an allow zone transfer method.  This method is called when
+ * the DNS server is performing a zone transfer query.  The driver's
+ * method should return ISC_R_SUCCESS and a database pointer to the
+ * name server if the zone is supported by the database, and zone
+ * transfer is allowed.  Otherwise it will return ISC_R_NOTFOUND if
+ * the zone is not supported by the database, or ISC_R_NOPERM if zone
+ * transfers are not allowed.  If an error occurs it should return a
+ * result code indicating the type of error.
+ */
+
+typedef isc_result_t
+(*dns_dlzcreate_t)(isc_mem_t *mctx, const char *dlzname, unsigned int argc,
+                  char *argv[], void *driverarg, void **dbdata);
+
+/*%<
+ * Method prototype.  Drivers implementing the DLZ interface MUST
+ * supply a create method.  This method is called when the DNS server
+ * is starting up and creating drivers for use later.
+ */
+
+typedef void
+(*dns_dlzdestroy_t)(void *driverarg, void **dbdata);
+
+/*%<
+ * Method prototype.  Drivers implementing the DLZ interface MUST
+ * supply a destroy method.  This method is called when the DNS server
+ * is shuting down and no longer needs the driver.
+ */
+
+typedef isc_result_t
+(*dns_dlzfindzone_t)(void *driverarg, void *dbdata, isc_mem_t *mctx,
+                    dns_rdataclass_t rdclass, dns_name_t *name,
+                    dns_db_t **dbp);
+
+/*%<
+
+ * Method prototype.  Drivers implementing the DLZ interface MUST
+ * supply a find zone method.  This method is called when the DNS
+ * server is performing a query.  The find zone method will be called
+ * with the longest possible name first, and continue to be called
+ * with successively shorter domain names, until any of the following
+ * occur:
+ *
+ * \li 1) a match is found, and the function returns (ISC_R_SUCCESS)
+ *
+ * \li 2) a problem occurs, and the functions returns anything other
+ *        than (ISC_R_NOTFOUND)
+ * \li 3) we run out of domain name labels. I.E. we have tried the
+ *        shortest domain name
+ * \li 4) the number of labels in the domain name is less than
+ *        min_lables for dns_dlzfindzone
+ *
+ * The driver's find zone method should return ISC_R_SUCCESS and a
+ * database pointer to the name server if the zone is supported by the
+ * database.  Otherwise it will return ISC_R_NOTFOUND, and a null
+ * pointer if the zone is not supported.  If an error occurs it should
+ * return a result code indicating the type of error.
+ */
+
+/*% the methods supplied by a DLZ driver */
+typedef struct dns_dlzmethods {
+       dns_dlzcreate_t         create;
+       dns_dlzdestroy_t        destroy;
+       dns_dlzfindzone_t       findzone;
+       dns_dlzallowzonexfr_t   allowzonexfr;
+} dns_dlzmethods_t;
+
+/*% information about a DLZ driver */
+struct dns_dlzimplementation {
+       const char                              *name;
+       const dns_dlzmethods_t                  *methods;
+       isc_mem_t                               *mctx;
+       void                                    *driverarg;
+       ISC_LINK(dns_dlzimplementation_t)       link;
+};
+
+/*% an instance of a DLZ driver */
+struct dns_dlzdb {
+       unsigned int            magic;
+       isc_mem_t               *mctx;
+       dns_dlzimplementation_t *implementation;
+       void                    *dbdata;
+};
+
+
+/***
+ *** Method declarations
+ ***/
+
+isc_result_t
+dns_dlzallowzonexfr(dns_view_t *view, dns_name_t *name,
+                   isc_sockaddr_t *clientaddr, dns_db_t **dbp);
+
+/*%<
+ * This method is called when the DNS server is performing a zone
+ * transfer query.  It will call the DLZ driver's allow zone tranfer
+ * method.
+ */
+
+isc_result_t
+dns_dlzcreate(isc_mem_t *mctx, const char *dlzname,
+             const char *drivername, unsigned int argc,
+             char *argv[], dns_dlzdb_t **dbp);
+
+/*%<
+ * This method is called when the DNS server is starting up and
+ * creating drivers for use later.  It will search the DLZ driver list
+ * for 'drivername' and return a DLZ driver via dbp if a match is
+ * found.  If the DLZ driver supplies a create method, this function
+ * will call it.
+ */
+
+void
+dns_dlzdestroy(dns_dlzdb_t **dbp);
+
+/*%<
+ * This method is called when the DNS server is shuting down and no
+ * longer needs the driver.  If the DLZ driver supplies a destroy
+ * methods, this function will call it.
+ */
+
+isc_result_t
+dns_dlzfindzone(dns_view_t *view, dns_name_t *name,
+               unsigned int minlabels, dns_db_t **dbp);
+
+/*%<
+ * This method is called when the DNS server is performing a query.
+ * It will call the DLZ driver's find zone method.
+ */
+
+isc_result_t
+dns_dlzregister(const char *drivername, const dns_dlzmethods_t *methods,
+                void *driverarg, isc_mem_t *mctx,
+               dns_dlzimplementation_t **dlzimp);
+
+/*%<
+ * Register a dynamically loadable zones (DLZ) driver for the database
+ * type 'drivername', implemented by the functions in '*methods'.
+ *
+ * dlzimp must point to a NULL dlz_implementation_t pointer.  That is,
+ * dlzimp != NULL && *dlzimp == NULL.  It will be assigned a value that
+ * will later be used to identify the driver when deregistering it.
+ */
+
+isc_result_t
+dns_dlzstrtoargv(isc_mem_t *mctx, char *s, unsigned int *argcp, char ***argvp);
+
+/*%<
+ * This method is called when the name server is starting up to parse
+ * the DLZ driver command line from named.conf.  Basically it splits
+ * up a string into and argc / argv.  The primary difference of this
+ * method is items between braces { } are considered only 1 word.  for
+ * example the command line "this is { one grouped phrase } and this
+ * isn't" would be parsed into:
+ *
+ * \li argv[0]: "this"
+ * \li argv[1]: "is"
+ * \li argv{2]: " one grouped phrase "
+ * \li argv[3]: "and"
+ * \li argv[4]: "this"
+ * \li argv{5}: "isn't"
+ *
+ * braces should NOT be nested, more than one grouping in the command
+ * line is allowed.  Notice, argv[2] has an extra space at the
+ * beginning and end.  Extra spaces are not stripped between a
+ * grouping.  You can do so in your driver if needed, or be sure not
+ * to put extra spaces before / after the braces.
+ */
+
+void
+dns_dlzunregister(dns_dlzimplementation_t **dlzimp);
+
+/*%<
+ * Removes the dlz driver from the list of registered dlz drivers.
+ * There must be no active dlz drivers of this type when this function
+ * is called.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* DLZ_H */
index 312bcc68952687c3899ee09b6e5e84f7aff0288e..738b293fcf19eaf9db6afa542556a4f55cd59eb1 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: log.h,v 1.36 2005/04/29 00:22:59 marka Exp $ */
+/* $Id: log.h,v 1.37 2005/09/05 00:11:02 marka Exp $ */
 
 /*! \file
  * \author  Principal Authors: DCL */
@@ -71,6 +71,7 @@ LIBDNS_EXTERNAL_DATA extern isc_logmodule_t dns_modules[];
 #define DNS_LOGMODULE_DIFF             (&dns_modules[23])
 #define DNS_LOGMODULE_HINTS            (&dns_modules[24])
 #define DNS_LOGMODULE_ACACHE           (&dns_modules[25])
+#define DNS_LOGMODULE_DLZ              (&dns_modules[26])
 
 ISC_LANG_BEGINDECLS
 
index 656ac08857381995d794456e482d11f227fae1ee..586c4ef931eb98be65a38bcfd6c7bb9d7baaf315 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: name.h,v 1.116 2005/05/19 04:59:04 marka Exp $ */
+/* $Id: name.h,v 1.117 2005/09/05 00:11:03 marka Exp $ */
 
 #ifndef DNS_NAME_H
 #define DNS_NAME_H 1
@@ -264,7 +264,6 @@ dns_name_hasbuffer(const dns_name_t *name);
  * \li ISC_FALSE       'name' does not have a dedicated buffer.
  */
 
-
 /***
  *** Properties
  ***/
@@ -958,9 +957,9 @@ dns_name_split(dns_name_t *name, unsigned int suffixlabels,
  * Requires:
  *\li  'name' is a valid name.
  *
- * \li 'suffixlabels' cannot exceed the number of labels in 'name'.
+ *\li  'suffixlabels' cannot exceed the number of labels in 'name'.
  *
- *\li  'prefix' is a valid name or NULL, and cannot be read-only.
+ * \li 'prefix' is a valid name or NULL, and cannot be read-only.
  *
  *\li  'suffix' is a valid name or NULL, and cannot be read-only.
  *
diff --git a/lib/dns/include/dns/sdlz.h b/lib/dns/include/dns/sdlz.h
new file mode 100644 (file)
index 0000000..61ae8ad
--- /dev/null
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
+ *
+ * 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 STICHTING NLNET
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * STICHTING NLNET 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 development of Dynamically Loadable Zones (DLZ) for Bind 9 was
+ * conceived and contributed by Rob Butler.
+ *
+ * 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 ROB BUTLER
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * ROB BUTLER 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.
+ */
+
+/*
+ * Copyright (C) 1999-2001  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: sdlz.h,v 1.2 2005/09/05 00:11:03 marka Exp $ */
+
+/*! \file */
+
+#ifndef SDLZ_H
+#define SDLZ_H 1
+
+#include <dns/dlz.h>
+
+ISC_LANG_BEGINDECLS
+
+#define DNS_SDLZFLAG_THREADSAFE                0x00000001U
+#define DNS_SDLZFLAG_RELATIVEOWNER     0x00000002U
+#define DNS_SDLZFLAG_RELATIVERDATA     0x00000004U
+
+ /* A simple DLZ database. */
+typedef struct dns_sdlz_db dns_sdlz_db_t;
+
+ /* A simple DLZ database lookup in progress. */
+typedef struct dns_sdlzlookup dns_sdlzlookup_t;
+
+ /* A simple DLZ database traversal in progress. */
+typedef struct dns_sdlzallnodes dns_sdlzallnodes_t;
+
+
+typedef isc_result_t
+(*dns_sdlzallnodesfunc_t)(const char *zone, void *driverarg, void *dbdata,
+                         dns_sdlzallnodes_t *allnodes);
+
+/*%<
+ * Method prototype.  Drivers implementing the SDLZ interface may
+ * supply an all nodes method.  This method is called when the DNS
+ * server is performing a zone transfer query, after the allow zone
+ * transfer method has been called.  This method is only called if the
+ * allow zone transfer method returned ISC_R_SUCCESS.  This method and
+ * the allow zone transfer method are both required for zone transfers
+ * to be supported.  If the driver generates data dynamically (instead
+ * of searching in a database for it) it should not implement this
+ * function as a zone transfer would be meaningless.  A SDLZ driver
+ * does not have to implement an all nodes method.
+ */
+
+typedef isc_result_t
+(*dns_sdlzallowzonexfr_t)(void *driverarg, void *dbdata, const char *name,
+                         const char *client);
+
+/*%<
+ * Method prototype.  Drivers implementing the SDLZ interface may
+ * supply an allow zone transfer method.  This method is called when
+ * the DNS server is performing a zone transfer query, before the all
+ * nodes method can be called.  This method and the all node method
+ * are both required for zone transfers to be supported.  If the
+ * driver generates data dynamically (instead of searching in a
+ * database for it) it should not implement this function as a zone
+ * transfer would be meaningless.  A SDLZ driver does not have to
+ * implement an allow zone transfer method.
+ *
+ * This method should return ISC_R_SUCCESS if the zone is supported by
+ * the database and a zone transfer is allowed for the specified
+ * client.  If the zone is supported by the database, but zone
+ * transfers are not allowed for the specified client this method
+ * should return ISC_R_NOPERM..  Lastly the method should return
+ * ISC_R_NOTFOUND if the zone is not supported by the database.  If an
+ * error occurs it should return a result code indicating the type of
+ * error.
+ */
+
+typedef isc_result_t
+(*dns_sdlzauthorityfunc_t)(const char *zone, void *driverarg, void *dbdata,
+                          dns_sdlzlookup_t *lookup);
+
+/*%<
+ * Method prototype.  Drivers implementing the SDLZ interface may
+ * supply an authority method.  This method is called when the DNS
+ * server is performing a query, after both the find zone and lookup
+ * methods have been called.  This method is required if the lookup
+ * function does not supply authority information for the dns
+ * record. A SDLZ driver does not have to implement an authority
+ * method.
+ */
+
+typedef isc_result_t
+(*dns_sdlzcreate_t)(const char *dlzname, unsigned int argc, char *argv[],
+                   void *driverarg, void **dbdata);
+
+/*%<
+ * Method prototype.  Drivers implementing the SDLZ interface may
+ * supply a create method.  This method is called when the DNS server
+ * is starting up and creating drivers for use later. A SDLZ driver
+ * does not have to implement a create method.
+ */
+
+typedef void
+(*dns_sdlzdestroy_t)(void *driverarg, void *dbdata);
+
+/*%<
+ * Method prototype.  Drivers implementing the SDLZ interface may
+ * supply a destroy method.  This method is called when the DNS server
+ * is shuting down and no longer needs the driver.  A SDLZ driver does
+ * not have to implement a destroy method.
+ */
+
+typedef isc_result_t
+(*dns_sdlzfindzone_t)(void *driverarg, void *dbdata, const char *name);
+
+/*%<
+ * Method prototype.  Drivers implementing the SDLZ interface MUST
+ * supply a find zone method.  This method is called when the DNS
+ * server is performing a query to to determine if 'name' is a
+ * supported dns zone.  The find zone method will be called with the
+ * longest possible name first, and continue to be called with
+ * successively shorter domain names, until any of the following
+ * occur:
+ *
+ * \li 1) the function returns (ISC_R_SUCCESS) indicating a zone name
+ *        match.
+ *
+ * \li 2) a problem occurs, and the functions returns anything other than
+ *        (ISC_R_NOTFOUND)
+ *
+ * \li 3) we run out of domain name labels. I.E. we have tried the
+ *        shortest domain name
+ *
+ * \li 4) the number of labels in the domain name is less than min_lables
+ *        for dns_dlzfindzone
+ *
+ * The driver's find zone method should return ISC_R_SUCCESS if the
+ * zone is supported by the database.  Otherwise it should return
+ * ISC_R_NOTFOUND, if the zone is not supported.  If an error occurs
+ * it should return a result code indicating the type of error.
+ */
+
+typedef isc_result_t
+(*dns_sdlzlookupfunc_t)(const char *zone, const char *name, void *driverarg,
+                       void *dbdata, dns_sdlzlookup_t *lookup);
+
+/*%<
+ * Method prototype.  Drivers implementing the SDLZ interface MUST
+ * supply a lookup method.  This method is called when the DNS server
+ * is performing a query, after the find zone and before any other
+ * methods have been called.  This function returns record DNS record
+ * information using the dns_sdlz_putrr and dns_sdlz_putsoa functions.
+ * If this function supplies authority information for the DNS record
+ * the authority method is not required.  If it does not, the
+ * authority function is required.  A SDLZ driver must implement a
+ * lookup method.
+ */
+
+typedef struct dns_sdlzmethods {
+       dns_sdlzcreate_t        create;
+       dns_sdlzdestroy_t       destroy;
+       dns_sdlzfindzone_t      findzone;
+       dns_sdlzlookupfunc_t    lookup;
+       dns_sdlzauthorityfunc_t authority;
+       dns_sdlzallnodesfunc_t  allnodes;
+       dns_sdlzallowzonexfr_t  allowzonexfr;
+} dns_sdlzmethods_t;
+
+isc_result_t
+dns_sdlzregister(const char *drivername, const dns_sdlzmethods_t *methods,
+                void *driverarg, unsigned int flags, isc_mem_t *mctx,
+                dns_sdlzimplementation_t **sdlzimp);
+/*%<
+ * Register a dynamically loadable zones (dlz) driver for the database
+ * type 'drivername', implemented by the functions in '*methods'.
+ *
+ * sdlzimp must point to a NULL dns_sdlzimplementation_t pointer.
+ * That is, sdlzimp != NULL && *sdlzimp == NULL.  It will be assigned
+ * a value that will later be used to identify the driver when
+ * deregistering it.
+ */
+
+void
+dns_sdlzunregister(dns_sdlzimplementation_t **sdlzimp);
+
+/*%<
+ * Removes the sdlz driver from the list of registered sdlz drivers.
+ * There must be no active sdlz drivers of this type when this
+ * function is called.
+ */
+
+isc_result_t
+dns_sdlz_putnamedrr(dns_sdlzallnodes_t *allnodes, const char *name,
+                  const char *type, dns_ttl_t ttl, const char *data);
+/*%<
+ * Add a single resource record to the allnodes structure to be later
+ * parsed into a zone transfer response.
+ */
+
+isc_result_t
+dns_sdlz_putrr(dns_sdlzlookup_t *lookup, const char *type, dns_ttl_t ttl,
+             const char *data);
+/*%<
+ * Add a single resource record to the lookup structure to be later
+ * parsed into a query response.
+ */
+
+isc_result_t
+dns_sdlz_putsoa(dns_sdlzlookup_t *lookup, const char *mname, const char *rname,
+              isc_uint32_t serial);
+/*%<
+ * This function may optionally be called from the 'authority'
+ * callback to simplify construction of the SOA record for 'zone'.  It
+ * will provide a SOA listing 'mname' as as the master server and
+ * 'rname' as the responsible person mailbox.  It is the
+ * responsibility of the driver to increment the serial number between
+ * responses if necessary.  All other SOA fields will have reasonable
+ * default values.
+ */
+
+
+ISC_LANG_ENDDECLS
+
+#endif /* SDLZ_H */
index cf77d16a1d66582e45731c0efdb1c00b5b976c4a..5763fe65ccec9848af83f8abd8e6af996d1c3010 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: types.h,v 1.116 2005/06/20 01:03:54 marka Exp $ */
+/* $Id: types.h,v 1.117 2005/09/05 00:11:03 marka Exp $ */
 
 #ifndef DNS_TYPES_H
 #define DNS_TYPES_H 1
@@ -53,6 +53,9 @@ typedef void                                  dns_dbload_t;
 typedef void                                   dns_dbnode_t;
 typedef struct dns_dbtable                     dns_dbtable_t;
 typedef void                                   dns_dbversion_t;
+typedef struct dns_dlzimplementation           dns_dlzimplementation_t;
+typedef struct dns_dlzdb                       dns_dlzdb_t;
+typedef struct dns_sdlzimplementation          dns_sdlzimplementation_t;
 typedef struct dns_decompress                  dns_decompress_t;
 typedef struct dns_dispatch                    dns_dispatch_t;
 typedef struct dns_dispatchevent               dns_dispatchevent_t;
index 7cc4e89185f9cbe7b2f17c5566b03773a4b445a5..d967dcf9956c48c955d55097b9b7db63925991d9 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: view.h,v 1.96 2005/07/18 05:58:59 marka Exp $ */
+/* $Id: view.h,v 1.97 2005/09/05 00:11:04 marka Exp $ */
 
 #ifndef DNS_VIEW_H
 #define DNS_VIEW_H 1
@@ -84,6 +84,7 @@ struct dns_view {
        dns_rdataclass_t                rdclass;
        char *                          name;
        dns_zt_t *                      zonetable;
+       dns_dlzdb_t *                   dlzdatabase;
        dns_resolver_t *                resolver;
        dns_adb_t *                     adb;
        dns_requestmgr_t *              requestmgr;
index d2bc330aa1fbb33e6273311bfcc37ae156dad596..afaaef103803085278910f5e844236f173bf3057 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: log.c,v 1.39 2005/04/29 00:22:48 marka Exp $ */
+/* $Id: log.c,v 1.40 2005/09/05 00:11:01 marka Exp $ */
 
 /*! \file */
 
@@ -77,6 +77,7 @@ LIBDNS_EXTERNAL_DATA isc_logmodule_t dns_modules[] = {
        { "dns/diff",           0 },
        { "dns/hints",          0 },
        { "dns/acache",         0 },
+       { "dns/dlz",            0 },
        { NULL,                 0 }
 };
 
diff --git a/lib/dns/sdlz.c b/lib/dns/sdlz.c
new file mode 100644 (file)
index 0000000..a8d8adc
--- /dev/null
@@ -0,0 +1,1781 @@
+/*
+ * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
+ *
+ * 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 STICHTING NLNET
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * STICHTING NLNET 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 development of Dynamically Loadable Zones (DLZ) for Bind 9 was
+ * conceived and contributed by Rob Butler.
+ *
+ * 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 ROB BUTLER
+ * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+ * ROB BUTLER 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.
+ */
+
+/*
+ * Copyright (C) 1999-2001  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: sdlz.c,v 1.2 2005/09/05 00:11:02 marka Exp $ */
+
+/*! \file */
+
+#include <config.h>
+#include <string.h>
+
+#include <isc/buffer.h>
+#include <isc/lex.h>
+#include <isc/log.h>
+#include <isc/rwlock.h>
+#include <isc/string.h>
+#include <isc/util.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/once.h>
+#include <isc/print.h>
+#include <isc/region.h>
+
+#include <dns/callbacks.h>
+#include <dns/db.h>
+#include <dns/dbiterator.h>
+#include <dns/dlz.h>
+#include <dns/fixedname.h>
+#include <dns/log.h>
+#include <dns/rdata.h>
+#include <dns/rdatalist.h>
+#include <dns/rdataset.h>
+#include <dns/rdatasetiter.h>
+#include <dns/rdatatype.h>
+#include <dns/result.h>
+#include <dns/master.h>
+#include <dns/sdlz.h>
+#include <dns/types.h>
+
+#include "rdatalist_p.h"
+
+/*
+ * Private Types
+ */
+
+struct dns_sdlzimplementation {
+       const dns_sdlzmethods_t         *methods;
+       isc_mem_t                       *mctx;
+       void                            *driverarg;
+       unsigned int                    flags;
+       isc_mutex_t                     driverlock;
+       dns_dlzimplementation_t         *dlz_imp;
+};
+
+struct dns_sdlz_db {
+       /* Unlocked */
+       dns_db_t                        common;
+       void                            *dbdata;
+       dns_sdlzimplementation_t        *dlzimp;
+       isc_mutex_t                     refcnt_lock;
+       /* Locked */
+       unsigned int                    references;
+};
+
+struct dns_sdlzlookup {
+       /* Unlocked */
+       unsigned int                    magic;
+       dns_sdlz_db_t                   *sdlz;
+       ISC_LIST(dns_rdatalist_t)       lists;
+       ISC_LIST(isc_buffer_t)          buffers;
+       dns_name_t                      *name;
+       ISC_LINK(dns_sdlzlookup_t)      link;
+       isc_mutex_t                     lock;
+       dns_rdatacallbacks_t            callbacks;
+       /* Locked */
+       unsigned int                    references;
+};
+
+typedef struct dns_sdlzlookup dns_sdlznode_t;
+
+struct dns_sdlzallnodes {
+       dns_dbiterator_t                common;
+       ISC_LIST(dns_sdlznode_t)        nodelist;
+       dns_sdlznode_t                  *current;
+       dns_sdlznode_t                  *origin;
+};
+
+typedef dns_sdlzallnodes_t sdlz_dbiterator_t;
+
+typedef struct sdlz_rdatasetiter {
+       dns_rdatasetiter_t              common;
+       dns_rdatalist_t                 *current;
+} sdlz_rdatasetiter_t;
+
+
+#define SDLZDB_MAGIC           ISC_MAGIC('D', 'L', 'Z', 'S')
+
+/*
+ * Note that "impmagic" is not the first four bytes of the struct, so
+ * ISC_MAGIC_VALID cannot be used.
+ */
+
+#define VALID_SDLZDB(sdlzdb)   ((sdlzdb) != NULL && \
+                                (sdlzdb)->common.impmagic == SDLZDB_MAGIC)
+
+#define SDLZLOOKUP_MAGIC       ISC_MAGIC('D','L','Z','L')
+#define VALID_SDLZLOOKUP(sdlzl)        ISC_MAGIC_VALID(sdlzl, SDLZLOOKUP_MAGIC)
+#define VALID_SDLZNODE(sdlzn)  VALID_SDLZLOOKUP(sdlzn)
+
+/* These values are taken from RFC 1537 */
+#define SDLZ_DEFAULT_REFRESH   (60 * 60 * 8)
+#define SDLZ_DEFAULT_RETRY     (60 * 60 * 2)
+#define SDLZ_DEFAULT_EXPIRE    (60 * 60 * 24 * 7)
+#define SDLZ_DEFAULT_MINIMUM   (60 * 60 * 24)
+
+/* This is a reasonable value */
+#define SDLZ_DEFAULT_TTL       (60 * 60 * 24)
+
+static int dummy;
+
+#define MAYBE_LOCK(imp) \
+       do { \
+               unsigned int flags = imp->flags; \
+               if ((flags & DNS_SDLZFLAG_THREADSAFE) == 0) \
+                       LOCK(&imp->driverlock); \
+       } while (0)
+
+#define MAYBE_UNLOCK(imp) \
+       do { \
+               unsigned int flags = imp->flags; \
+               if ((flags & DNS_SDLZFLAG_THREADSAFE) == 0) \
+                       UNLOCK(&imp->driverlock); \
+       } while (0)
+
+/*
+ * Forward references.  Try to keep these to a minimum.
+ */
+
+static void list_tordataset(dns_rdatalist_t *rdatalist,
+                           dns_db_t *db, dns_dbnode_t *node,
+                           dns_rdataset_t *rdataset);
+
+static void detachnode(dns_db_t *db, dns_dbnode_t **targetp);
+
+static void            dbiterator_destroy(dns_dbiterator_t **iteratorp);
+static isc_result_t    dbiterator_first(dns_dbiterator_t *iterator);
+static isc_result_t    dbiterator_last(dns_dbiterator_t *iterator);
+static isc_result_t    dbiterator_seek(dns_dbiterator_t *iterator,
+                                       dns_name_t *name);
+static isc_result_t    dbiterator_prev(dns_dbiterator_t *iterator);
+static isc_result_t    dbiterator_next(dns_dbiterator_t *iterator);
+static isc_result_t    dbiterator_current(dns_dbiterator_t *iterator,
+                                          dns_dbnode_t **nodep,
+                                          dns_name_t *name);
+static isc_result_t    dbiterator_pause(dns_dbiterator_t *iterator);
+static isc_result_t    dbiterator_origin(dns_dbiterator_t *iterator,
+                                         dns_name_t *name);
+
+static dns_dbiteratormethods_t dbiterator_methods = {
+       dbiterator_destroy,
+       dbiterator_first,
+       dbiterator_last,
+       dbiterator_seek,
+       dbiterator_prev,
+       dbiterator_next,
+       dbiterator_current,
+       dbiterator_pause,
+       dbiterator_origin
+};
+
+/*
+ * Utility functions
+ */
+
+/*% Converts the input string to lowercase, in place. */
+
+static void
+dns_sdlz_tolower(char *str) {
+
+       unsigned int len = strlen(str);
+       unsigned int i;
+
+       for (i = 0; i < len; i++) {
+               if (str[i] >= 'A' && str[i] <= 'Z')
+                       str[i] += 32;
+       }
+
+}
+
+static inline unsigned int
+initial_size(const char *data) {
+       unsigned int len = (strlen(data) / 64) + 1;
+       return (len * 64 + 64);
+}
+
+/*
+ * Rdataset Iterator Methods. These methods were "borrowed" from the SDB
+ * driver interface.  See the SDB driver interface documentation for more info.
+ */
+
+static void
+rdatasetiter_destroy(dns_rdatasetiter_t **iteratorp) {
+       sdlz_rdatasetiter_t *sdlziterator =
+               (sdlz_rdatasetiter_t *)(*iteratorp);
+
+       detachnode(sdlziterator->common.db, &sdlziterator->common.node);
+       isc_mem_put(sdlziterator->common.db->mctx, sdlziterator,
+                   sizeof(sdlz_rdatasetiter_t));
+       *iteratorp = NULL;
+}
+
+static isc_result_t
+rdatasetiter_first(dns_rdatasetiter_t *iterator) {
+       sdlz_rdatasetiter_t *sdlziterator = (sdlz_rdatasetiter_t *)iterator;
+       dns_sdlznode_t *sdlznode = (dns_sdlznode_t *)iterator->node;
+
+       if (ISC_LIST_EMPTY(sdlznode->lists))
+               return (ISC_R_NOMORE);
+       sdlziterator->current = ISC_LIST_HEAD(sdlznode->lists);
+       return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+rdatasetiter_next(dns_rdatasetiter_t *iterator) {
+       sdlz_rdatasetiter_t *sdlziterator = (sdlz_rdatasetiter_t *)iterator;
+
+       sdlziterator->current = ISC_LIST_NEXT(sdlziterator->current, link);
+       if (sdlziterator->current == NULL)
+               return (ISC_R_NOMORE);
+       else
+               return (ISC_R_SUCCESS);
+}
+
+static void
+rdatasetiter_current(dns_rdatasetiter_t *iterator, dns_rdataset_t *rdataset) {
+       sdlz_rdatasetiter_t *sdlziterator = (sdlz_rdatasetiter_t *)iterator;
+
+       list_tordataset(sdlziterator->current, iterator->db, iterator->node,
+                       rdataset);
+}
+
+static dns_rdatasetitermethods_t rdatasetiter_methods = {
+       rdatasetiter_destroy,
+       rdatasetiter_first,
+       rdatasetiter_next,
+       rdatasetiter_current
+};
+
+/*
+ * DB routines. These methods were "borrowed" from the SDB driver interface.
+ * See the SDB driver interface documentation for more info.
+ */
+
+static void
+attach(dns_db_t *source, dns_db_t **targetp) {
+       dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *) source;
+
+       REQUIRE(VALID_SDLZDB(sdlz));
+
+       LOCK(&sdlz->refcnt_lock);
+       REQUIRE(sdlz->references > 0);
+       sdlz->references++;
+       UNLOCK(&sdlz->refcnt_lock);
+
+       *targetp = source;
+}
+
+static void
+destroy(dns_sdlz_db_t *sdlz) {
+       isc_mem_t *mctx;
+       mctx = sdlz->common.mctx;
+
+       sdlz->common.magic = 0;
+       sdlz->common.impmagic = 0;
+
+       isc_mutex_destroy(&sdlz->refcnt_lock);
+
+       dns_name_free(&sdlz->common.origin, mctx);
+
+       isc_mem_put(mctx, sdlz, sizeof(dns_sdlz_db_t));
+       isc_mem_detach(&mctx);
+}
+
+static void
+detach(dns_db_t **dbp) {
+       dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)(*dbp);
+       isc_boolean_t need_destroy = ISC_FALSE;
+
+       REQUIRE(VALID_SDLZDB(sdlz));
+       LOCK(&sdlz->refcnt_lock);
+       REQUIRE(sdlz->references > 0);
+       sdlz->references--;
+       if (sdlz->references == 0)
+               need_destroy = ISC_TRUE;
+       UNLOCK(&sdlz->refcnt_lock);
+
+       if (need_destroy)
+               destroy(sdlz);
+
+       *dbp = NULL;
+}
+
+static isc_result_t
+beginload(dns_db_t *db, dns_addrdatasetfunc_t *addp, dns_dbload_t **dbloadp) {
+       UNUSED(db);
+       UNUSED(addp);
+       UNUSED(dbloadp);
+       return (ISC_R_NOTIMPLEMENTED);
+}
+
+static isc_result_t
+endload(dns_db_t *db, dns_dbload_t **dbloadp) {
+       UNUSED(db);
+       UNUSED(dbloadp);
+       return (ISC_R_NOTIMPLEMENTED);
+}
+
+static isc_result_t
+dump(dns_db_t *db, dns_dbversion_t *version, const char *filename,
+     dns_masterformat_t masterformat)
+{
+       UNUSED(db);
+       UNUSED(version);
+       UNUSED(filename);
+       UNUSED(masterformat);
+       return (ISC_R_NOTIMPLEMENTED);
+}
+
+static void
+currentversion(dns_db_t *db, dns_dbversion_t **versionp) {
+       REQUIRE(versionp != NULL && *versionp == NULL);
+
+       UNUSED(db);
+
+       *versionp = (void *) &dummy;
+       return;
+}
+
+static isc_result_t
+newversion(dns_db_t *db, dns_dbversion_t **versionp) {
+       UNUSED(db);
+       UNUSED(versionp);
+
+       return (ISC_R_NOTIMPLEMENTED);
+}
+
+static void
+attachversion(dns_db_t *db, dns_dbversion_t *source,
+             dns_dbversion_t **targetp)
+{
+       REQUIRE(source != NULL && source == (void *) &dummy);
+
+       UNUSED(db);
+       UNUSED(source);
+       UNUSED(targetp);
+       *targetp = source;
+}
+
+static void
+closeversion(dns_db_t *db, dns_dbversion_t **versionp, isc_boolean_t commit) {
+       REQUIRE(versionp != NULL && *versionp == (void *) &dummy);
+       REQUIRE(commit == ISC_FALSE);
+
+       UNUSED(db);
+       UNUSED(commit);
+
+       *versionp = NULL;
+}
+
+static isc_result_t
+createnode(dns_sdlz_db_t *sdlz, dns_sdlznode_t **nodep) {
+       dns_sdlznode_t *node;
+       isc_result_t result;
+
+       node = isc_mem_get(sdlz->common.mctx, sizeof(dns_sdlznode_t));
+       if (node == NULL)
+               return (ISC_R_NOMEMORY);
+
+       node->sdlz = NULL;
+       attach((dns_db_t *)sdlz, (dns_db_t **)&node->sdlz);
+       ISC_LIST_INIT(node->lists);
+       ISC_LIST_INIT(node->buffers);
+       ISC_LINK_INIT(node, link);
+       node->name = NULL;
+       result = isc_mutex_init(&node->lock);
+       if (result != ISC_R_SUCCESS) {
+               UNEXPECTED_ERROR(__FILE__, __LINE__,
+                                "isc_mutex_init() failed: %s",
+                                isc_result_totext(result));
+               isc_mem_put(sdlz->common.mctx, node, sizeof(dns_sdlznode_t));
+               return (ISC_R_UNEXPECTED);
+       }
+       dns_rdatacallbacks_init(&node->callbacks);
+       node->references = 1;
+       node->magic = SDLZLOOKUP_MAGIC;
+
+       *nodep = node;
+       return (ISC_R_SUCCESS);
+}
+
+static void
+destroynode(dns_sdlznode_t *node) {
+       dns_rdatalist_t *list;
+       dns_rdata_t *rdata;
+       isc_buffer_t *b;
+       dns_sdlz_db_t *sdlz;
+       dns_db_t *db;
+       isc_mem_t *mctx;
+
+       sdlz = node->sdlz;
+       mctx = sdlz->common.mctx;
+
+       while (!ISC_LIST_EMPTY(node->lists)) {
+               list = ISC_LIST_HEAD(node->lists);
+               while (!ISC_LIST_EMPTY(list->rdata)) {
+                       rdata = ISC_LIST_HEAD(list->rdata);
+                       ISC_LIST_UNLINK(list->rdata, rdata, link);
+                       isc_mem_put(mctx, rdata, sizeof(dns_rdata_t));
+               }
+               ISC_LIST_UNLINK(node->lists, list, link);
+               isc_mem_put(mctx, list, sizeof(dns_rdatalist_t));
+       }
+
+       while (!ISC_LIST_EMPTY(node->buffers)) {
+               b = ISC_LIST_HEAD(node->buffers);
+               ISC_LIST_UNLINK(node->buffers, b, link);
+               isc_buffer_free(&b);
+       }
+
+       if (node->name != NULL) {
+               dns_name_free(node->name, mctx);
+               isc_mem_put(mctx, node->name, sizeof(dns_name_t));
+       }
+       DESTROYLOCK(&node->lock);
+       node->magic = 0;
+       isc_mem_put(mctx, node, sizeof(dns_sdlznode_t));
+       db = &sdlz->common;
+       detach(&db);
+}
+
+static isc_result_t
+findnode(dns_db_t *db, dns_name_t *name, isc_boolean_t create,
+        dns_dbnode_t **nodep)
+{
+       dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db;
+       dns_sdlznode_t *node = NULL;
+       isc_result_t result;
+       isc_buffer_t b;
+       char namestr[DNS_NAME_MAXTEXT + 1];
+       isc_buffer_t b2;
+       char zonestr[DNS_NAME_MAXTEXT + 1];
+       isc_boolean_t isorigin;
+       dns_sdlzauthorityfunc_t authority;
+
+       REQUIRE(VALID_SDLZDB(sdlz));
+       REQUIRE(create == ISC_FALSE);
+       REQUIRE(nodep != NULL && *nodep == NULL);
+
+       UNUSED(name);
+       UNUSED(create);
+
+       isc_buffer_init(&b, namestr, sizeof(namestr));
+       if ((sdlz->dlzimp->flags & DNS_SDLZFLAG_RELATIVEOWNER) != 0) {
+               dns_name_t relname;
+               unsigned int labels;
+
+               labels = dns_name_countlabels(name) -
+                        dns_name_countlabels(&db->origin);
+               dns_name_init(&relname, NULL);
+               dns_name_getlabelsequence(name, 0, labels, &relname);
+               result = dns_name_totext(&relname, ISC_TRUE, &b);
+               if (result != ISC_R_SUCCESS)
+                       return (result);
+       } else {
+               result = dns_name_totext(name, ISC_TRUE, &b);
+               if (result != ISC_R_SUCCESS)
+                       return (result);
+       }
+       isc_buffer_putuint8(&b, 0);
+
+       isc_buffer_init(&b2, zonestr, sizeof(zonestr));
+       result = dns_name_totext(&sdlz->common.origin, ISC_TRUE, &b2);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+       isc_buffer_putuint8(&b2, 0);
+
+       result = createnode(sdlz, &node);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+
+       isorigin = dns_name_equal(name, &sdlz->common.origin);
+
+       /* make sure strings are always lowercase */
+       dns_sdlz_tolower(zonestr);
+       dns_sdlz_tolower(namestr);
+
+       MAYBE_LOCK(sdlz->dlzimp);
+
+       /* try to lookup the host (namestr) */
+       result = sdlz->dlzimp->methods->lookup(zonestr, namestr,
+                                              sdlz->dlzimp->driverarg,
+                                              sdlz->dbdata, node);
+
+       /*
+        * if the host (namestr) was not found, try to lookup a
+        * "wildcard" host.
+        */
+       if (result != ISC_R_SUCCESS) {
+               result = sdlz->dlzimp->methods->lookup(zonestr, "*",
+                                                      sdlz->dlzimp->driverarg,
+                                                      sdlz->dbdata, node);
+       }
+
+       MAYBE_UNLOCK(sdlz->dlzimp);
+
+       if (result != ISC_R_SUCCESS && !isorigin) {
+               destroynode(node);
+               return (result);
+       }
+
+       if (isorigin && sdlz->dlzimp->methods->authority != NULL) {
+               MAYBE_LOCK(sdlz->dlzimp);
+               authority = sdlz->dlzimp->methods->authority;
+               result = (*authority)(zonestr, sdlz->dlzimp->driverarg,
+                                     sdlz->dbdata, node);
+               MAYBE_UNLOCK(sdlz->dlzimp);
+               if (result != ISC_R_SUCCESS &&
+                   result != ISC_R_NOTIMPLEMENTED) {
+                       destroynode(node);
+                       return (result);
+               }
+       }
+
+       *nodep = node;
+       return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+findzonecut(dns_db_t *db, dns_name_t *name, unsigned int options,
+           isc_stdtime_t now, dns_dbnode_t **nodep, dns_name_t *foundname,
+           dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
+{
+       UNUSED(db);
+       UNUSED(name);
+       UNUSED(options);
+       UNUSED(now);
+       UNUSED(nodep);
+       UNUSED(foundname);
+       UNUSED(rdataset);
+       UNUSED(sigrdataset);
+
+       return (ISC_R_NOTIMPLEMENTED);
+}
+
+static void
+attachnode(dns_db_t *db, dns_dbnode_t *source, dns_dbnode_t **targetp) {
+       dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db;
+       dns_sdlznode_t *node = (dns_sdlznode_t *)source;
+
+       REQUIRE(VALID_SDLZDB(sdlz));
+
+       UNUSED(sdlz);
+
+       LOCK(&node->lock);
+       INSIST(node->references > 0);
+       node->references++;
+       INSIST(node->references != 0);          /* Catch overflow. */
+       UNLOCK(&node->lock);
+
+       *targetp = source;
+}
+
+static void
+detachnode(dns_db_t *db, dns_dbnode_t **targetp) {
+       dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db;
+       dns_sdlznode_t *node;
+       isc_boolean_t need_destroy = ISC_FALSE;
+
+       REQUIRE(VALID_SDLZDB(sdlz));
+       REQUIRE(targetp != NULL && *targetp != NULL);
+
+       UNUSED(sdlz);
+
+       node = (dns_sdlznode_t *)(*targetp);
+
+       LOCK(&node->lock);
+       INSIST(node->references > 0);
+       node->references--;
+       if (node->references == 0)
+               need_destroy = ISC_TRUE;
+       UNLOCK(&node->lock);
+
+       if (need_destroy)
+               destroynode(node);
+
+       *targetp = NULL;
+}
+
+static isc_result_t
+expirenode(dns_db_t *db, dns_dbnode_t *node, isc_stdtime_t now) {
+       UNUSED(db);
+       UNUSED(node);
+       UNUSED(now);
+       INSIST(0);
+       return (ISC_R_UNEXPECTED);
+}
+
+static void
+printnode(dns_db_t *db, dns_dbnode_t *node, FILE *out) {
+       UNUSED(db);
+       UNUSED(node);
+       UNUSED(out);
+       return;
+}
+
+static isc_result_t
+createiterator(dns_db_t *db, isc_boolean_t relative_names,
+              dns_dbiterator_t **iteratorp)
+{
+       dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db;
+       sdlz_dbiterator_t *sdlziter;
+       isc_result_t result;
+       isc_buffer_t b;
+       char zonestr[DNS_NAME_MAXTEXT + 1];
+
+       REQUIRE(VALID_SDLZDB(sdlz));
+
+       if (sdlz->dlzimp->methods->allnodes == NULL)
+               return (ISC_R_NOTIMPLEMENTED);
+
+       isc_buffer_init(&b, zonestr, sizeof(zonestr));
+       result = dns_name_totext(&sdlz->common.origin, ISC_TRUE, &b);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+       isc_buffer_putuint8(&b, 0);
+
+       sdlziter = isc_mem_get(sdlz->common.mctx, sizeof(sdlz_dbiterator_t));
+       if (sdlziter == NULL)
+               return (ISC_R_NOMEMORY);
+
+       sdlziter->common.methods = &dbiterator_methods;
+       sdlziter->common.db = NULL;
+       dns_db_attach(db, &sdlziter->common.db);
+       sdlziter->common.relative_names = relative_names;
+       sdlziter->common.magic = DNS_DBITERATOR_MAGIC;
+       ISC_LIST_INIT(sdlziter->nodelist);
+       sdlziter->current = NULL;
+       sdlziter->origin = NULL;
+
+       /* make sure strings are always lowercase */
+       dns_sdlz_tolower(zonestr);
+
+       MAYBE_LOCK(sdlz->dlzimp);
+       result = sdlz->dlzimp->methods->allnodes(zonestr,
+                                                sdlz->dlzimp->driverarg,
+                                                sdlz->dbdata, sdlziter);
+       MAYBE_UNLOCK(sdlz->dlzimp);
+       if (result != ISC_R_SUCCESS) {
+               dns_dbiterator_t *iter = &sdlziter->common;
+               dbiterator_destroy(&iter);
+               return (result);
+       }
+
+       if (sdlziter->origin != NULL) {
+               ISC_LIST_UNLINK(sdlziter->nodelist, sdlziter->origin, link);
+               ISC_LIST_PREPEND(sdlziter->nodelist, sdlziter->origin, link);
+       }
+
+       *iteratorp = (dns_dbiterator_t *)sdlziter;
+
+       return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+findrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+            dns_rdatatype_t type, dns_rdatatype_t covers,
+            isc_stdtime_t now, dns_rdataset_t *rdataset,
+            dns_rdataset_t *sigrdataset)
+{
+       dns_rdatalist_t *list;
+       dns_sdlznode_t *sdlznode = (dns_sdlznode_t *)node;
+
+       REQUIRE(VALID_SDLZNODE(node));
+
+       UNUSED(db);
+       UNUSED(version);
+       UNUSED(covers);
+       UNUSED(now);
+       UNUSED(sigrdataset);
+
+       if (type == dns_rdatatype_sig)
+               return (ISC_R_NOTIMPLEMENTED);
+
+       list = ISC_LIST_HEAD(sdlznode->lists);
+       while (list != NULL) {
+               if (list->type == type)
+                       break;
+               list = ISC_LIST_NEXT(list, link);
+       }
+       if (list == NULL)
+               return (ISC_R_NOTFOUND);
+
+       list_tordataset(list, db, node, rdataset);
+
+       return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+find(dns_db_t *db, dns_name_t *name, dns_dbversion_t *version,
+     dns_rdatatype_t type, unsigned int options, isc_stdtime_t now,
+     dns_dbnode_t **nodep, dns_name_t *foundname,
+     dns_rdataset_t *rdataset, dns_rdataset_t *sigrdataset)
+{
+       dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)db;
+       dns_dbnode_t *node = NULL;
+       dns_fixedname_t fname;
+       dns_rdataset_t xrdataset;
+       dns_name_t *xname;
+       unsigned int nlabels, olabels;
+       isc_result_t result;
+       unsigned int i;
+
+       REQUIRE(VALID_SDLZDB(sdlz));
+       REQUIRE(nodep == NULL || *nodep == NULL);
+       REQUIRE(version == NULL || version == (void *) &dummy);
+
+       UNUSED(options);
+       UNUSED(sdlz);
+
+       if (!dns_name_issubdomain(name, &db->origin))
+               return (DNS_R_NXDOMAIN);
+
+       olabels = dns_name_countlabels(&db->origin);
+       nlabels = dns_name_countlabels(name);
+
+       dns_fixedname_init(&fname);
+       xname = dns_fixedname_name(&fname);
+
+       if (rdataset == NULL)
+               rdataset = &xrdataset;
+
+       result = DNS_R_NXDOMAIN;
+
+       for (i = olabels; i <= nlabels; i++) {
+               /*
+                * Unless this is an explicit lookup at the origin, don't
+                * look at the origin.
+                */
+               if (i == olabels && i != nlabels)
+                       continue;
+
+               /*
+                * Look up the next label.
+                */
+               dns_name_getlabelsequence(name, nlabels - i, i, xname);
+               result = findnode(db, xname, ISC_FALSE, &node);
+               if (result != ISC_R_SUCCESS) {
+                       result = DNS_R_NXDOMAIN;
+                       continue;
+               }
+
+               /*
+                * Look for a DNAME at the current label, unless this is
+                * the qname.
+                */
+               if (i < nlabels) {
+                       result = findrdataset(db, node, version,
+                                             dns_rdatatype_dname,
+                                             0, now, rdataset, sigrdataset);
+                       if (result == ISC_R_SUCCESS) {
+                               result = DNS_R_DNAME;
+                               break;
+                       }
+               }
+
+               /*
+                * Look for an NS at the current label, unless this is the
+                * origin or glue is ok.
+                */
+               if (i != olabels && (options & DNS_DBFIND_GLUEOK) == 0) {
+                       result = findrdataset(db, node, version,
+                                             dns_rdatatype_ns,
+                                             0, now, rdataset, sigrdataset);
+                       if (result == ISC_R_SUCCESS) {
+                               if (i == nlabels && type == dns_rdatatype_any)
+                               {
+                                       result = DNS_R_ZONECUT;
+                                       dns_rdataset_disassociate(rdataset);
+                                       if (sigrdataset != NULL)
+                                               dns_rdataset_disassociate
+                                                       (sigrdataset);
+                               } else
+                                       result = DNS_R_DELEGATION;
+                               break;
+                       }
+               }
+
+               /*
+                * If the current name is not the qname, add another label
+                * and try again.
+                */
+               if (i < nlabels) {
+                       destroynode(node);
+                       node = NULL;
+                       continue;
+               }
+
+               /*
+                * If we're looking for ANY, we're done.
+                */
+               if (type == dns_rdatatype_any) {
+                       result = ISC_R_SUCCESS;
+                       break;
+               }
+
+               /*
+                * Look for the qtype.
+                */
+               result = findrdataset(db, node, version, type,
+                                     0, now, rdataset, sigrdataset);
+               if (result == ISC_R_SUCCESS)
+                       break;
+
+               /*
+                * Look for a CNAME
+                */
+               if (type != dns_rdatatype_cname) {
+                       result = findrdataset(db, node, version,
+                                             dns_rdatatype_cname,
+                                             0, now, rdataset, sigrdataset);
+                       if (result == ISC_R_SUCCESS) {
+                               result = DNS_R_CNAME;
+                               break;
+                       }
+               }
+
+               result = DNS_R_NXRRSET;
+               break;
+       }
+
+       if (rdataset == &xrdataset && dns_rdataset_isassociated(rdataset))
+               dns_rdataset_disassociate(rdataset);
+
+       if (foundname != NULL) {
+               isc_result_t xresult;
+
+               xresult = dns_name_copy(xname, foundname, NULL);
+               if (xresult != ISC_R_SUCCESS) {
+                       destroynode(node);
+                       if (dns_rdataset_isassociated(rdataset))
+                               dns_rdataset_disassociate(rdataset);
+                       return (DNS_R_BADDB);
+               }
+       }
+
+       if (nodep != NULL)
+               *nodep = node;
+       else if (node != NULL)
+               detachnode(db, &node);
+
+       return (result);
+}
+
+static isc_result_t
+allrdatasets(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+            isc_stdtime_t now, dns_rdatasetiter_t **iteratorp)
+{
+       sdlz_rdatasetiter_t *iterator;
+
+       REQUIRE(version == NULL || version == &dummy);
+
+       UNUSED(version);
+       UNUSED(now);
+
+       iterator = isc_mem_get(db->mctx, sizeof(sdlz_rdatasetiter_t));
+       if (iterator == NULL)
+               return (ISC_R_NOMEMORY);
+
+       iterator->common.magic = DNS_RDATASETITER_MAGIC;
+       iterator->common.methods = &rdatasetiter_methods;
+       iterator->common.db = db;
+       iterator->common.node = NULL;
+       attachnode(db, node, &iterator->common.node);
+       iterator->common.version = version;
+       iterator->common.now = now;
+
+       *iteratorp = (dns_rdatasetiter_t *)iterator;
+
+       return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+addrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+           isc_stdtime_t now, dns_rdataset_t *rdataset, unsigned int options,
+           dns_rdataset_t *addedrdataset)
+{
+       UNUSED(db);
+       UNUSED(node);
+       UNUSED(version);
+       UNUSED(now);
+       UNUSED(rdataset);
+       UNUSED(options);
+       UNUSED(addedrdataset);
+
+       return (ISC_R_NOTIMPLEMENTED);
+}
+
+static isc_result_t
+subtractrdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+                dns_rdataset_t *rdataset, unsigned int options,
+                dns_rdataset_t *newrdataset)
+{
+       UNUSED(db);
+       UNUSED(node);
+       UNUSED(version);
+       UNUSED(rdataset);
+       UNUSED(options);
+       UNUSED(newrdataset);
+
+       return (ISC_R_NOTIMPLEMENTED);
+}
+
+static isc_result_t
+deleterdataset(dns_db_t *db, dns_dbnode_t *node, dns_dbversion_t *version,
+              dns_rdatatype_t type, dns_rdatatype_t covers)
+{
+       UNUSED(db);
+       UNUSED(node);
+       UNUSED(version);
+       UNUSED(type);
+       UNUSED(covers);
+
+       return (ISC_R_NOTIMPLEMENTED);
+}
+
+static isc_boolean_t
+issecure(dns_db_t *db) {
+       UNUSED(db);
+
+       return (ISC_FALSE);
+}
+
+static unsigned int
+nodecount(dns_db_t *db) {
+       UNUSED(db);
+
+       return (0);
+}
+
+static isc_boolean_t
+ispersistent(dns_db_t *db) {
+       UNUSED(db);
+       return (ISC_TRUE);
+}
+
+static void
+overmem(dns_db_t *db, isc_boolean_t overmem) {
+       UNUSED(db);
+       UNUSED(overmem);
+}
+
+static void
+settask(dns_db_t *db, isc_task_t *task) {
+       UNUSED(db);
+       UNUSED(task);
+}
+
+
+static dns_dbmethods_t sdlzdb_methods = {
+       attach,
+       detach,
+       beginload,
+       endload,
+       dump,
+       currentversion,
+       newversion,
+       attachversion,
+       closeversion,
+       findnode,
+       find,
+       findzonecut,
+       attachnode,
+       detachnode,
+       expirenode,
+       printnode,
+       createiterator,
+       findrdataset,
+       allrdatasets,
+       addrdataset,
+       subtractrdataset,
+       deleterdataset,
+       issecure,
+       nodecount,
+       ispersistent,
+       overmem,
+       settask,
+       NULL,
+       NULL,
+       NULL,
+       NULL
+};
+
+/*
+ * Database Iterator Methods.  These methods were "borrowed" from the SDB
+ * driver interface.  See the SDB driver interface documentation for more info.
+ */
+
+static void
+dbiterator_destroy(dns_dbiterator_t **iteratorp) {
+       sdlz_dbiterator_t *sdlziter = (sdlz_dbiterator_t *)(*iteratorp);
+       dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)sdlziter->common.db;
+
+       while (!ISC_LIST_EMPTY(sdlziter->nodelist)) {
+               dns_sdlznode_t *node;
+               node = ISC_LIST_HEAD(sdlziter->nodelist);
+               ISC_LIST_UNLINK(sdlziter->nodelist, node, link);
+               destroynode(node);
+       }
+
+       dns_db_detach(&sdlziter->common.db);
+       isc_mem_put(sdlz->common.mctx, sdlziter, sizeof(sdlz_dbiterator_t));
+
+       *iteratorp = NULL;
+}
+
+static isc_result_t
+dbiterator_first(dns_dbiterator_t *iterator) {
+       sdlz_dbiterator_t *sdlziter = (sdlz_dbiterator_t *)iterator;
+
+       sdlziter->current = ISC_LIST_HEAD(sdlziter->nodelist);
+       if (sdlziter->current == NULL)
+               return (ISC_R_NOMORE);
+       else
+               return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+dbiterator_last(dns_dbiterator_t *iterator) {
+       sdlz_dbiterator_t *sdlziter = (sdlz_dbiterator_t *)iterator;
+
+       sdlziter->current = ISC_LIST_TAIL(sdlziter->nodelist);
+       if (sdlziter->current == NULL)
+               return (ISC_R_NOMORE);
+       else
+               return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+dbiterator_seek(dns_dbiterator_t *iterator, dns_name_t *name) {
+       sdlz_dbiterator_t *sdlziter = (sdlz_dbiterator_t *)iterator;
+
+       sdlziter->current = ISC_LIST_HEAD(sdlziter->nodelist);
+       while (sdlziter->current != NULL)
+               if (dns_name_equal(sdlziter->current->name, name))
+                       return (ISC_R_SUCCESS);
+       return (ISC_R_NOTFOUND);
+}
+
+static isc_result_t
+dbiterator_prev(dns_dbiterator_t *iterator) {
+       sdlz_dbiterator_t *sdlziter = (sdlz_dbiterator_t *)iterator;
+
+       sdlziter->current = ISC_LIST_PREV(sdlziter->current, link);
+       if (sdlziter->current == NULL)
+               return (ISC_R_NOMORE);
+       else
+               return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+dbiterator_next(dns_dbiterator_t *iterator) {
+       sdlz_dbiterator_t *sdlziter = (sdlz_dbiterator_t *)iterator;
+
+       sdlziter->current = ISC_LIST_NEXT(sdlziter->current, link);
+       if (sdlziter->current == NULL)
+               return (ISC_R_NOMORE);
+       else
+               return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+dbiterator_current(dns_dbiterator_t *iterator, dns_dbnode_t **nodep,
+                  dns_name_t *name)
+{
+       sdlz_dbiterator_t *sdlziter = (sdlz_dbiterator_t *)iterator;
+
+       attachnode(iterator->db, sdlziter->current, nodep);
+       if (name != NULL)
+               return (dns_name_copy(sdlziter->current->name, name, NULL));
+       return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+dbiterator_pause(dns_dbiterator_t *iterator) {
+       UNUSED(iterator);
+       return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+dbiterator_origin(dns_dbiterator_t *iterator, dns_name_t *name) {
+       UNUSED(iterator);
+       return (dns_name_copy(dns_rootname, name, NULL));
+}
+
+/*
+ * Rdataset Methods. These methods were "borrowed" from the SDB driver
+ * interface.  See the SDB driver interface documentation for more info.
+ */
+
+static void
+disassociate(dns_rdataset_t *rdataset) {
+       dns_dbnode_t *node = rdataset->private5;
+       dns_sdlznode_t *sdlznode = (dns_sdlznode_t *) node;
+       dns_db_t *db = (dns_db_t *) sdlznode->sdlz;
+
+       detachnode(db, &node);
+       isc__rdatalist_disassociate(rdataset);
+}
+
+static void
+rdataset_clone(dns_rdataset_t *source, dns_rdataset_t *target) {
+       dns_dbnode_t *node = source->private5;
+       dns_sdlznode_t *sdlznode = (dns_sdlznode_t *) node;
+       dns_db_t *db = (dns_db_t *) sdlznode->sdlz;
+       dns_dbnode_t *tempdb = NULL;
+
+       isc__rdatalist_clone(source, target);
+       attachnode(db, node, &tempdb);
+       source->private5 = tempdb;
+}
+
+static dns_rdatasetmethods_t rdataset_methods = {
+       disassociate,
+       isc__rdatalist_first,
+       isc__rdatalist_next,
+       isc__rdatalist_current,
+       rdataset_clone,
+       isc__rdatalist_count,
+       isc__rdatalist_addnoqname,
+       isc__rdatalist_getnoqname,
+       NULL,
+       NULL,
+       NULL
+};
+
+static void
+list_tordataset(dns_rdatalist_t *rdatalist,
+               dns_db_t *db, dns_dbnode_t *node,
+               dns_rdataset_t *rdataset)
+{
+       /*
+        * The sdlz rdataset is an rdatalist with some additions.
+        *      - private1 & private2 are used by the rdatalist.
+        *      - private3 & private 4 are unused.
+        *      - private5 is the node.
+        */
+
+       /* This should never fail. */
+       RUNTIME_CHECK(dns_rdatalist_tordataset(rdatalist, rdataset) ==
+                     ISC_R_SUCCESS);
+
+       rdataset->methods = &rdataset_methods;
+       dns_db_attachnode(db, node, &rdataset->private5);
+}
+
+/*
+ * SDLZ core methods. This is the core of the new DLZ functionality.
+ */
+
+/*%
+ * Build a 'bind' database driver structure to be returned by
+ * either the find zone or the allow zone transfer method.
+ * This method is only available in this source file, it is
+ * not made available anywhere else.
+ */
+
+static isc_result_t
+dns_sdlzcreateDBP(isc_mem_t *mctx, void *driverarg, void *dbdata,
+                 dns_name_t *name, dns_rdataclass_t rdclass, dns_db_t **dbp)
+{
+       isc_result_t result;
+       dns_sdlz_db_t *sdlzdb;
+       dns_sdlzimplementation_t *imp;
+
+       /* check that things are as we expect */
+       REQUIRE(dbp != NULL && *dbp == NULL);
+       REQUIRE(name != NULL);
+
+       imp = (dns_sdlzimplementation_t *) driverarg;
+
+       /* allocate and zero memory for driver structure */
+       sdlzdb = isc_mem_get(mctx, sizeof(dns_sdlz_db_t));
+       if (sdlzdb == NULL)
+               return (ISC_R_NOMEMORY);
+       memset(sdlzdb, 0, sizeof(dns_sdlz_db_t));
+
+       /* initialize and set origin */
+       dns_name_init(&sdlzdb->common.origin, NULL);
+       result = dns_name_dupwithoffsets(name, mctx, &sdlzdb->common.origin);
+       if (result != ISC_R_SUCCESS)
+               goto mem_cleanup;
+
+       /* initialize the reference count mutex */
+       result = isc_mutex_init(&sdlzdb->refcnt_lock);
+       if (result != ISC_R_SUCCESS)
+               goto name_cleanup;
+
+       /* set the rest of the database structure attributes */
+       sdlzdb->dlzimp = imp;
+       sdlzdb->common.methods = &sdlzdb_methods;
+       sdlzdb->common.attributes = 0;
+       sdlzdb->common.rdclass = rdclass;
+       sdlzdb->common.mctx = NULL;
+       sdlzdb->dbdata = dbdata;
+       sdlzdb->references = 1;
+
+       /* attach to the memory context */
+       isc_mem_attach(mctx, &sdlzdb->common.mctx);
+
+       /* mark structure as valid */
+       sdlzdb->common.magic = DNS_DB_MAGIC;
+       sdlzdb->common.impmagic = SDLZDB_MAGIC;
+       *dbp = (dns_db_t *) sdlzdb;
+
+       return (result);
+
+       /*
+        * reference count mutex could not be initialized, clean up
+        * name memory
+        */
+ name_cleanup:
+       dns_name_free(&sdlzdb->common.origin, mctx);
+ mem_cleanup:
+       isc_mem_put(mctx, sdlzdb, sizeof(dns_sdlz_db_t));
+       return (result);
+}
+
+static isc_result_t
+dns_sdlzallowzonexfr(void *driverarg, void *dbdata, isc_mem_t *mctx,
+                    dns_rdataclass_t rdclass, dns_name_t *name,
+                    isc_sockaddr_t *clientaddr, dns_db_t **dbp)
+{
+       isc_buffer_t b;
+       isc_buffer_t b2;
+       char namestr[DNS_NAME_MAXTEXT + 1];
+       char clientstr[(sizeof "xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255")
+                      + 1];
+       isc_netaddr_t netaddr;
+       isc_result_t result;
+       dns_sdlzimplementation_t *imp;
+
+       /*
+        * Perform checks to make sure data is as we expect it to be.
+        */
+       REQUIRE(driverarg != NULL);
+       REQUIRE(name != NULL);
+       REQUIRE(clientaddr != NULL);
+       REQUIRE(dbp != NULL && *dbp == NULL);
+
+       imp = (dns_sdlzimplementation_t *) driverarg;
+
+       /* Convert DNS name to ascii text */
+       isc_buffer_init(&b, namestr, sizeof(namestr));
+       result = dns_name_totext(name, ISC_TRUE, &b);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+       isc_buffer_putuint8(&b, 0);
+
+       /* convert client address to ascii text */
+       isc_buffer_init(&b2, clientstr, sizeof(clientstr));
+       isc_netaddr_fromsockaddr(&netaddr, clientaddr);
+       result = isc_netaddr_totext(&netaddr, &b2);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+       isc_buffer_putuint8(&b2, 0);
+
+        /* make sure strings are always lowercase */
+       dns_sdlz_tolower(namestr);
+       dns_sdlz_tolower(clientstr);
+
+       /* Call SDLZ driver's find zone method */
+       if (imp->methods->allowzonexfr != NULL) {
+               MAYBE_LOCK(imp);
+               result = imp->methods->allowzonexfr(imp->driverarg, dbdata,
+                                                   namestr, clientstr);
+               MAYBE_UNLOCK(imp);
+               /*
+                * if zone is supported and transfers allowed build a 'bind'
+                * database driver
+                */
+               if (result == ISC_R_SUCCESS)
+                       result = dns_sdlzcreateDBP(mctx, driverarg, dbdata,
+                                                  name, rdclass, dbp);
+               return (result);
+       }
+
+       return (ISC_R_NOTIMPLEMENTED);
+}
+
+static isc_result_t
+dns_sdlzcreate(isc_mem_t *mctx, const char *dlzname, unsigned int argc,
+              char *argv[], void *driverarg, void **dbdata)
+{
+       dns_sdlzimplementation_t *imp;
+       isc_result_t result = ISC_R_NOTFOUND;
+
+       /* Write debugging message to log */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                     "Loading SDLZ driver.");
+
+       /*
+        * Performs checks to make sure data is as we expect it to be.
+        */
+       REQUIRE(driverarg != NULL);
+       REQUIRE(dlzname != NULL);
+       REQUIRE(dbdata != NULL);
+       UNUSED(mctx);
+
+       imp = driverarg;
+
+       /* If the create method exists, call it. */
+       if (imp->methods->create != NULL) {
+               MAYBE_LOCK(imp);
+               result = imp->methods->create(dlzname, argc, argv,
+                                             imp->driverarg, dbdata);
+               MAYBE_UNLOCK(imp);
+       }
+
+       /* Write debugging message to log */
+       if (result == ISC_R_SUCCESS) {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                             "SDLZ driver loaded successfully.");
+       } else {
+               isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                             DNS_LOGMODULE_DLZ, ISC_LOG_ERROR,
+                             "SDLZ driver failed to load.");
+       }
+
+       return (result);
+}
+
+static void
+dns_sdlzdestroy(void *driverdata, void **dbdata)
+{
+
+       dns_sdlzimplementation_t *imp;
+
+       /* Write debugging message to log */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                     "Unloading SDLZ driver.");
+
+       imp = driverdata;
+
+       /* If the destroy method exists, call it. */
+       if (imp->methods->destroy != NULL) {
+               MAYBE_LOCK(imp);
+               imp->methods->destroy(imp->driverarg, dbdata);
+               MAYBE_UNLOCK(imp);
+       }
+}
+
+static isc_result_t
+dns_sdlzfindzone(void *driverarg, void *dbdata, isc_mem_t *mctx,
+                dns_rdataclass_t rdclass, dns_name_t *name, dns_db_t **dbp)
+{
+       isc_buffer_t b;
+       char namestr[DNS_NAME_MAXTEXT + 1];
+       isc_result_t result;
+       dns_sdlzimplementation_t *imp;
+
+       /*
+        * Perform checks to make sure data is as we expect it to be.
+        */
+       REQUIRE(driverarg != NULL);
+       REQUIRE(name != NULL);
+       REQUIRE(dbp != NULL && *dbp == NULL);
+
+       imp = (dns_sdlzimplementation_t *) driverarg;
+
+       /* Convert DNS name to ascii text */
+       isc_buffer_init(&b, namestr, sizeof(namestr));
+       result = dns_name_totext(name, ISC_TRUE, &b);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+       isc_buffer_putuint8(&b, 0);
+
+        /* make sure strings are always lowercase */
+       dns_sdlz_tolower(namestr);
+
+       /* Call SDLZ driver's find zone method */
+       MAYBE_LOCK(imp);
+       result = imp->methods->findzone(imp->driverarg, dbdata, namestr);
+       MAYBE_UNLOCK(imp);
+
+       /*
+        * if zone is supported build a 'bind' database driver
+        * structure to return
+        */
+       if (result == ISC_R_SUCCESS)
+               result = dns_sdlzcreateDBP(mctx, driverarg, dbdata, name,
+                                          rdclass, dbp);
+
+       return (result);
+}
+
+static dns_dlzmethods_t sdlzmethods = {
+       dns_sdlzcreate,
+       dns_sdlzdestroy,
+       dns_sdlzfindzone,
+       dns_sdlzallowzonexfr
+};
+
+/*
+ * Public functions.
+ */
+
+isc_result_t
+dns_sdlz_putrr(dns_sdlzlookup_t *lookup, const char *type, dns_ttl_t ttl,
+              const char *data)
+{
+       dns_rdatalist_t *rdatalist;
+       dns_rdata_t *rdata;
+       dns_rdatatype_t typeval;
+       isc_consttextregion_t r;
+       isc_buffer_t b;
+       isc_buffer_t *rdatabuf;
+       isc_lex_t *lex;
+       isc_result_t result;
+       unsigned int size;
+       isc_mem_t *mctx;
+       dns_name_t *origin;
+
+       REQUIRE(VALID_SDLZLOOKUP(lookup));
+       REQUIRE(type != NULL);
+       REQUIRE(data != NULL);
+
+       mctx = lookup->sdlz->common.mctx;
+
+       r.base = type;
+       r.length = strlen(type);
+       result = dns_rdatatype_fromtext(&typeval, (void *) &r);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+
+       rdatalist = ISC_LIST_HEAD(lookup->lists);
+       while (rdatalist != NULL) {
+               if (rdatalist->type == typeval)
+                       break;
+               rdatalist = ISC_LIST_NEXT(rdatalist, link);
+       }
+
+       if (rdatalist == NULL) {
+               rdatalist = isc_mem_get(mctx, sizeof(dns_rdatalist_t));
+               if (rdatalist == NULL)
+                       return (ISC_R_NOMEMORY);
+               rdatalist->rdclass = lookup->sdlz->common.rdclass;
+               rdatalist->type = typeval;
+               rdatalist->covers = 0;
+               rdatalist->ttl = ttl;
+               ISC_LIST_INIT(rdatalist->rdata);
+               ISC_LINK_INIT(rdatalist, link);
+               ISC_LIST_APPEND(lookup->lists, rdatalist, link);
+       } else
+               if (rdatalist->ttl != ttl)
+                       return (DNS_R_BADTTL);
+
+       rdata = isc_mem_get(mctx, sizeof(dns_rdata_t));
+       if (rdata == NULL)
+               return (ISC_R_NOMEMORY);
+       dns_rdata_init(rdata);
+
+       if ((lookup->sdlz->dlzimp->flags & DNS_SDLZFLAG_RELATIVERDATA) != 0)
+               origin = &lookup->sdlz->common.origin;
+       else
+               origin = dns_rootname;
+
+       lex = NULL;
+       result = isc_lex_create(mctx, 64, &lex);
+       if (result != ISC_R_SUCCESS)
+               goto failure;
+
+       size = initial_size(data);
+       do {
+               isc_buffer_init(&b, data, strlen(data));
+               isc_buffer_add(&b, strlen(data));
+
+               result = isc_lex_openbuffer(lex, &b);
+               if (result != ISC_R_SUCCESS)
+                       goto failure;
+
+               rdatabuf = NULL;
+               result = isc_buffer_allocate(mctx, &rdatabuf, size);
+               if (result != ISC_R_SUCCESS)
+                       goto failure;
+
+               result = dns_rdata_fromtext(rdata, rdatalist->rdclass,
+                                           rdatalist->type, lex,
+                                           origin, ISC_FALSE,
+                                           mctx, rdatabuf,
+                                           &lookup->callbacks);
+               if (result != ISC_R_SUCCESS)
+                       isc_buffer_free(&rdatabuf);
+               size *= 2;
+       } while (result == ISC_R_NOSPACE);
+
+       if (result != ISC_R_SUCCESS)
+               goto failure;
+
+       ISC_LIST_APPEND(rdatalist->rdata, rdata, link);
+       ISC_LIST_APPEND(lookup->buffers, rdatabuf, link);
+
+       if (lex != NULL)
+               isc_lex_destroy(&lex);
+
+       return (ISC_R_SUCCESS);
+
+ failure:
+       if (rdatabuf != NULL)
+               isc_buffer_free(&rdatabuf);
+       if (lex != NULL)
+               isc_lex_destroy(&lex);
+       isc_mem_put(mctx, rdata, sizeof(dns_rdata_t));
+
+       return (result);
+}
+
+isc_result_t
+dns_sdlz_putnamedrr(dns_sdlzallnodes_t *allnodes, const char *name,
+                   const char *type, dns_ttl_t ttl, const char *data)
+{
+       dns_name_t *newname, *origin;
+       dns_fixedname_t fnewname;
+       dns_sdlz_db_t *sdlz = (dns_sdlz_db_t *)allnodes->common.db;
+       dns_sdlznode_t *sdlznode;
+       isc_mem_t *mctx = sdlz->common.mctx;
+       isc_buffer_t b;
+       isc_result_t result;
+
+       dns_fixedname_init(&fnewname);
+       newname = dns_fixedname_name(&fnewname);
+
+       if ((sdlz->dlzimp->flags & DNS_SDLZFLAG_RELATIVERDATA) != 0)
+               origin = &sdlz->common.origin;
+       else
+               origin = dns_rootname;
+       isc_buffer_init(&b, name, strlen(name));
+       isc_buffer_add(&b, strlen(name));
+
+       result = dns_name_fromtext(newname, &b, origin, ISC_FALSE, NULL);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+
+       if (allnodes->common.relative_names) {
+               /* All names are relative to the root */
+               unsigned int nlabels = dns_name_countlabels(newname);
+               dns_name_getlabelsequence(newname, 0, nlabels - 1, newname);
+       }
+
+       sdlznode = ISC_LIST_HEAD(allnodes->nodelist);
+       if (sdlznode == NULL || !dns_name_equal(sdlznode->name, newname)) {
+               sdlznode = NULL;
+               result = createnode(sdlz, &sdlznode);
+               if (result != ISC_R_SUCCESS)
+                       return (result);
+               sdlznode->name = isc_mem_get(mctx, sizeof(dns_name_t));
+               if (sdlznode->name == NULL) {
+                       destroynode(sdlznode);
+                       return (ISC_R_NOMEMORY);
+               }
+               dns_name_init(sdlznode->name, NULL);
+               result = dns_name_dup(newname, mctx, sdlznode->name);
+               if (result != ISC_R_SUCCESS) {
+                       isc_mem_put(mctx, sdlznode->name, sizeof(dns_name_t));
+                       destroynode(sdlznode);
+                       return (result);
+               }
+               ISC_LIST_PREPEND(allnodes->nodelist, sdlznode, link);
+               if (allnodes->origin == NULL &&
+                   dns_name_equal(newname, &sdlz->common.origin))
+                       allnodes->origin = sdlznode;
+       }
+       return (dns_sdlz_putrr(sdlznode, type, ttl, data));
+
+}
+
+isc_result_t
+dns_sdlz_putsoa(dns_sdlzlookup_t *lookup, const char *mname, const char *rname,
+               isc_uint32_t serial)
+{
+       char str[2 * DNS_NAME_MAXTEXT + 5 * (sizeof("2147483647")) + 7];
+       int n;
+
+       REQUIRE(mname != NULL);
+       REQUIRE(rname != NULL);
+
+       n = snprintf(str, sizeof str, "%s %s %u %u %u %u %u",
+                    mname, rname, serial,
+                    SDLZ_DEFAULT_REFRESH, SDLZ_DEFAULT_RETRY,
+                    SDLZ_DEFAULT_EXPIRE, SDLZ_DEFAULT_MINIMUM);
+       if (n >= (int)sizeof(str) || n < 0)
+               return (ISC_R_NOSPACE);
+       return (dns_sdlz_putrr(lookup, "SOA", SDLZ_DEFAULT_TTL, str));
+}
+
+isc_result_t
+dns_sdlzregister(const char *drivername, const dns_sdlzmethods_t *methods,
+                void *driverarg, unsigned int flags, isc_mem_t *mctx,
+                dns_sdlzimplementation_t **sdlzimp)
+{
+
+       dns_sdlzimplementation_t *imp;
+       isc_result_t result;
+
+       /*
+        * Performs checks to make sure data is as we expect it to be.
+        */
+       REQUIRE(drivername != NULL);
+       REQUIRE(methods != NULL);
+       REQUIRE(methods->findzone != NULL);
+       REQUIRE(methods->lookup != NULL);
+       REQUIRE(mctx != NULL);
+       REQUIRE(sdlzimp != NULL && *sdlzimp == NULL);
+       REQUIRE((flags & ~(DNS_SDLZFLAG_RELATIVEOWNER |
+                          DNS_SDLZFLAG_RELATIVERDATA |
+                          DNS_SDLZFLAG_THREADSAFE)) == 0);
+
+       /* Write debugging message to log */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                     "Registering SDLZ driver '%s'", drivername);
+
+       /*
+        * Allocate memory for a sdlz_implementation object.  Error if
+        * we cannot.
+        */
+       imp = isc_mem_get(mctx, sizeof(dns_sdlzimplementation_t));
+       if (imp == NULL)
+               return (ISC_R_NOMEMORY);
+
+       /* Make sure memory region is set to all 0's */
+       memset(imp, 0, sizeof(dns_sdlzimplementation_t));
+
+       /* Store the data passed into this method */
+       imp->methods = methods;
+       imp->driverarg = driverarg;
+       imp->flags = flags;
+       imp->mctx = NULL;
+
+       /* attach the new sdlz_implementation object to a memory context */
+       isc_mem_attach(mctx, &imp->mctx);
+
+       /*
+        * initialize the driver lock, error if we cannot
+        * (used if a driver does not support multiple threads)
+        */
+       result = isc_mutex_init(&imp->driverlock);
+       if (result != ISC_R_SUCCESS) {
+               UNEXPECTED_ERROR(__FILE__, __LINE__,
+                                "isc_mutex_init() failed: %s",
+                                isc_result_totext(result));
+               goto cleanup_mctx;
+       }
+
+       imp->dlz_imp = NULL;
+
+       /*
+        * register the DLZ driver.  Pass in our "extra" sdlz information as
+        * a driverarg.  (that's why we stored the passed in driver arg in our
+        * sdlz_implementation structure)  Also, store the dlz_implementation
+        * structure in our sdlz_implementation.
+        */
+       result = dns_dlzregister(drivername, &sdlzmethods, imp, mctx,
+                                &imp->dlz_imp);
+
+       /* if registration fails, cleanup and get outta here. */
+       if (result != ISC_R_SUCCESS)
+               goto cleanup_mutex;
+
+       *sdlzimp = imp;
+
+       return (ISC_R_SUCCESS);
+
+ cleanup_mutex:
+       /* destroy the driver lock, we don't need it anymore */
+       DESTROYLOCK(&imp->driverlock);
+
+ cleanup_mctx:
+       /*
+        * return the memory back to the available memory pool and
+        * remove it from the memory context.
+        */
+       isc_mem_put(mctx, imp, sizeof(dns_sdlzimplementation_t));
+       isc_mem_detach(&mctx);
+       return (result);
+}
+
+void
+dns_sdlzunregister(dns_sdlzimplementation_t **sdlzimp) {
+       dns_sdlzimplementation_t *imp;
+       isc_mem_t *mctx;
+
+       /* Write debugging message to log */
+       isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+                     DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2),
+                     "Unregistering SDLZ driver.");
+
+       /*
+        * Performs checks to make sure data is as we expect it to be.
+        */
+       REQUIRE(sdlzimp != NULL && *sdlzimp != NULL);
+
+       imp = *sdlzimp;
+
+       /* Unregister the DLZ driver implementation */
+       dns_dlzunregister(&imp->dlz_imp);
+
+       /* destroy the driver lock, we don't need it anymore */
+       DESTROYLOCK(&imp->driverlock);
+
+       mctx = imp->mctx;
+
+       /*
+        * return the memory back to the available memory pool and
+        * remove it from the memory context.
+        */
+       isc_mem_put(mctx, imp, sizeof(dns_sdlzimplementation_t));
+       isc_mem_detach(&mctx);
+
+       *sdlzimp = NULL;
+}
index 00071c8275288082868a86eab11829c84c30b944..e8e38c982a9faabcbee2d543c0828c9671657f65 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: view.c,v 1.132 2005/07/12 01:00:16 marka Exp $ */
+/* $Id: view.c,v 1.133 2005/09/05 00:11:02 marka Exp $ */
 
 /*! \file */
 
@@ -31,6 +31,7 @@
 #include <dns/adb.h>
 #include <dns/cache.h>
 #include <dns/db.h>
+#include <dns/dlz.h>
 #include <dns/events.h>
 #include <dns/forward.h>
 #include <dns/keytable.h>
@@ -122,6 +123,7 @@ dns_view_create(isc_mem_t *mctx, dns_rdataclass_t rdclass,
        view->acache = NULL;
        view->cache = NULL;
        view->cachedb = NULL;
+       view->dlzdatabase = NULL;
        view->hints = NULL;
        view->resolver = NULL;
        view->adb = NULL;
@@ -269,6 +271,8 @@ destroy(dns_view_t *view) {
                isc_task_detach(&view->task);
        if (view->hints != NULL)
                dns_db_detach(&view->hints);
+       if (view->dlzdatabase != NULL)
+               dns_dlzdestroy(&view->dlzdatabase);
        if (view->cachedb != NULL)
                dns_db_detach(&view->cachedb);
        if (view->cache != NULL)
index 78d859c59aad540c386449a22ef4bac4cdc4dded..699bfdeb747430d1c06a7b9cad655973b4e88438 100644 (file)
@@ -175,6 +175,13 @@ dns_dispatchmgr_destroy
 dns_dispatchmgr_getblackhole
 dns_dispatchmgr_setblackhole
 dns_dispatchmgr_setblackportlist
+dns_dlzallowzonexfr
+dns_dlzcreate
+dns_dlzdestroy
+dns_dlzfindzone
+dns_dlzregister
+dns_dlzstrtoargv
+dns_dlzunregister
 dns_dnssec_findzonekeys
 dns_dnssec_findzonekeys2
 dns_dnssec_keyfromrdata
@@ -504,6 +511,11 @@ dns_sdb_putrr
 dns_sdb_putsoa
 dns_sdb_register
 dns_sdb_unregister
+dns_sdlz_putnamedrr
+dns_sdlz_putrr
+dns_sdlz_putsoa
+dns_sdlzregister
+dns_sdlzunregister
 dns_secalg_fromtext
 dns_secalg_totext
 dns_secproto_fromtext
index 557d46f68c794e041fd38755654b718a32b32008..0ab2ce15815e479839036898421f6df944c0b916 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: dir.c,v 1.22 2005/04/29 00:23:49 marka Exp $ */
+/* $Id: dir.c,v 1.23 2005/09/05 00:11:04 marka Exp $ */
 
 /*! \file
  * \author  Principal Authors: DCL */
@@ -57,11 +57,30 @@ isc_dir_init(isc_dir_t *dir) {
  */
 isc_result_t
 isc_dir_open(isc_dir_t *dir, const char *dirname) {
+       char *p;
        isc_result_t result = ISC_R_SUCCESS;
 
        REQUIRE(VALID_DIR(dir));
        REQUIRE(dirname != NULL);
 
+       /*
+        * Copy directory name.  Need to have enough space for the name,
+        * a possible path separator, the wildcard, and the final NUL.
+        */
+       if (strlen(dirname) + 3 > sizeof(dir->dirname))
+               /* XXXDCL ? */
+               return (ISC_R_NOSPACE);
+       strcpy(dir->dirname, dirname);
+
+       /*
+        * Append path separator, if needed, and "*".
+        */
+       p = dir->dirname + strlen(dir->dirname);
+       if (dir->dirname < p && *(p - 1) != '/')
+               *p++ = '/';
+       *p++ = '*';
+       *p++ = '\0';
+
        /*
         * Open stream.
         */
index feddcde69fc45c15c84a313f0719edc81e5dde48..55b8e9029a44aba7ba4ee29d42bfede34df393d3 100644 (file)
@@ -13,7 +13,7 @@
 # OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 # PERFORMANCE OF THIS SOFTWARE.
 
-# $Id: Makefile.in,v 1.15 2005/01/12 01:56:12 marka Exp $
+# $Id: Makefile.in,v 1.16 2005/09/05 00:11:04 marka Exp $
 
 srcdir =       @srcdir@
 VPATH =                @srcdir@
@@ -27,7 +27,7 @@ top_srcdir =  @top_srcdir@
 
 CINCLUDES =    -I. ${DNS_INCLUDES} ${ISC_INCLUDES} ${ISCCFG_INCLUDES}
 
-CDEFINES =
+CDEFINES =      @USE_DLZ@
 CWARNINGS =
 
 ISCLIBS =      ../../lib/isc/libisc.@A@
index 3613b33c9ab6d3b096881dfe54dd80d913f58341..23627f6b2d3f35bf57e7b45d85fdd3d1f3cf9736 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: namedconf.c,v 1.56 2005/08/24 23:54:04 marka Exp $ */
+/* $Id: namedconf.c,v 1.57 2005/09/05 00:11:05 marka Exp $ */
 
 /*! \file */
 
@@ -108,6 +108,33 @@ static cfg_type_t cfg_type_view;
 static cfg_type_t cfg_type_viewopts;
 static cfg_type_t cfg_type_zone;
 static cfg_type_t cfg_type_zoneopts;
+static cfg_type_t cfg_type_dynamically_loadable_zones;
+static cfg_type_t cfg_type_dynamically_loadable_zones_opts;
+
+/*
+ * Clauses that can be found in a 'dynamically loadable zones' statement
+ */
+static cfg_clausedef_t
+dynamically_loadable_zones_clauses[] = {
+       { "database", &cfg_type_astring, 0 },
+       { NULL, NULL, 0 }
+};
+
+/*
+ * A dynamically loadable zones statement.
+ */
+static cfg_tuplefielddef_t dynamically_loadable_zones_fields[] = {
+       { "name", &cfg_type_astring, 0 },
+       { "options", &cfg_type_dynamically_loadable_zones_opts, 0 },
+       { NULL, NULL, 0 }
+};
+
+static cfg_type_t cfg_type_dynamically_loadable_zones = {
+       "dlz", cfg_parse_tuple, cfg_print_tuple, cfg_doc_tuple,
+       &cfg_rep_tuple,
+       dynamically_loadable_zones_fields
+       };
+
 
 /*% tkey-dhkey */
 
@@ -567,6 +594,8 @@ static cfg_clausedef_t
 namedconf_or_view_clauses[] = {
        { "key", &cfg_type_key, CFG_CLAUSEFLAG_MULTI },
        { "zone", &cfg_type_zone, CFG_CLAUSEFLAG_MULTI },
+       /* only 1 DLZ per view allowed */
+       { "dlz", &cfg_type_dynamically_loadable_zones, 0 },
        { "server", &cfg_type_server, CFG_CLAUSEFLAG_MULTI },
        { "trusted-keys", &cfg_type_trustedkeys, CFG_CLAUSEFLAG_MULTI },
        { NULL, NULL, 0 }
@@ -873,6 +902,7 @@ view_clausesets[] = {
        namedconf_or_view_clauses,
        view_clauses,
        zone_clauses,
+       dynamically_loadable_zones_clauses,
        NULL
 };
 static cfg_type_t cfg_type_viewopts = {
@@ -887,8 +917,22 @@ zone_clausesets[] = {
        NULL
 };
 static cfg_type_t cfg_type_zoneopts = {
-       "zoneopts", cfg_parse_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, zone_clausesets };
-
+       "zoneopts", cfg_parse_map, cfg_print_map, 
+       cfg_doc_map, &cfg_rep_map, zone_clausesets };
+/*% The "dynamically loadable zones" statement syntax. */
+static cfg_clausedef_t *
+dynamically_loadable_zones_clausesets[] = {
+       dynamically_loadable_zones_clauses,
+       NULL
+};
+static cfg_type_t cfg_type_dynamically_loadable_zones_opts = {
+       "dynamically_loadable_zones_opts", cfg_parse_map, 
+       cfg_print_map, cfg_doc_map, &cfg_rep_map,
+       dynamically_loadable_zones_clausesets 
+};
 /*%
  * Clauses that can be found within the 'key' statement.
  */
@@ -905,7 +949,9 @@ key_clausesets[] = {
        NULL
 };
 static cfg_type_t cfg_type_key = {
-       "key", cfg_parse_named_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, key_clausesets };
+       "key", cfg_parse_named_map, cfg_print_map,
+       cfg_doc_map, &cfg_rep_map, key_clausesets 
+};
 
 
 /*%
index ac9dc21468238b11afbb897db3815ef528a83aa0..f60f5105a00c47a1f0c0ddaa9714b9ac9def47fa 100644 (file)
@@ -15,7 +15,7 @@
  * PERFORMANCE OF THIS SOFTWARE.
  */
 
-/* $Id: parser.c,v 1.118 2005/08/23 02:36:10 marka Exp $ */
+/* $Id: parser.c,v 1.119 2005/09/05 00:11:05 marka Exp $ */
 
 /*! \file */
 
@@ -1087,7 +1087,6 @@ cfg_print_spacelist(cfg_printer_t *pctx, cfg_obj_t *obj) {
        }
 }
 
-
 isc_boolean_t
 cfg_obj_islist(cfg_obj_t *obj) {
        REQUIRE(obj != NULL);