]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
[master] DLZ modules: filesystem, ldap, wildcard
authorEvan Hunt <each@isc.org>
Mon, 11 Mar 2013 23:49:52 +0000 (16:49 -0700)
committerEvan Hunt <each@isc.org>
Tue, 12 Mar 2013 00:03:46 +0000 (17:03 -0700)
3523. [contrib] Ported filesystem and ldap DLZ drivers to
dynamically-loadable modules, and added the
"wildcard" module based on a contribution from
Vadim Goncharov <vgoncharov@nic.ru>. [RT #23569]

26 files changed:
CHANGES
bin/named/unix/dlz_dlopen_driver.c
bin/named/win32/dlz_dlopen_driver.c
contrib/dlz/modules/bdbhpt/Makefile
contrib/dlz/modules/bdbhpt/dlz_bdbhpt_dynamic.c
contrib/dlz/modules/common/dlz_dbi.c [new file with mode: 0644]
contrib/dlz/modules/filesystem/Makefile [new file with mode: 0644]
contrib/dlz/modules/filesystem/dir.c [new file with mode: 0644]
contrib/dlz/modules/filesystem/dir.h [new file with mode: 0644]
contrib/dlz/modules/filesystem/dlz_filesystem_dynamic.c [new file with mode: 0644]
contrib/dlz/modules/include/dlz_dbi.h [new file with mode: 0644]
contrib/dlz/modules/include/dlz_list.h [new file with mode: 0644]
contrib/dlz/modules/include/dlz_minimal.h [moved from contrib/dlz/modules/dlz_minimal.h with 93% similarity]
contrib/dlz/modules/include/dlz_pthread.h [new file with mode: 0644]
contrib/dlz/modules/ldap/Makefile [new file with mode: 0644]
contrib/dlz/modules/ldap/dlz_ldap_dynamic.c [new file with mode: 0644]
contrib/dlz/modules/ldap/testing/README [new file with mode: 0644]
contrib/dlz/modules/ldap/testing/dlz.schema [new file with mode: 0644]
contrib/dlz/modules/ldap/testing/example.ldif [new file with mode: 0644]
contrib/dlz/modules/ldap/testing/named.conf [new file with mode: 0644]
contrib/dlz/modules/ldap/testing/slapd.conf [new file with mode: 0644]
contrib/dlz/modules/wildcard/Makefile [new file with mode: 0644]
contrib/dlz/modules/wildcard/README [new file with mode: 0644]
contrib/dlz/modules/wildcard/dlz_wildcard_dynamic.c [new file with mode: 0644]
contrib/dlz/modules/wildcard/testing/named.conf [new file with mode: 0644]
lib/dns/sdlz.c

diff --git a/CHANGES b/CHANGES
index d66584e5f5dd4e96657ed1be8fb48898350849c5..5e0d74c41bacf7e1985661ff214eee086e502f1b 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,8 @@
+3523.  [contrib]       Ported filesystem and ldap DLZ drivers to
+                       dynamically-loadable modules, and added the
+                       "wildcard" module based on a contribution from
+                       Vadim Goncharov <vgoncharov@nic.ru>. [RT #23569]
+
 3522.  [bug]           DLZ lookups could fail to return SERVFAIL when
                        they ought to. [RT #32685]
 
index a294b18554990449b11443b26ea59835763e29c5..77c10239f808a4120461ff827152b967e20288ec 100644 (file)
@@ -602,6 +602,7 @@ dlz_dlopen_init(isc_mem_t *mctx) {
 
        result = dns_sdlzregister("dlopen", &dlz_dlopen_methods, NULL,
                                  DNS_SDLZFLAG_RELATIVEOWNER |
+                                 DNS_SDLZFLAG_RELATIVERDATA |
                                  DNS_SDLZFLAG_THREADSAFE,
                                  mctx, &dlz_dlopen);
 
index f4982f354d07e12f97ab6c017f80f47158252ab6..20448bda45d7eba30175c71c2df79f257f789c23 100644 (file)
@@ -580,6 +580,7 @@ dlz_dlopen_init(isc_mem_t *mctx) {
 
        result = dns_sdlzregister("dlopen", &dlz_dlopen_methods, NULL,
                                  DNS_SDLZFLAG_RELATIVEOWNER |
+                                 DNS_SDLZFLAG_RELATIVERDATA |
                                  DNS_SDLZFLAG_THREADSAFE,
                                  mctx, &dlz_dlopen);
 
index 76f590a8bef33d4df19fb3ad6475cadfc4863e3c..795a2e8b9221ea2b8430b55af3597d13baac9aac 100644 (file)
@@ -1,12 +1,12 @@
 prefix = /usr
 libdir = $(prefix)/lib/bind9
 
-CFLAGS=-fPIC -g
+CFLAGS=-fPIC -g -I../include
 BDB_LIBS=-ldb
 
 all: dlz_bdbhpt_dynamic.so
 
-dlz_bdbhpt_dynamic.so:
+dlz_bdbhpt_dynamic.so: dlz_bdbhpt_dynamic.c
        $(CC) $(CFLAGS) -shared -o dlz_bdbhpt_dynamic.so \
                dlz_bdbhpt_dynamic.c $(BDB_LIBS)
 
index ecfa1018b23b577ad2da5b72a99468e2b1f247d4..9de2e451c576bb5b7c0770d2c66d7ff82b831886 100644 (file)
 
 #include <db.h>
 
-#include "../dlz_minimal.h"
-
-#ifdef WIN32
-#define STRTOK_R(a, b, c)      strtok_s(a, b, c)
-#elif defined(_REENTRANT)
-#define STRTOK_R(a, b, c)       strtok_r(a, b, c)
-#else
-#define STRTOK_R(a, b, c)       strtok(a, b)
-#endif
+#include "dlz_minimal.h"
 
 /* should the bdb driver use threads. */
 #ifdef ISC_PLATFORM_USETHREADS
  *
  */
 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 */
-
-  /* Helper functions from the dlz_dlopen driver */
-  log_t *log;
-  dns_sdlz_putrr_t *putrr;
-  dns_sdlz_putnamedrr_t *putnamedrr;
-  dns_dlz_writeablezone_t *writeable_zone;
+       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 */
+
+       /* Helper functions from the dlz_dlopen driver */
+       log_t *log;
+       dns_sdlz_putrr_t *putrr;
+       dns_sdlz_putnamedrr_t *putnamedrr;
+       dns_dlz_writeablezone_t *writeable_zone;
 } bdbhpt_instance_t;
 
-
 typedef struct bdbhpt_parsed_data {
-  char *host;
-  char *type;
-  int ttl;
-  char *data;
+       char *host;
+       char *type;
+       int ttl;
+       char *data;
 } bdbhpt_parsed_data_t;
 
-/*
+static void
+b9_add_helper(struct bdbhpt_instance *db, const char *helper_name, void *ptr);
+
+/*%
  * 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;
+       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(log_t *log, char *in, bdbhpt_parsed_data_t *pd) {
-  char *endp, *ttlStr;
-  char *tmp = in;
-  char *lastchar = (char *) &tmp[strlen(tmp)];
-  
-  /*
-   * String should be formatted 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) {
-    log(ISC_LOG_ERROR,
-        "bdbhpt_dynamic: ttl must be a positive number");
-    return ISC_R_FAILURE;
-  }
-  
-  /* if we get this far everything should have worked. */
-  return ISC_R_SUCCESS;
+
+       char *endp, *ttlStr;
+       char *tmp = in;
+       char *lastchar = (char *) &tmp[strlen(tmp)];
+  
+       /*%
+        * String should be formatted 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) {
+               log(ISC_LOG_ERROR,
+                               "bdbhpt_dynamic: "
+                               "ttl must be a positive number");
+               return ISC_R_FAILURE;
+       }
+       
+       /* if we get this far everything should have worked. */
+       return ISC_R_SUCCESS;
 }
 
 /*
- * Return the version of the API
+ * See if a zone transfer is allowed
  */
-int
-dlz_version(unsigned int *flags) {
-  UNUSED(flags);
-  return (DLZ_DLOPEN_VERSION);
+isc_result_t
+dlz_allowzonexfr(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. */
+#if DLZ_DLOPEN_VERSION >= 3
+       result = dlz_findzonedb(dbdata, name, NULL, NULL);
+#else
+       result = dlz_findzonedb(dbdata, name);
+#endif
+       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;
 }
 
-/*
- * Remember a helper function from the bind9 dlz_dlopen driver
+/*%
+ * Perform a zone transfer
+ *
+ * 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 void
-b9_add_helper(struct bdbhpt_instance *db,
-             const char *helper_name, void *ptr) {
-  if (strcmp(helper_name, "log") == 0)
-    db->log = (log_t *)ptr;
-  if (strcmp(helper_name, "putrr") == 0)
-    db->putrr = (dns_sdlz_putrr_t *)ptr;
-  if (strcmp(helper_name, "putnamedrr") == 0)
-    db->putnamedrr = (dns_sdlz_putnamedrr_t *)ptr;
-  if (strcmp(helper_name, "writeable_zone") == 0)
-    db->writeable_zone = (dns_dlz_writeablezone_t *)ptr;
+isc_result_t
+dlz_allnodes(const char *zone, 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;
+       
+       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(db->log,
+                                             tmp, &pd) != ISC_R_SUCCESS)
+                               goto allnodes_cleanup;
+                       result = db->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(dns_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) {
-  /* 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);
-}
-
-/*
- * Initialises, sets flags and then opens Berkeley databases.
- *
- */
-static isc_result_t
-bdbhpt_opendb(log_t *log, DB_ENV *db_env, DBTYPE db_type, DB **db, const char *db_name,
-              char *db_file, int flags) {
-  int result;
-
-  /* Initialise the database. */
-  if ((result = db_create(db, db_env, 0)) != 0) {
-    log(ISC_LOG_ERROR,
-        "bdbhpt_dynamic: could not initialize %s database. "
-        "BerkeleyDB error: %s",
-        db_name, db_strerror(result));
-    return ISC_R_FAILURE;
-  }
-
-  /* set database flags. */
-  if ((result = (*db)->set_flags(*db, flags)) != 0) {
-    log(ISC_LOG_ERROR,
-        "bdbhpt_dynamic: could not set flags for %s database. "
-        "BerkeleyDB 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) {
-    log(ISC_LOG_ERROR,
-        "bdbhpt_dynamic: could not open %s database in %s. "
-        "BerkeleyDB error: %s",
-        db_name, db_file, db_strerror(result));
-    return ISC_R_FAILURE;
-  }
-  
-  return ISC_R_SUCCESS;
-}
-
-
-/*
- * Called to initialize the driver
- */
-isc_result_t
-dlz_create(const char *dlzname, unsigned int argc, char *argv[],
-          void **dbdata, ...)
-{
-  isc_result_t result;
-  int bdbhptres;
-  int bdbFlags = 0;
-  bdbhpt_instance_t *db = NULL;
-  
-  const char *helper_name;
-  va_list ap;
-
-  UNUSED(dlzname);
-
-  /* Allocate memory for our db structures and helper functions */
-  db = calloc(1, sizeof(struct bdbhpt_instance));
-  if (db == NULL)
-    return (ISC_R_NOMEMORY);
-
-  /* Fill in the helper functions */
-  va_start(ap, dbdata);
-  while ((helper_name = va_arg(ap, const char *)) != NULL) {
-    b9_add_helper(db, helper_name, va_arg(ap, void*));
-  }
-  va_end(ap);
-
-  /* verify we have 4 arg's passed to the driver */
-  if (argc != 4) {
-    db->log(ISC_LOG_ERROR,
-            "bdbhpt_dynamic: please supply 3 command line args.  "
-            "You supplied: %s",
-            argc);
-    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;
-    db->log(ISC_LOG_INFO,
-            "bdbhpt_dynamic: using transactional mode.");
-    break;
-
-    /*
-     * Concurrent mode.  Lower safety (no rollback) -
-     * higher speed.
-     */
-  case 'C':
-  case 'c':
-    bdbFlags = DB_INIT_CDB | DB_INIT_MPOOL;
-    db->log(ISC_LOG_INFO,
-            "bdbhpt_dynamic: 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;
-    db->log(ISC_LOG_INFO,
-            "bdbhpt_dynamic: using private mode.");
-    break;
-  default:
-    db->log(ISC_LOG_ERROR,
-            "bdbhpt_dynamic: operating mode must be set to P or C or T.  "
-            "You specified '%s'",
-            argv[1]);
-    return (ISC_R_FAILURE);
-  }
-  
-  /*
-   * create bdbhpt environment
-   * Basically bdbhpt allocates and assigns memory to db->dbenv
-   */
-  bdbhptres = db_env_create(&db->dbenv, 0);
-  if (bdbhptres != 0) {
-    db->log(ISC_LOG_ERROR,
-            "bdbhpt_dynamic: db environment could not be created. "
-            "BerkeleyDB 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) {
-    db->log(ISC_LOG_ERROR,
-            "bdbhpt_dynamic: db environment at '%s' could not be opened. "
-            "BerkeleyDB error: %s",
-            argv[2], db_strerror(bdbhptres));
-    result = ISC_R_FAILURE;
-    goto init_cleanup;
-  }
-
-  /* open dlz_data database. */
-  result = bdbhpt_opendb(db->log, 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->log, 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->log, 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->log, db->dbenv, DB_UNKNOWN, &db->client,
-                         dlz_client, argv[3], DB_DUP | DB_DUPSORT);
-  if (result != ISC_R_SUCCESS)
-    goto init_cleanup;
-  
-  *dbdata = db;
-
-  db->log(ISC_LOG_INFO,
-          "bdbhpt_dynamic: version %s, started", dlz_bdbhpt_dynamic_version);
-  return(ISC_R_SUCCESS);
-  
- init_cleanup:
-  bdbhpt_cleanup(db);
-  return result;
-}
-
-/*
- * Shut down the backend
- */
-void
-dlz_destroy(void *dbdata) {
-  struct bdbhpt_instance *db = (struct bdbhpt_instance *)dbdata;
-  
-  db->log(ISC_LOG_INFO,
-          "dlz_bdbhpt_dynamic (%s): shutting down", dlz_bdbhpt_dynamic_version);
-  bdbhpt_cleanup((bdbhpt_instance_t *) dbdata);
-  free (db);
+       /* 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);
 }
 
-
 /*
  * See if we handle a given zone
  */
@@ -491,56 +471,56 @@ dlz_findzonedb(void *dbdata, const char *name)
 #else
 isc_result_t
 dlz_findzonedb(void *dbdata, const char *name,
-          dns_clientinfomethods_t *methods,
-          dns_clientinfo_t *clientinfo)
+                dns_clientinfomethods_t *methods,
+                dns_clientinfo_t *clientinfo)
 #endif
 {
-  isc_result_t result;
-  bdbhpt_instance_t *db = (bdbhpt_instance_t *) dbdata;
-  DBT key, data;
-  
-  memset(&key, 0, sizeof(DBT));
-  memset(&data, 0, sizeof(DBT));
-  data.flags = DB_DBT_MALLOC;
+       isc_result_t result;
+       bdbhpt_instance_t *db = (bdbhpt_instance_t *) dbdata;
+       DBT key, data;
+       
+       memset(&key, 0, sizeof(DBT));
+       memset(&data, 0, sizeof(DBT));
+       data.flags = DB_DBT_MALLOC;
 
 #if DLZ_DLOPEN_VERSION >= 3
-  UNUSED(methods);
-  UNUSED(clientinfo);
+       UNUSED(methods);
+       UNUSED(clientinfo);
 #endif
 
-  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;
+       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;
 }
 
 /*
@@ -549,7 +529,7 @@ dlz_findzonedb(void *dbdata, const char *name,
  */
 #if DLZ_DLOPEN_VERSION == 1
 isc_result_t dlz_lookup(const char *zone, const char *name, 
-                        void *dbdata, dns_sdlzlookup_t *lookup)
+                       void *dbdata, dns_sdlzlookup_t *lookup)
 #else
 isc_result_t dlz_lookup(const char *zone, const char *name, void *dbdata,
                        dns_sdlzlookup_t *lookup,
@@ -557,280 +537,293 @@ isc_result_t dlz_lookup(const char *zone, const char *name, void *dbdata,
                        dns_clientinfo_t *clientinfo)
 #endif
 {
-
-  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;
-  
+       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;
+       
 #if DLZ_DLOPEN_VERSION >= 2
-  UNUSED(methods);
-  UNUSED(clientinfo);
+       UNUSED(methods);
+       UNUSED(clientinfo);
 #endif
 
-  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(db->log, tmp, &pd) != ISC_R_SUCCESS)
-      goto lookup_cleanup;
-    
-    result = db->putrr(lookup, pd.type, pd.ttl, pd.data);
-    
-    if (result != ISC_R_SUCCESS)
-      goto lookup_cleanup;
-  } /* end while loop */
-  
+       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(db->log, tmp, &pd) != ISC_R_SUCCESS)
+                       goto lookup_cleanup;
+               
+               result = db->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;
+       /* 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;
+}
+
+/*%
+ * Initialises, sets flags and then opens Berkeley databases.
+ */
+static isc_result_t
+bdbhpt_opendb(log_t *log, DB_ENV *db_env, DBTYPE db_type, DB **db,
+             const char *db_name, char *db_file, int flags)
+{
+       int result;
+
+       /* Initialise the database. */
+       if ((result = db_create(db, db_env, 0)) != 0) {
+               log(ISC_LOG_ERROR,
+                   "bdbhpt_dynamic: could not initialize %s database. "
+                   "BerkeleyDB error: %s",
+                   db_name, db_strerror(result));
+               return ISC_R_FAILURE;
+       }
+
+       /* set database flags. */
+       if ((result = (*db)->set_flags(*db, flags)) != 0) {
+               log(ISC_LOG_ERROR,
+                   "bdbhpt_dynamic: could not set flags for %s database. "
+                   "BerkeleyDB 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) {
+               log(ISC_LOG_ERROR,
+                   "bdbhpt_dynamic: could not open %s database in %s. "
+                   "BerkeleyDB error: %s",
+                   db_name, db_file, db_strerror(result));
+               return ISC_R_FAILURE;
+       }
+       
+       return ISC_R_SUCCESS;
 }
 
 
 /*
- * See if a zone transfer is allowed
+ * Called to initialize the driver
  */
 isc_result_t
-dlz_allowzonexfr(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. */
-#if DLZ_DLOPEN_VERSION >= 3
-  result = dlz_findzonedb(dbdata, name, NULL, NULL);
-#else
-  result = dlz_findzonedb(dbdata, name);
-#endif
-  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;
-  }
+dlz_create(const char *dlzname, unsigned int argc, char *argv[],
+                void **dbdata, ...)
+{
+       isc_result_t result;
+       int bdbhptres;
+       int bdbFlags = 0;
+       bdbhpt_instance_t *db = NULL;
+       
+       const char *helper_name;
+       va_list ap;
+
+       UNUSED(dlzname);
+
+       /* Allocate memory for our db structures and helper functions */
+       db = calloc(1, sizeof(struct bdbhpt_instance));
+       if (db == NULL)
+               return (ISC_R_NOMEMORY);
+
+       /* Fill in the helper functions */
+       va_start(ap, dbdata);
+       while ((helper_name = va_arg(ap, const char *)) != NULL)
+               b9_add_helper(db, helper_name, va_arg(ap, void*));
+       va_end(ap);
+
+       /* verify we have 4 arg's passed to the driver */
+       if (argc != 4) {
+               db->log(ISC_LOG_ERROR,
+                       "bdbhpt_dynamic: please supply 3 command line args. "
+                       "You supplied: %s", argc);
+               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;
+               db->log(ISC_LOG_INFO,
+                       "bdbhpt_dynamic: using transactional mode.");
+               break;
+
+               /*
+                * Concurrent mode.      Lower safety (no rollback) -
+                * higher speed.
+                */
+       case 'C':
+       case 'c':
+               bdbFlags = DB_INIT_CDB | DB_INIT_MPOOL;
+               db->log(ISC_LOG_INFO,
+                       "bdbhpt_dynamic: 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;
+               db->log(ISC_LOG_INFO,
+                       "bdbhpt_dynamic: using private mode.");
+               break;
+       default:
+               db->log(ISC_LOG_ERROR,
+                       "bdbhpt_dynamic: "
+                       "operating mode must be set to P or C or T. "
+                       "You specified '%s'", argv[1]);
+               return (ISC_R_FAILURE);
+       }
+       
+       /*
+        * create bdbhpt environment
+        * Basically bdbhpt allocates and assigns memory to db->dbenv
+        */
+       bdbhptres = db_env_create(&db->dbenv, 0);
+       if (bdbhptres != 0) {
+               db->log(ISC_LOG_ERROR,
+                       "bdbhpt_dynamic: db environment could not be created. "
+                       "BerkeleyDB error: %s",jdb_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) {
+               db->log(ISC_LOG_ERROR,
+                       "bdbhpt_dynamic: "
+                       "db environment at '%s' could not be opened. "
+                       "BerkeleyDB error: %s",
+                       argv[2], db_strerror(bdbhptres));
+               result = ISC_R_FAILURE;
+               goto init_cleanup;
+       }
+
+       /* open dlz_data database. */
+       result = bdbhpt_opendb(db->log, 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->log, 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->log, 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->log, db->dbenv, DB_UNKNOWN, &db->client,
+                              dlz_client, argv[3], DB_DUP | DB_DUPSORT);
+       if (result != ISC_R_SUCCESS)
+               goto init_cleanup;
+       
+       *dbdata = db;
+
+       db->log(ISC_LOG_INFO,
+               "bdbhpt_dynamic: version %s, started",
+               dlz_bdbhpt_dynamic_version);
+       return(ISC_R_SUCCESS);
+       
+ init_cleanup:
+       bdbhpt_cleanup(db);
+       return result;
+}
 
- xfr_cleanup:
+/*
+ * Shut down the backend
+ */
+void
+dlz_destroy(void *dbdata) {
+       struct bdbhpt_instance *db = (struct bdbhpt_instance *)dbdata;
+       
+       db->log(ISC_LOG_INFO,
+               "dlz_bdbhpt_dynamic (%s): shutting down",
+               dlz_bdbhpt_dynamic_version);
+       bdbhpt_cleanup((bdbhpt_instance_t *) dbdata);
+       free(db);
+}
 
-  /* 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;
+/*
+ * Return the version of the API
+ */
+int
+dlz_version(unsigned int *flags) {
+       UNUSED(flags);
+       return (DLZ_DLOPEN_VERSION);
 }
 
 /*
- * Perform a zone transfer
- *
- * 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).
+ * Register a helper function from the bind9 dlz_dlopen driver
  */
-isc_result_t
-dlz_allnodes(const char *zone, 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;
-  
-  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(db->log, tmp, &pd) != ISC_R_SUCCESS)
-        goto allnodes_cleanup;
-      result = db->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(dns_cursor);
-  
-  return result;
+static void
+b9_add_helper(struct bdbhpt_instance *db, const char *helper_name, void *ptr) {
+       if (strcmp(helper_name, "log") == 0)
+               db->log = (log_t *)ptr;
+       if (strcmp(helper_name, "putrr") == 0)
+               db->putrr = (dns_sdlz_putrr_t *)ptr;
+       if (strcmp(helper_name, "putnamedrr") == 0)
+               db->putnamedrr = (dns_sdlz_putnamedrr_t *)ptr;
+       if (strcmp(helper_name, "writeable_zone") == 0)
+               db->writeable_zone = (dns_dlz_writeablezone_t *)ptr;
 }
+
diff --git a/contrib/dlz/modules/common/dlz_dbi.c b/contrib/dlz/modules/common/dlz_dbi.c
new file mode 100644 (file)
index 0000000..1a5dc0b
--- /dev/null
@@ -0,0 +1,461 @@
+/*
+ * 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) 2013  Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001  Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or 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.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <sys/errno.h>
+
+#include <dlz_minimal.h>
+#include <dlz_list.h>
+#include <dlz_dbi.h>
+#include <dlz_pthread.h>
+
+/*%
+ * properly destroys a querylist by de-allocating the
+ * memory for each query segment, and then the list itself
+ */
+
+void
+destroy_querylist(query_list_t **querylist) {
+       query_segment_t *tseg = NULL;
+       query_segment_t *nseg = NULL;
+
+       /* if query list is null, nothing to do */
+       if (*querylist == NULL)
+               return;
+
+       /* start at the top of the list */
+       nseg = DLZ_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->cmd != NULL && tseg->direct == ISC_TRUE)
+                       free(tseg->cmd);
+               /* get the next query segment, before we destroy this one. */
+               nseg = DLZ_LIST_NEXT(nseg, link);
+               /* deallocate this query segment. */
+               free(tseg);
+       }
+       /* deallocate the query segment list */
+       free(*querylist);
+}
+
+/*% constructs a query list by parsing a string into query segments */
+isc_result_t
+build_querylist(const char *query_str, char **zone, char **record,
+               char **client, query_list_t **querylist, unsigned int flags, 
+               log_t log)
+{
+       isc_result_t result;
+       isc_boolean_t foundzone = ISC_FALSE;
+       isc_boolean_t foundrecord = ISC_FALSE;
+       isc_boolean_t foundclient = ISC_FALSE;
+       char *temp_str = NULL;
+       char *right_str = NULL;
+       query_list_t *tql;
+       query_segment_t *tseg = NULL;
+
+       /* if query string is null, or zero length */
+       if (query_str == NULL || strlen(query_str) < 1) {
+               if ((flags & 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 = calloc(1, sizeof(query_list_t));
+       /* couldn't allocate memory.  Problem!! */
+       if (tql == NULL)
+               return (ISC_R_NOMEMORY);
+
+       /* initialize the query segment list */
+       DLZ_LIST_INIT(*tql);
+
+       /* make a copy of query_str so we can chop it up */
+       temp_str = right_str = strdup(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 = calloc(1, sizeof(query_segment_t));
+               if (tseg  == NULL) {    /* no memory, clean everything up. */
+                       result = ISC_R_NOMEMORY;
+                       goto cleanup;
+               }
+               tseg->cmd = NULL;
+               tseg->direct = ISC_FALSE;
+               /* initialize the query segment link */
+               DLZ_LINK_INIT(tseg, link);
+               /* append the query segment to the list */
+               DLZ_LIST_APPEND(*tql, tseg, link);
+
+               /*
+                * split string at the first "$". set query segment to
+                * left portion
+                */
+               tseg->cmd = strdup(strsep(&right_str, "$"));
+               if (tseg->cmd == NULL) {
+                       /* no memory, clean everything up. */
+                       result = ISC_R_NOMEMORY;
+                       goto cleanup;
+               }
+               /* tseg->cmd points directly to a string. */
+               tseg->direct = ISC_TRUE;
+               tseg->strlen = strlen(tseg->cmd);
+
+               /* check if we encountered "$zone$" token */
+               if (strcasecmp(tseg->cmd, "zone") == 0) {
+                       /*
+                        * we don't really need, or want the "zone"
+                        * text, so get rid of it.
+                        */
+                       free(tseg->cmd);
+                       /* set tseg->cmd to in-direct zone string */
+                       tseg->cmd = (char**) zone;
+                       tseg->strlen = 0;
+                       /* tseg->cmd points in-directly to a string */
+                       tseg->direct = ISC_FALSE;
+                       foundzone = ISC_TRUE;
+                       /* check if we encountered "$record$" token */
+               } else if (strcasecmp(tseg->cmd, "record") == 0) {
+                       /*
+                        * we don't really need, or want the "record"
+                        * text, so get rid of it.
+                        */
+                       free(tseg->cmd);
+                       /* set tseg->cmd to in-direct record string */
+                       tseg->cmd = (char**) record;
+                       tseg->strlen = 0;
+                       /* tseg->cmd points in-directly poinsts to a string */
+                       tseg->direct = ISC_FALSE;
+                       foundrecord = ISC_TRUE;
+                       /* check if we encountered "$client$" token */
+               } else if (strcasecmp(tseg->cmd, "client") == 0) {
+                       /*
+                        * we don't really need, or want the "client"
+                        * text, so get rid of it.
+                        */
+                       free(tseg->cmd);
+                       /* set tseg->cmd to in-direct record string */
+                       tseg->cmd = (char**) client;
+                       tseg->strlen = 0;
+                       /* tseg->cmd points in-directly poinsts to a string */
+                       tseg->direct = ISC_FALSE;
+                       foundclient = ISC_TRUE;
+               }
+       }
+
+       /* we don't need temp_str any more */
+       free(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 & REQUIRE_CLIENT) != 0) && (!foundclient) ) {
+               /* Write error message to log */
+               if (log != NULL)
+                       log(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 & REQUIRE_RECORD) != 0) && (!foundrecord) ) {
+               /* Write error message to log */
+               if (log != NULL)
+                       log(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 & REQUIRE_ZONE) != 0) && (!foundzone) ) {
+               /* Write error message to log */
+               if (log != NULL)
+                       log(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)
+               free(temp_str);
+
+ flag_fail:
+       /* get rid of what was build of the query list */
+       if (tql != NULL)
+               destroy_querylist(&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 *
+build_querystring(query_list_t *querylist) {
+       query_segment_t *tseg = NULL;
+       unsigned int length = 0;
+       char *qs = NULL;
+
+       /* start at the top of the list */
+       tseg = DLZ_LIST_HEAD(*querylist);
+       while (tseg != NULL) {
+               /*
+                * if this is a query segment, use the
+                * precalculated string length
+                */
+               if (tseg->direct == ISC_TRUE)
+                       length += tseg->strlen;
+               else    /* calculate string length for dynamic segments. */
+                       length += strlen(* (char**) tseg->cmd);
+               /* get the next segment */
+               tseg = DLZ_LIST_NEXT(tseg, link);
+       }
+
+       /* allocate memory for the string */
+       qs = malloc(length + 1);
+       /* couldn't allocate memory,  We need more ram! */
+       if (qs == NULL)
+               return (NULL);
+
+       *qs = 0;
+       /* start at the top of the list again */
+       tseg = DLZ_LIST_HEAD(*querylist);
+       while (tseg != NULL) {
+               if (tseg->direct == ISC_TRUE)
+                       /* query segments */
+                       strcat(qs, tseg->cmd);
+               else
+                       /* dynamic segments */
+                       strcat(qs, * (char**) tseg->cmd);
+               /* get the next segment */
+               tseg = DLZ_LIST_NEXT(tseg, link);
+       }
+
+       return (qs);
+}
+
+/*% constructs a dbinstance (DBI) */
+isc_result_t
+build_dbinstance(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, log_t log)
+{
+
+       isc_result_t result;
+       dbinstance_t *db = NULL;
+       int err;
+
+       /* allocate and zero memory for driver structure */
+       db = calloc(1, sizeof(dbinstance_t));
+       if (db == NULL) {
+               if (log != NULL)
+                       log(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->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;
+
+       /* initialize the reference count mutex */
+       err = dlz_mutex_init(&db->lock, NULL);
+       if (err == ENOMEM) {
+               result = ISC_R_NOMEMORY;
+               goto cleanup;
+       } else if (err != 0) {
+               result = ISC_R_UNEXPECTED;
+               goto cleanup;
+       }
+
+       /* build the all nodes query list */
+       result = build_querylist(allnodes_str, &db->zone, &db->record,
+                                &db->client, &db->allnodes_q,
+                                REQUIRE_ZONE, log);
+       /* if unsuccessful, log err msg and cleanup */
+       if (result != ISC_R_SUCCESS) {
+               if (log != NULL)
+                       log(ISC_LOG_ERROR,
+                           "Could not build all nodes query list");
+               goto cleanup;
+       }
+
+       /* build the allow zone transfer query list */
+       result = build_querylist(allowxfr_str, &db->zone, &db->record,
+                                &db->client, &db->allowxfr_q,
+                                REQUIRE_ZONE | REQUIRE_CLIENT,
+                                log);
+       /* if unsuccessful, log err msg and cleanup */
+       if (result != ISC_R_SUCCESS) {
+               if (log != NULL)
+                       log(ISC_LOG_ERROR,
+                           "Could not build allow xfr query list");
+               goto cleanup;
+       }
+
+       /* build the authority query, query list */
+       result = build_querylist(authority_str, &db->zone, &db->record,
+                                &db->client, &db->authority_q,
+                                REQUIRE_ZONE, log);
+       /* if unsuccessful, log err msg and cleanup */
+       if (result != ISC_R_SUCCESS) {
+               if (log != NULL)
+                       log(ISC_LOG_ERROR,
+                           "Could not build authority query list");
+               goto cleanup;
+       }
+
+       /* build findzone query, query list */
+       result = build_querylist(findzone_str, &db->zone, &db->record,
+                                &db->client, &db->findzone_q,
+                                REQUIRE_ZONE, log);
+       /* if unsuccessful, log err msg and cleanup */
+       if (result != ISC_R_SUCCESS) {
+               if (log != NULL)
+                       log(ISC_LOG_ERROR,
+                           "Could not build find zone query list");
+               goto cleanup;
+       }
+
+       /* build countzone query, query list */
+       result = build_querylist(countzone_str, &db->zone, &db->record,
+                                &db->client, &db->countzone_q,
+                                REQUIRE_ZONE, log);
+       /* if unsuccessful, log err msg and cleanup */
+       if (result != ISC_R_SUCCESS) {
+               if (log != NULL)
+                       log(ISC_LOG_ERROR,
+                           "Could not build count zone query list");
+               goto cleanup;
+       }
+
+       /* build lookup query, query list */
+       result = build_querylist(lookup_str, &db->zone, &db->record,
+                                &db->client, &db->lookup_q,
+                                REQUIRE_RECORD, log);
+       /* if unsuccessful, log err msg and cleanup */
+       if (result != ISC_R_SUCCESS) {
+               if (log != NULL)
+                       log(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_dbinstance(db);
+       /* return failure */
+       return (ISC_R_FAILURE);
+}
+
+void
+destroy_dbinstance(dbinstance_t *dbi) {
+       /* destroy any query lists we created */
+       destroy_querylist(&dbi->allnodes_q);
+       destroy_querylist(&dbi->allowxfr_q);
+       destroy_querylist(&dbi->authority_q);
+       destroy_querylist(&dbi->findzone_q);
+       destroy_querylist(&dbi->countzone_q);
+       destroy_querylist(&dbi->lookup_q);
+
+       /* get rid of the mutex */
+       (void) dlz_mutex_destroy(&dbi->lock);
+
+       /* return, and detach the memory */
+       free(dbi);
+}
diff --git a/contrib/dlz/modules/filesystem/Makefile b/contrib/dlz/modules/filesystem/Makefile
new file mode 100644 (file)
index 0000000..fd87ee8
--- /dev/null
@@ -0,0 +1,20 @@
+prefix = /usr
+libdir = $(prefix)/lib/bind9
+
+CFLAGS=-fPIC -g -I../include
+
+all: dlz_filesystem_dynamic.so
+
+dir.o: dir.c
+       $(CC) $(CFLAGS) -c dir.c
+
+dlz_filesystem_dynamic.so: dlz_filesystem_dynamic.c dir.o
+       $(CC) $(CFLAGS) -shared -o dlz_filesystem_dynamic.so \
+               dlz_filesystem_dynamic.c dir.o
+
+clean:
+       rm -f dlz_filesystem_dynamic.so *.o
+
+install: dlz_filesystem_dynamic.so
+       mkdir -p $(DESTDIR)$(libdir)
+       install dlz_filesystem_dynamic.so $(DESTDIR)$(libdir)
diff --git a/contrib/dlz/modules/filesystem/dir.c b/contrib/dlz/modules/filesystem/dir.c
new file mode 100644 (file)
index 0000000..e5f1ac4
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or 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.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "dlz_minimal.h"
+#include "dir.h"
+
+void
+dir_init(dir_t *dir) {
+       dir->entry.name[0] = '\0';
+       dir->entry.length = 0;
+
+       dir->handle = NULL;
+}
+
+isc_result_t
+dir_open(dir_t *dir, const char *dirname) {
+       char *p;
+       isc_result_t result = ISC_R_SUCCESS;
+
+       if (strlen(dirname) + 3 > sizeof(dir->dirname))
+               return (ISC_R_NOSPACE);
+       strcpy(dir->dirname, dirname);
+
+       p = dir->dirname + strlen(dir->dirname);
+       if (dir->dirname < p && *(p - 1) != '/')
+               *p++ = '/';
+       *p++ = '*';
+       *p = '\0';
+
+       dir->handle = opendir(dirname);
+       if (dir->handle == NULL) {
+               switch (errno) {
+               case ENOTDIR:
+               case ELOOP:
+               case EINVAL:
+               case ENAMETOOLONG:
+               case EBADF:
+                       result = ISC_R_INVALIDFILE;
+               case ENOENT:
+                       result = ISC_R_FILENOTFOUND;
+               case EACCES:
+               case EPERM:
+                       result = ISC_R_NOPERM;
+               case ENOMEM:
+                       result = ISC_R_NOMEMORY;
+               default:
+                       result = ISC_R_UNEXPECTED;
+               }
+       }
+
+       return (result);
+}
+
+/*!
+ * \brief Return previously retrieved file or get next one.
+
+ * Unix's dirent has
+ * separate open and read functions, but the Win32 and DOS interfaces open
+ * the dir stream and reads the first file in one operation.
+ */
+isc_result_t
+dir_read(dir_t *dir) {
+       struct dirent *entry;
+
+       entry = readdir(dir->handle);
+       if (entry == NULL)
+               return (ISC_R_NOMORE);
+
+       if (sizeof(dir->entry.name) <= strlen(entry->d_name))
+           return (ISC_R_UNEXPECTED);
+
+       strcpy(dir->entry.name, entry->d_name);
+
+       dir->entry.length = strlen(entry->d_name);
+       return (ISC_R_SUCCESS);
+}
+
+/*!
+ * \brief Close directory stream.
+ */
+void
+dir_close(dir_t *dir) {
+       (void)closedir(dir->handle);
+       dir->handle = NULL;
+}
+
+/*!
+ * \brief Reposition directory stream at start.
+ */
+isc_result_t
+dir_reset(dir_t *dir) {
+       rewinddir(dir->handle);
+
+       return (ISC_R_SUCCESS);
+}
diff --git a/contrib/dlz/modules/filesystem/dir.h b/contrib/dlz/modules/filesystem/dir.h
new file mode 100644 (file)
index 0000000..66041fa
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or 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.
+ */
+
+#include <sys/types.h>
+#include <dirent.h>
+
+#define DIR_NAMEMAX 256
+#define DIR_PATHMAX 1024
+
+typedef struct direntry {
+       char            name[DIR_NAMEMAX];
+       unsigned int    length;
+} direntry_t;
+
+typedef struct dir {
+       char            dirname[DIR_PATHMAX];
+       direntry_t      entry;
+       DIR *           handle;
+} dir_t;
+
+void
+dir_init(dir_t *dir);
+
+isc_result_t
+dir_open(dir_t *dir, const char *dirname);
+
+isc_result_t
+dir_read(dir_t *dir);
+
+isc_result_t
+dir_reset(dir_t *dir);
+
+void
+dir_close(dir_t *dir);
diff --git a/contrib/dlz/modules/filesystem/dlz_filesystem_dynamic.c b/contrib/dlz/modules/filesystem/dlz_filesystem_dynamic.c
new file mode 100644 (file)
index 0000000..d199d39
--- /dev/null
@@ -0,0 +1,979 @@
+/*
+ * 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) 2013  Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001  Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or 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.
+ */
+
+/*
+ * This provides the externally loadable filesystem DLZ module, without
+ * update support
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <sys/stat.h>
+
+#include "dlz_minimal.h"
+#include "dlz_list.h"
+#include "dir.h"
+
+typedef struct config_data {
+       char            *basedir;
+       int             basedirsize;
+       char            *datadir;
+       int             datadirsize;
+       char            *xfrdir;
+       int             xfrdirsize;
+       int             splitcnt;
+       char            separator;
+       char            pathsep;
+
+       /* Helper functions from the dlz_dlopen driver */
+       log_t *log;
+       dns_sdlz_putrr_t *putrr;
+       dns_sdlz_putnamedrr_t *putnamedrr;
+       dns_dlz_writeablezone_t *writeable_zone;
+} config_data_t;
+
+typedef struct dir_entry dir_entry_t;
+
+struct dir_entry {
+       char dirpath[DIR_PATHMAX];
+       DLZ_LINK(dir_entry_t)   link;
+};
+
+typedef DLZ_LIST(dir_entry_t) dlist_t;
+
+/* forward reference */
+
+static void
+b9_add_helper(struct config_data *cd, const char *helper_name, void *ptr);
+
+/*
+ * 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 = strdup(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;
+       }
+
+       free(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;
+       isc_boolean_t isroot = ISC_FALSE;
+
+       /* special case for root zone */
+       if (strcmp(zone, ".") == 0)
+               isroot = ISC_TRUE;
+
+       /* if the requested zone is "unsafe", return error */
+       if (!isroot && !is_safe(zone))
+               return (ISC_R_FAILURE);
+
+       /* if host was passed, verify that it is safe */
+       if (host != NULL && !is_safe(host))
+               return (ISC_R_FAILURE);
+
+       /* if client was passed, verify that it is safe */
+       if (client != NULL && !is_safe(client))
+               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 = malloc(pathsize * sizeof(char));
+       if (tmpPath == NULL) {
+               /* write error message */
+               cd->log(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 (!isroot) {
+               result = create_path_helper(tmpPath, zone, cd);
+               if (result != 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);
+               result = create_path_helper(tmpPath, host, cd);
+               if (result != 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)
+               free(tmpPath);
+
+       return (result);
+}
+
+static isc_result_t
+process_dir(dir_t *dir, void *passback, config_data_t *cd,
+           dlist_t *dir_list, unsigned int basedirlen)
+{
+
+       char tmp[DIR_PATHMAX + DIR_NAMEMAX];
+       int astPos;
+       struct stat     sb;
+       isc_result_t result = ISC_R_FAILURE;
+       char *endp;
+       char *type;
+       char *ttlStr;
+       char *data;
+       char host[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)
+                                       {
+                                               if ((strlen(host) +
+                                                    strlen(tmpPtr + 1) + 2)
+                                                   > DIR_NAMEMAX)
+                                                       continue;
+                                               strcat(host, tmpPtr + 1);
+                                               strcat(host, ".");
+                                               tmpPtr[0] = '\0';
+                                       }
+                                       if ((strlen(host) +
+                                            strlen(tmpString) + 1)
+                                           <= DIR_NAMEMAX)
+                                               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 (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 {
+                                               strncpy(host,
+                                                  (char *) &dir->entry.name[6],
+                                                  sizeof(host) - 1);
+                                               host[255] = '\0';
+                                       }
+                                       foundHost = ISC_TRUE;
+                                       break;
+                               }
+                       }
+                       /* reset dir list for use later */
+                       dir_reset(dir);
+               } /* end of else */
+       }
+
+       while (dir_read(dir) == ISC_R_SUCCESS) {
+               cd->log(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 = malloc(sizeof(dir_entry_t));
+                                       if (direntry == NULL)
+                                               return (ISC_R_NOMEMORY);
+                                       strcpy(direntry->dirpath, tmp);
+                                       DLZ_LINK_INIT(direntry, link);
+                                       DLZ_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) {
+                       cd->log(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) {
+                       cd->log(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)
+                       cd->log(ISC_LOG_ERROR,
+                               "Filesystem driver "
+                               "ttl must be a postive number");
+
+               /* pass data back to Bind */
+               if (dir_list == NULL)
+                       result = cd->putrr((dns_sdlzlookup_t *) passback,
+                                          type, ttl, data);
+               else
+                       result = cd->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);
+}
+
+/*
+ * DLZ methods
+ */
+isc_result_t
+dlz_allowzonexfr(void *dbdata, const char *name, const char *client) {
+       isc_result_t result;
+       char *path;
+       struct stat     sb;
+       config_data_t *cd;
+       path = NULL;
+
+       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:
+       free(path);
+       return (result);
+}
+
+isc_result_t
+dlz_allnodes(const char *zone, void *dbdata, dns_sdlzallnodes_t *allnodes) {
+       isc_result_t result;
+       dlist_t *dir_list;
+       config_data_t *cd = (config_data_t *) dbdata;
+       char *basepath;
+       unsigned int basepathlen;
+       struct stat     sb;
+       dir_t dir;
+       dir_entry_t *dir_entry;
+       dir_entry_t *next_de;
+
+       basepath = NULL;
+       dir_list = NULL;
+
+       /* allocate memory for list */
+       dir_list = malloc(sizeof(dlist_t));
+       if (dir_list == NULL) {
+               result = ISC_R_NOTFOUND;
+               goto complete_allnds;
+       }
+
+       /* initialize list */
+       DLZ_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 */
+       dir_init(&dir);
+       result = dir_open(&dir, basepath);
+
+       /* if directory open failed, return error. */
+       if (result != ISC_R_SUCCESS) {
+               cd->log(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 */
+       dir_close(&dir);
+
+       if (result != ISC_R_SUCCESS)
+               goto complete_allnds;
+
+       /* get first dir entry from list. */
+       dir_entry = DLZ_LIST_HEAD(*dir_list);
+       while (dir_entry != NULL) {
+               result = dir_open(&dir, dir_entry->dirpath);
+               /* if directory open failed, return error. */
+               if (result != ISC_R_SUCCESS) {
+                       cd->log(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 */
+               dir_close(&dir);
+
+               if (result != ISC_R_SUCCESS)
+                       goto complete_allnds;
+
+               dir_entry = DLZ_LIST_NEXT(dir_entry, link);
+       } /* end while */
+
+ complete_allnds:
+       if (dir_list != NULL) {
+               /* clean up entries from list. */
+               dir_entry = DLZ_LIST_HEAD(*dir_list);
+               while (dir_entry != NULL) {
+                       next_de = DLZ_LIST_NEXT(dir_entry, link);
+                       free(dir_entry);
+                       dir_entry = next_de;
+               } /* end while */
+               free(dir_list);
+       }
+
+       if (basepath != NULL)
+               free(basepath);
+
+       return (result);
+}
+
+#if DLZ_DLOPEN_VERSION < 3
+isc_result_t
+dlz_findzonedb(void *dbdata, const char *name)
+#else
+isc_result_t
+dlz_findzonedb(void *dbdata, const char *name,
+              dns_clientinfomethods_t *methods,
+              dns_clientinfo_t *clientinfo)
+#endif
+{
+
+       isc_result_t result;
+       config_data_t *cd = (config_data_t *) dbdata;
+       char *path;
+       struct stat sb;
+       path = NULL;
+
+#if DLZ_DLOPEN_VERSION >= 3
+       UNUSED(methods);
+       UNUSED(clientinfo);
+#endif
+
+       if (create_path(name, NULL, NULL, cd, &path) != ISC_R_SUCCESS)
+               return (ISC_R_NOTFOUND);
+
+       cd->log(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:
+
+       free(path);
+       return (result);
+}
+
+#if DLZ_DLOPEN_VERSION == 1
+isc_result_t
+dlz_lookup(const char *zone, const char *name,
+          void *dbdata, dns_sdlzlookup_t *lookup)
+#else
+isc_result_t
+dlz_lookup(const char *zone, const char *name,
+          void *dbdata, dns_sdlzlookup_t *lookup,
+          dns_clientinfomethods_t *methods,
+          dns_clientinfo_t *clientinfo)
+#endif
+{
+       isc_result_t result = ISC_R_NOTFOUND;
+       config_data_t *cd = (config_data_t *) dbdata;
+       char *path;
+       struct stat sb;
+       dir_t dir;
+       path = NULL;
+
+       UNUSED(lookup);
+#if DLZ_DLOPEN_VERSION >= 2
+       UNUSED(methods);
+       UNUSED(clientinfo);
+#endif
+
+       if (strcmp(name, "*") == 0)
+               /*
+                * handle filesystem's special wildcard "-"
+                */
+               result = create_path(zone, "-", NULL, cd, &path);
+       else
+               result = create_path(zone, name, NULL, cd, &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';
+
+       cd->log(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 */
+       dir_init(&dir);
+       result = dir_open(&dir, path);
+
+       /* if directory open failed, return error. */
+       if (result != ISC_R_SUCCESS) {
+               cd->log(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, cd, NULL, 0);
+
+       /* close the directory */
+       dir_close(&dir);
+
+ complete_lkup:
+
+       free(path);
+       return (result);
+}
+
+isc_result_t
+dlz_create(const char *dlzname, unsigned int argc, char *argv[],
+          void **dbdata, ...)
+{
+       config_data_t *cd;
+       char *endp;
+       int len;
+       char pathsep;
+       const char *helper_name;
+       va_list ap;
+
+       UNUSED(dlzname);
+
+       /* allocate memory for our config data and helper functions */
+       cd = calloc(1, sizeof(config_data_t));
+       if (cd == NULL)
+               goto no_mem;
+
+       /* zero the memory */
+       memset(cd, 0, sizeof(config_data_t));
+
+       /* Fill in the helper functions */
+       va_start(ap, dbdata);
+       while ((helper_name = va_arg(ap, const char*)) != NULL)
+               b9_add_helper(cd, helper_name, va_arg(ap, void*));
+       va_end(ap);
+
+       /* we require 5 command line args. */
+       if (argc != 6) {
+               cd->log(ISC_LOG_ERROR,
+                       "Filesystem driver requires "
+                       "6 command line args.");
+               return (ISC_R_FAILURE);
+       }
+
+       if (strlen(argv[5]) > 1) {
+               cd->log(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] != '/') {
+               cd->log(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 = '/';
+
+       cd->pathsep = pathsep;
+
+       /* get and store our base directory */
+       cd->basedir = strdup(argv[1]);
+       if (cd->basedir == NULL)
+               goto no_mem;
+       cd->basedirsize = strlen(cd->basedir);
+
+       /* get and store our data sub-dir */
+       cd->datadir = strdup(argv[2]);
+       if (cd->datadir == NULL)
+               goto no_mem;
+       cd->datadirsize = strlen(cd->datadir);
+
+       /* get and store our zone xfr sub-dir */
+       cd->xfrdir = strdup(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)
+               cd->log(ISC_LOG_ERROR,
+                       "Directory split count must be zero (0) "
+                       "or a postive number");
+
+       /* get and store our separator character */
+       cd->separator = *argv[5];
+
+       /* pass back config data */
+       *dbdata = cd;
+
+       /* return success */
+       return (ISC_R_SUCCESS);
+
+       /* handle no memory error */
+ no_mem:
+
+       /* write error message */
+       if (cd != NULL && cd->log != NULL)
+               cd->log(ISC_LOG_ERROR,
+                       "filesystem_dynamic: Filesystem driver unable to "
+                       "allocate memory for config data.");
+
+       /* if we allocated a config data object clean it up */
+       if (cd != NULL)
+               dlz_destroy(cd);
+
+       /* return error */
+       return (ISC_R_NOMEMORY);
+}
+
+void
+dlz_destroy(void *dbdata) {
+       config_data_t *cd;
+
+       cd = (config_data_t *) dbdata;
+
+       /*
+        * free memory for each section of config data that was
+        * allocated
+        */
+       if (cd->basedir != NULL)
+               free(cd->basedir);
+
+       if (cd->datadir != NULL)
+               free(cd->datadir);
+
+       if (cd->xfrdir != NULL)
+               free(cd->xfrdir);
+
+       /* free config data memory */
+       free(cd);
+}
+
+/*
+ * Return the version of the API
+ */
+int
+dlz_version(unsigned int *flags) {
+       UNUSED(flags);
+       return (DLZ_DLOPEN_VERSION);
+}
+
+/*
+ * Register a helper function from the bind9 dlz_dlopen driver
+ */
+static void
+b9_add_helper(struct config_data *cd, const char *helper_name, void *ptr) {
+       if (strcmp(helper_name, "log") == 0)
+               cd->log = (log_t *)ptr;
+       if (strcmp(helper_name, "putrr") == 0)
+               cd->putrr = (dns_sdlz_putrr_t *)ptr;
+       if (strcmp(helper_name, "putnamedrr") == 0)
+               cd->putnamedrr = (dns_sdlz_putnamedrr_t *)ptr;
+       if (strcmp(helper_name, "writeable_zone") == 0)
+               cd->writeable_zone = (dns_dlz_writeablezone_t *)ptr;
+}
diff --git a/contrib/dlz/modules/include/dlz_dbi.h b/contrib/dlz/modules/include/dlz_dbi.h
new file mode 100644 (file)
index 0000000..5123927
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * 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.
+ */
+
+#include <dlz_minimal.h>
+#include <dlz_list.h>
+#include <dlz_pthread.h>
+
+#ifndef DLZ_DBI_H
+#define DLZ_DBI_H 1
+
+/*
+ * Types
+ */
+#define REQUIRE_CLIENT 0x01
+#define REQUIRE_QUERY  0x02
+#define REQUIRE_RECORD 0x04
+#define REQUIRE_ZONE   0x08
+
+typedef struct query_segment query_segment_t;
+typedef DLZ_LIST(query_segment_t) query_list_t;
+typedef struct dbinstance dbinstance_t;
+typedef DLZ_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                            *cmd;
+       unsigned int                    strlen;
+       isc_boolean_t                   direct;
+       DLZ_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;
+       dlz_mutex_t             lock;
+       DLZ_LINK(dbinstance_t)  link;
+};
+
+/*
+ * Method declarations
+ */
+
+void
+destroy_querylist(query_list_t **querylist);
+
+isc_result_t
+build_querylist(const char *query_str, char **zone, char **record,
+               char **client, query_list_t **querylist, unsigned int flags, 
+               log_t log);
+
+char *
+build_querystring(query_list_t *querylist);
+
+isc_result_t
+build_dbinstance(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, log_t log);
+
+void
+destroy_dbinstance(dbinstance_t *dbi);
+
+char *
+get_parameter_value(const char *input, const char* key);
+
+#endif /* DLZ_DBI_H */
diff --git a/contrib/dlz/modules/include/dlz_list.h b/contrib/dlz/modules/include/dlz_list.h
new file mode 100644 (file)
index 0000000..50e3dec
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2004, 2006, 2007, 2011-2013  Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1997-2002  Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or 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.
+ */
+
+#ifndef DLZ_LIST_H
+#define DLZ_LIST_H 1
+
+#define DLZ_LIST(type) struct { type *head, *tail; }
+#define DLZ_LIST_INIT(list) \
+       do { (list).head = NULL; (list).tail = NULL; } while (0)
+
+#define DLZ_LINK(type) struct { type *prev, *next; }
+#define DLZ_LINK_INIT(elt, link) \
+       do { \
+               (elt)->link.prev = (void *)(-1); \
+               (elt)->link.next = (void *)(-1); \
+       } while (0)
+
+#define DLZ_LIST_HEAD(list) ((list).head)
+#define DLZ_LIST_TAIL(list) ((list).tail)
+
+#define DLZ_LIST_APPEND(list, elt, link) \
+       do { \
+               if ((list).tail != NULL) \
+                       (list).tail->link.next = (elt); \
+               else \
+                       (list).head = (elt); \
+               (elt)->link.prev = (list).tail; \
+               (elt)->link.next = NULL; \
+               (list).tail = (elt); \
+       } while (0)
+
+#define DLZ_LIST_PREV(elt, link) ((elt)->link.prev)
+#define DLZ_LIST_NEXT(elt, link) ((elt)->link.next)
+
+#endif /* DLZ_LIST_H */
similarity index 93%
rename from contrib/dlz/modules/dlz_minimal.h
rename to contrib/dlz/modules/include/dlz_minimal.h
index d90e6a7ae71d3478d4bfae391b6cb56b91f5a0b0..6019a1236fc9b09dac2670ad64df736a2b6f7ec6 100644 (file)
@@ -23,6 +23,9 @@
  * tree.
  */
 
+#ifndef DLZ_MINIMAL_H
+#define DLZ_MINIMAL_H 1
+
 #include <sys/types.h>
 #include <sys/socket.h>
 #ifdef ISC_PLATFORM_HAVESYSUNH
@@ -45,8 +48,10 @@ typedef uint32_t dns_ttl_t;
 #define DLZ_DLOPEN_AGE 0
 #endif
 
-/* return this in flags to dlz_version() if thread safe */
+/* return these in flags from dlz_version() */
 #define DNS_SDLZFLAG_THREADSAFE                0x00000001U
+#define DNS_SDLZFLAG_RELATIVEOWNER     0x00000002U
+#define DNS_SDLZFLAG_RELATIVERDATA     0x00000004U
 
 /* result codes */
 #define ISC_R_SUCCESS                  0
@@ -57,6 +62,9 @@ typedef uint32_t dns_ttl_t;
 #define ISC_R_FAILURE                  25
 #define ISC_R_NOTIMPLEMENTED           27
 #define ISC_R_NOMORE                   29
+#define ISC_R_INVALIDFILE              30
+#define ISC_R_UNEXPECTED               34
+#define ISC_R_FILENOTFOUND             38
 
 /* boolean values */
 #define ISC_TRUE 1
@@ -68,6 +76,7 @@ typedef uint32_t dns_ttl_t;
 #define ISC_LOG_WARNING        (-3)
 #define ISC_LOG_ERROR          (-4)
 #define ISC_LOG_CRITICAL       (-5)
+#define ISC_LOG_DEBUG(level)   (level)
 
 /* other useful definitions */
 #define UNUSED(x) (void)(x)
@@ -196,6 +205,13 @@ dlz_lookup(const char *zone, const char *name, void *dbdata,
           dns_clientinfo_t *clientinfo);
 #endif /* DLZ_DLOPEN_VERSION */
 
+/*
+ * dlz_authority() is optional if dlz_lookup() supplies
+ * authority information (i.e., SOA, NS) for the dns record
+ */
+isc_result_t
+dlz_authority(const char *zone, void *dbdata, dns_sdlzlookup_t *lookup);
+
 /*
  * dlz_allowzonexfr() is optional, and should be supplied if you want to
  * support zone transfers
@@ -269,3 +285,5 @@ dlz_subrdataset(const char *name, const char *rdatastr, void *dbdata,
 isc_result_t
 dlz_delrdataset(const char *name, const char *type, void *dbdata,
                void *version);
+
+#endif /* DLZ_MINIMAL_H */
diff --git a/contrib/dlz/modules/include/dlz_pthread.h b/contrib/dlz/modules/include/dlz_pthread.h
new file mode 100644 (file)
index 0000000..993e9fc
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or 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.
+ */
+
+#ifndef DLZ_PTHREAD_H
+#define DLZ_PTHREAD_H 1
+
+#ifndef PTHREADS
+#define PTHREADS 1
+#endif
+
+#ifdef PTHREADS
+#define dlz_mutex_t pthread_mutex_t
+#define dlz_mutex_init pthread_mutex_init
+#define dlz_mutex_destroy pthread_mutex_destroy
+#define dlz_mutex_trylock pthread_mutex_trylock
+#define dlz_mutex_unlock pthread_mutex_unlock
+#else /* !PTHREADS */
+#define dlz_mutex_t void
+#define dlz_mutex_init(a, b) (0)
+#define dlz_mutex_destroy(a) (0)
+#define dlz_mutex_trylock(a) (0)
+#define dlz_mutex_unlock(a) (0)
+#endif
+
+#endif /* DLZ_PTHREAD_H */
diff --git a/contrib/dlz/modules/ldap/Makefile b/contrib/dlz/modules/ldap/Makefile
new file mode 100644 (file)
index 0000000..8926abf
--- /dev/null
@@ -0,0 +1,21 @@
+prefix = /usr
+libdir = $(prefix)/lib/bind9
+
+CFLAGS=-fPIC -g -I../include
+LDAP_LIBS=-lldap
+
+all: dlz_ldap_dynamic.so
+
+dlz_dbi.o: ../common/dlz_dbi.c
+       $(CC) $(CFLAGS) -c ../common/dlz_dbi.c
+
+dlz_ldap_dynamic.so: dlz_ldap_dynamic.c dlz_dbi.o
+       $(CC) $(CFLAGS) -shared -o dlz_ldap_dynamic.so \
+               dlz_ldap_dynamic.c dlz_dbi.o $(LDAP_LIBS)
+
+clean:
+       rm -f dlz_ldap_dynamic.so *.o
+
+install: dlz_ldap_dynamic.so
+       mkdir -p $(DESTDIR)$(libdir)
+       install dlz_ldap_dynamic.so $(DESTDIR)$(libdir)
diff --git a/contrib/dlz/modules/ldap/dlz_ldap_dynamic.c b/contrib/dlz/modules/ldap/dlz_ldap_dynamic.c
new file mode 100644 (file)
index 0000000..d5d5ad1
--- /dev/null
@@ -0,0 +1,1205 @@
+/*
+ * 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) 2013  Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001  Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or 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.
+ */
+
+/*
+ * This provides the externally loadable ldap DLZ module, without
+ * update support
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+#include <dlz_minimal.h>
+#include <dlz_list.h>
+#include <dlz_dbi.h>
+#include <dlz_pthread.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"
+
+#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 {
+#if PTHREADS
+       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 */
+
+       /* Helper functions from the dlz_dlopen driver */
+       log_t *log;
+       dns_sdlz_putrr_t *putrr;
+       dns_sdlz_putnamedrr_t *putnamedrr;
+       dns_dlz_writeablezone_t *writeable_zone;
+} ldap_instance_t;
+
+/* forward references */
+
+isc_result_t
+dlz_findzonedb(void *dbdata, const char *name,
+              dns_clientinfomethods_t *methods,
+              dns_clientinfo_t *clientinfo);
+
+void
+dlz_destroy(void *dbdata);
+
+static void
+b9_add_helper(ldap_instance_t *db, const char *helper_name, void *ptr);
+
+/*
+ * Private methods
+ */
+
+/*% checks that the LDAP URL parameters make sense */
+static isc_result_t
+ldap_checkURL(ldap_instance_t *db, 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)) {
+               db->log(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) {
+               db->log(ISC_LOG_ERROR, "parsing %s query failed", msg);
+               result = ISC_R_FAILURE;
+               goto cleanup;
+       }
+
+       if (ldap_count_values(ldap_url->lud_attrs) < attrCnt) {
+               db->log(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) {
+               db->log(ISC_LOG_ERROR,
+                       "%s query must not specify a host", msg);
+               result = ISC_R_FAILURE;
+               goto cleanup;
+       }
+
+       if (ldap_url->lud_port != 389) {
+               db->log(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) {
+               db->log(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) {
+               db->log(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
+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);
+}
+
+#if PTHREADS
+/*%
+ * 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 = DLZ_LIST_HEAD(*dblist);
+
+       /* loop through the list */
+       while (ndbi != NULL) {
+               dbi = ndbi;
+               /* get the next DBI in the list */
+               ndbi = DLZ_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_dbinstance(dbi);
+       }
+       /* release memory for the list structure */
+       free(dblist);
+}
+
+/*%
+ * 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(ldap_instance_t *ldap) {
+       dbinstance_t *dbi = NULL;
+       dbinstance_t *head;
+       int count = 0;
+
+       /* get top of list */
+       head = dbi = DLZ_LIST_HEAD(*ldap->db);
+
+       /* loop through list */
+       while (count < dbc_search_limit) {
+               /* try to lock on the mutex */
+               if (dlz_mutex_trylock(&dbi->lock) == 0)
+                       return (dbi); /* success, return the DBI for use. */
+
+               /* not successful, keep trying */
+               dbi = DLZ_LIST_NEXT(dbi, link);
+
+               /* check to see if we have gone to the top of the list. */
+               if (dbi == NULL) {
+                       count++;
+                       dbi = head;
+               }
+       }
+
+       ldap->log(ISC_LOG_INFO,
+               "LDAP driver unable to find available connection "
+               "after searching %d times", count);
+       return (NULL);
+}
+#endif /* PTHREADS */
+
+static isc_result_t
+ldap_process_results(ldap_instance_t *db, 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;
+
+       /* get the first entry to process */
+       entry = ldap_first_entry(dbc, msg);
+       if (entry == NULL) {
+               db->log(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);
+               }
+
+               /* allocate memory for data string */
+               data = malloc(len + 1);
+               if (data == NULL) {
+                       db->log(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) {
+                                       db->log(ISC_LOG_ERROR,
+                                               "LDAP driver ttl must "
+                                               "be a postive number");
+                                       goto cleanup;
+                               }
+                               break;
+                       case 1:
+                               j++;
+                               type = strdup(vals[0]);
+                               break;
+                       case 2:
+                               j++;
+                               if (allnodes)
+                                       host = strdup(vals[0]);
+                               else
+                                       strcpy(data, vals[0]);
+                               break;
+                       case 3:
+                               j++;
+                               if (allnodes)
+                                       strcpy(data, vals[0]);
+                               else {
+                                       strcat(data, " ");
+                                       strcat(data, vals[0]);
+                               }
+                               break;
+                       default:
+                               strcat(data, " ");
+                               strcat(data, vals[0]);
+                               break;
+                       }
+
+                       /* free values */
+                       ldap_value_free(vals);
+                       vals = NULL;
+
+                       /* increment attibute pointer */
+                       attribute = attrs[++i];
+               }
+
+               if (type == NULL) {
+                       db->log(ISC_LOG_ERROR,
+                               "LDAP driver unable to retrieve DNS type");
+                       result = ISC_R_FAILURE;
+                       goto cleanup;
+               }
+
+               if (strlen(data) < 1) {
+                       db->log(ISC_LOG_ERROR,
+                               "LDAP driver unable to retrieve DNS data");
+                       result = ISC_R_FAILURE;
+                       goto cleanup;
+               }
+
+               if (allnodes && host != NULL) {
+                       dns_sdlzallnodes_t *an = (dns_sdlzallnodes_t *) ptr;
+                       if (strcasecmp(host, "~") == 0)
+                               result = db->putnamedrr(an, "*", type,
+                                                       ttl, data);
+                       else
+                               result = db->putnamedrr(an, host, type,
+                                                       ttl, data);
+                       if (result != ISC_R_SUCCESS)
+                               db->log(ISC_LOG_ERROR,
+                                       "ldap_dynamic: putnamedrr failed "
+                                       "for \"%s %s %u %s\" (%d)",
+                                       host, type, ttl, data, result);
+               } else {
+                       dns_sdlzlookup_t *lookup = (dns_sdlzlookup_t *) ptr;
+                       result = db->putrr(lookup, type, ttl, data);
+                       if (result != ISC_R_SUCCESS)
+                               db->log(ISC_LOG_ERROR,
+                                       "ldap_dynamic: putrr failed "
+                                       "for \"%s %u %s\" (%s)",
+                                       type, ttl, data, result);
+               }
+
+               if (result != ISC_R_SUCCESS) {
+                       db->log(ISC_LOG_ERROR,
+                               "LDAP driver failed "
+                               "while sending data to BIND.");
+                       goto cleanup;
+               }
+
+               /* free memory for type, data and host for next loop */
+               free(type);
+               type = NULL;
+
+               free(data);
+               data = NULL;
+
+               if (host != NULL) {
+                       free(host);
+                       host = NULL;
+               }
+
+               /* get the next entry to process */
+               entry = ldap_next_entry(dbc, entry);
+       }
+
+ cleanup:
+       /* de-allocate memory */
+       if (vals != NULL)
+               ldap_value_free(vals);
+       if (host != NULL)
+               free(host);
+       if (type != NULL)
+               free(type);
+       if (data != NULL)
+               free(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;
+       ldap_instance_t *db = (ldap_instance_t *)dbdata;
+       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 */
+#if PTHREADS
+       /* find an available DBI from the list */
+       dbi = ldap_find_avail_conn(db);
+#else /* PTHREADS */
+       /*
+        * only 1 DBI - no need to lock instance lock either
+        * only 1 thread in the whole process, no possible contention.
+        */
+       dbi = (dbinstance_t *)(db->db);
+#endif /* PTHREADS */
+
+       /* if DBI is null, can't do anything else */
+       if (dbi == NULL)
+               return (ISC_R_FAILURE);
+
+       /* set fields */
+       if (zone != NULL) {
+               dbi->zone = strdup(zone);
+               if (dbi->zone == NULL) {
+                       result = ISC_R_NOMEMORY;
+                       goto cleanup;
+               }
+       } else
+               dbi->zone = NULL;
+
+       if (record != NULL) {
+               dbi->record = strdup(record);
+               if (dbi->record == NULL) {
+                       result = ISC_R_NOMEMORY;
+                       goto cleanup;
+               }
+       } else
+               dbi->record = NULL;
+
+       if (client != NULL) {
+               dbi->client = strdup(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(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(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(dbi->authority_q);
+               break;
+       case FINDZONE:
+               /* this is required.  It's the whole point of DLZ! */
+               if (dbi->findzone_q == NULL) {
+                       db->log(ISC_LOG_DEBUG(2),
+                               "No query specified for findzone. "
+                               "Findzone requires a query");
+                       result = ISC_R_FAILURE;
+                       goto cleanup;
+               } else
+                       querystring = build_querystring(dbi->findzone_q);
+               break;
+       case LOOKUP:
+               /* this is required.  It's also a major point of DLZ! */
+               if (dbi->lookup_q == NULL) {
+                       db->log(ISC_LOG_DEBUG(2),
+                               "No query specified for lookup. "
+                               "Lookup requires a query");
+                       result = ISC_R_FAILURE;
+                       goto cleanup;
+               } else
+                       querystring = build_querystring(dbi->lookup_q);
+               break;
+       default:
+               /*
+                * this should never happen.  If it does, the code is
+                * screwed up!
+                */
+               db->log(ISC_LOG_ERROR,
+                       "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.
+        */
+       db->log(ISC_LOG_DEBUG(1), "Query String: %s", 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) {
+                       db->log(ISC_LOG_INFO,
+                               "LDAP driver attempting to re-connect");
+
+                       result = 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:
+                       db->log(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:
+                       db->log(ISC_LOG_INFO,
+                               "LDAP driver attempting to re-connect");
+                       result = 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
+                        */
+                       db->log(ISC_LOG_ERROR, "LDAP error: %s",
+                               ldap_err2string(ldap_result));
+                       result = ISC_R_FAILURE;
+                       goto cleanup;
+                       break;
+               }
+       }
+
+       if (result != ISC_R_SUCCESS)
+               goto cleanup;
+
+       switch (query) {
+       case ALLNODES:
+               result = ldap_process_results(db, (LDAP *) dbi->dbconn,
+                                             ldap_msg, ldap_url->lud_attrs,
+                                             ptr, ISC_TRUE);
+               break;
+       case AUTHORITY:
+       case LOOKUP:
+               result = ldap_process_results(db, (LDAP *) dbi->dbconn,
+                                             ldap_msg, ldap_url->lud_attrs,
+                                             ptr, ISC_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!
+                */
+               db->log(ISC_LOG_ERROR,
+                       "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)
+               free(dbi->zone);
+       if (dbi->record != NULL)
+               free(dbi->record);
+       if (dbi->client != NULL)
+               free(dbi->client);
+       dbi->zone = dbi->record = dbi->client = NULL;
+
+       /* release the lock so another thread can use this dbi */
+       (void) dlz_mutex_unlock(&dbi->lock);
+
+        /* release query string */
+       if (querystring != NULL)
+               free(querystring);
+
+       /* return result */
+       return (result);
+}
+
+/*
+ * DLZ methods
+ */
+isc_result_t
+dlz_allowzonexfr(void *dbdata, const char *name, const char *client) {
+       isc_result_t result;
+
+       /* check to see if we are authoritative for the zone first */
+       result = dlz_findzonedb(dbdata, name, NULL, NULL);
+       if (result != ISC_R_SUCCESS) {
+               return (result);
+       }
+
+        /* get all the zone data */
+       result = ldap_get_results(name, NULL, client, ALLOWXFR, dbdata, NULL);
+       return (result);
+}
+
+isc_result_t
+dlz_allnodes(const char *zone, void *dbdata, dns_sdlzallnodes_t *allnodes)
+{
+       return (ldap_get_results(zone, NULL, NULL, ALLNODES, dbdata, allnodes));
+}
+
+isc_result_t
+dlz_authority(const char *zone, void *dbdata, dns_sdlzlookup_t *lookup) {
+       return (ldap_get_results(zone, NULL, NULL, AUTHORITY, dbdata, lookup));
+}
+
+isc_result_t
+dlz_findzonedb(void *dbdata, const char *name,
+              dns_clientinfomethods_t *methods,
+              dns_clientinfo_t *clientinfo)
+{
+       UNUSED(methods);
+       UNUSED(clientinfo);
+       return (ldap_get_results(name, NULL, NULL, FINDZONE, dbdata, NULL));
+}
+
+isc_result_t
+dlz_lookup(const char *zone, const char *name,
+          void *dbdata, dns_sdlzlookup_t *lookup,
+          dns_clientinfomethods_t *methods,
+          dns_clientinfo_t *clientinfo)
+{
+       isc_result_t result;
+
+       UNUSED(methods);
+       UNUSED(clientinfo);
+
+       if (strcmp(name, "*") == 0)
+               result = ldap_get_results(zone, "~", NULL, LOOKUP,
+                                         dbdata, lookup);
+       else
+               result = ldap_get_results(zone, name, NULL, LOOKUP,
+                                         dbdata, lookup);
+       return (result);
+}
+
+
+isc_result_t
+dlz_create(const char *dlzname, unsigned int argc, char *argv[],
+          void **dbdata, ...)
+{
+       isc_result_t result = ISC_R_FAILURE;
+       ldap_instance_t *ldap = NULL;
+       dbinstance_t *dbi = NULL;
+       const char *helper_name;
+       int protocol;
+       int method;
+#if PTHREADS
+       int dbcount;
+       char *endp;
+       int i;
+#endif /* PTHREADS */
+       va_list ap;
+
+       UNUSED(dlzname);
+
+       /* allocate memory for LDAP instance */
+       ldap = calloc(1, sizeof(ldap_instance_t));
+       if (ldap == NULL)
+               return (ISC_R_NOMEMORY);
+       memset(ldap, 0, sizeof(ldap_instance_t));
+
+        /* Fill in the helper functions */
+       va_start(ap, dbdata);
+       while ((helper_name = va_arg(ap, const char*)) != NULL)
+               b9_add_helper(ldap, helper_name, va_arg(ap, void*));
+       va_end(ap);
+
+#if PTHREADS
+       /* if debugging, let user know we are multithreaded. */
+       ldap->log(ISC_LOG_DEBUG(1), "LDAP driver running multithreaded");
+#else /* PTHREADS */
+       /* if debugging, let user know we are single threaded. */
+       ldap->log(ISC_LOG_DEBUG(1), "LDAP driver running single threaded");
+#endif /* PTHREADS */
+
+       if (argc < 9) {
+               ldap->log(ISC_LOG_ERROR,
+                         "LDAP driver requires at least "
+                         "8 command line args.");
+               goto cleanup;
+       }
+
+       /* no more than 13 arg's should be passed to the driver */
+       if (argc > 12) {
+               ldap->log(ISC_LOG_ERROR,
+                         "LDAP driver cannot accept more than "
+                         "11 command line args.");
+               goto cleanup;
+       }
+
+       /* 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 {
+               ldap->log(ISC_LOG_ERROR,
+                         "LDAP driver protocol must be either %s or %s",
+                         V2, V3);
+               goto cleanup;
+       }
+
+       /* 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 {
+               ldap->log(ISC_LOG_ERROR,
+                         "LDAP driver authentication method must be "
+                         "one of %s, %s or %s", SIMPLE, KRB41, KRB42);
+               goto cleanup;
+       }
+
+       /* multithreaded build can have multiple DB connections */
+#if PTHREADS
+       /* check how many db connections we should create */
+       dbcount = strtol(argv[1], &endp, 10);
+       if (*endp != '\0' || dbcount < 0) {
+               ldap->log(ISC_LOG_ERROR,
+                         "LDAP driver database connection count "
+                         "must be positive.");
+               goto cleanup;
+       }
+#endif
+
+       /* check that LDAP URL parameters make sense */
+       switch (argc) {
+       case 12:
+               result = ldap_checkURL(ldap, argv[11], 0,
+                                      "allow zone transfer");
+               if (result != ISC_R_SUCCESS)
+                       goto cleanup;
+       case 11:
+               result = ldap_checkURL(ldap, argv[10], 3, "all nodes");
+               if (result != ISC_R_SUCCESS)
+                       goto cleanup;
+       case 10:
+               if (strlen(argv[9]) > 0) {
+                       result = ldap_checkURL(ldap, argv[9], 3, "authority");
+                       if (result != ISC_R_SUCCESS)
+                               goto cleanup;
+               }
+       case 9:
+               result = ldap_checkURL(ldap, argv[8], 3, "lookup");
+               if (result != ISC_R_SUCCESS)
+                       goto cleanup;
+               result = ldap_checkURL(ldap, argv[7], 0, "find zone");
+               if (result != ISC_R_SUCCESS)
+                       goto cleanup;
+               break;
+       default:
+               /* not really needed, should shut up compiler. */
+               result = ISC_R_FAILURE;
+       }
+
+       /* store info needed to automatically re-connect. */
+       ldap->protocol = protocol;
+       ldap->method = method;
+       ldap->hosts = strdup(argv[6]);
+       if (ldap->hosts == NULL) {
+               result = ISC_R_NOMEMORY;
+               goto cleanup;
+       }
+       ldap->user = strdup(argv[4]);
+       if (ldap->user == NULL) {
+               result = ISC_R_NOMEMORY;
+               goto cleanup;
+       }
+       ldap->cred = strdup(argv[5]);
+       if (ldap->cred == NULL) {
+               result = ISC_R_NOMEMORY;
+               goto cleanup;
+       }
+
+#if PTHREADS
+       /* allocate memory for database connection list */
+       ldap->db = calloc(1, sizeof(db_list_t));
+       if (ldap->db == NULL) {
+               result = ISC_R_NOMEMORY;
+               goto cleanup;
+       }
+
+       /* initialize DB connection list */
+       DLZ_LIST_INIT(*(ldap->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 /* PTHREADS */
+               /* how many queries were passed in from config file? */
+               switch (argc) {
+               case 9:
+                       result = build_dbinstance(NULL, NULL, NULL, argv[7],
+                                                 argv[8], NULL, &dbi,
+                                                 ldap->log);
+                       break;
+               case 10:
+                       result = build_dbinstance(NULL, NULL, argv[9],
+                                                 argv[7], argv[8],
+                                                 NULL, &dbi, ldap->log);
+                       break;
+               case 11:
+                       result = build_dbinstance(argv[10], NULL, argv[9],
+                                                 argv[7], argv[8],
+                                                 NULL, &dbi, ldap->log);
+                       break;
+               case 12:
+                       result = build_dbinstance(argv[10], argv[11],
+                                                 argv[9], argv[7],
+                                                 argv[8], NULL, &dbi,
+                                                 ldap->log);
+                       break;
+               default:
+                       /* not really needed, should shut up compiler. */
+                       result = ISC_R_FAILURE;
+               }
+
+               if (result == ISC_R_SUCCESS) {
+                       ldap->log(ISC_LOG_DEBUG(2),
+                                 "LDAP driver created "
+                                 "database instance object.");
+               } else { /* unsuccessful?, log err msg and cleanup. */
+                       ldap->log(ISC_LOG_ERROR,
+                                 "LDAP driver could not create "
+                                 "database instance object.");
+                       goto cleanup;
+               }
+
+#if PTHREADS
+               /* when multithreaded, build a list of DBI's */
+               DLZ_LINK_INIT(dbi, link);
+               DLZ_LIST_APPEND(*(ldap->db), dbi, link);
+#else
+               /*
+                * when single threaded, hold onto the one connection
+                * instance.
+                */
+               ldap->db = dbi;
+#endif
+               /* attempt to connect */
+               result = ldap_connect(ldap, 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:
+#if PTHREADS
+                       ldap->log(ISC_LOG_ERROR,
+                                 "LDAP driver could not allocate memory "
+                                 "for connection number %u", i + 1);
+#else
+                       ldap->log(ISC_LOG_ERROR,
+                                 "LDAP driver could not allocate memory "
+                                 "for connection");
+#endif
+                       goto cleanup;
+                       /*
+                        * no perm means ldap_set_option could not set
+                        * protocol version
+                        */
+               case ISC_R_NOPERM:
+                       ldap->log(ISC_LOG_ERROR,
+                                 "LDAP driver could not "
+                                 "set protocol version.");
+                       result = ISC_R_FAILURE;
+                       goto cleanup;
+                       /* failure means couldn't connect to ldap server */
+               case ISC_R_FAILURE:
+#if PTHREADS
+                       ldap->log(ISC_LOG_ERROR,
+                                 "LDAP driver could not bind "
+                                 "connection number %u to server.", i + 1);
+#else
+                       ldap->log(ISC_LOG_ERROR,
+                                 "LDAP driver could not "
+                                 "bind connection to server.");
+#endif
+                       goto cleanup;
+                       /*
+                        * default should never happen.  If it does,
+                        * major errors.
+                        */
+               default:
+                       ldap->log(ISC_LOG_ERROR,
+                                 "dlz_create() failed (%d)", result);
+                       result = ISC_R_UNEXPECTED;
+                       goto cleanup;
+               }
+
+#if PTHREADS
+               /* set DBI = null for next loop through. */
+               dbi = NULL;
+       }
+#endif /* PTHREADS */
+
+       /* set dbdata to the ldap_instance we created. */
+       *dbdata = ldap;
+
+       return (ISC_R_SUCCESS);
+
+ cleanup:
+       dlz_destroy(ldap);
+
+       return (result);
+}
+
+void
+dlz_destroy(void *dbdata) {
+       if (dbdata != NULL) {
+               ldap_instance_t *db = (ldap_instance_t *)dbdata;
+#if PTHREADS
+               /* cleanup the list of DBI's */
+               ldap_destroy_dblist((db_list_t *)(db->db));
+#else /* PTHREADS */
+               if (db->db->dbconn != NULL)
+                       ldap_unbind_s((LDAP *)(db->db->dbconn));
+
+               /* destroy single DB instance */
+               destroy_dbinstance(db->db);
+#endif /* PTHREADS */
+
+               if (db->hosts != NULL)
+                       free(db->hosts);
+               if (db->user != NULL)
+                       free(db->user);
+               if (db->cred != NULL)
+                       free(db->cred);
+               free(dbdata);
+       }
+}
+
+/*
+ * Return the version of the API
+ */
+int
+dlz_version(unsigned int *flags) {
+       *flags |= DNS_SDLZFLAG_RELATIVERDATA;
+#if PTHREADS
+       *flags |= DNS_SDLZFLAG_THREADSAFE;
+#else
+       *flags &= ~DNS_SDLZFLAG_THREADSAFE;
+#endif
+       return (DLZ_DLOPEN_VERSION);
+}
+
+/*
+ * Register a helper function from the bind9 dlz_dlopen driver
+ */
+static void
+b9_add_helper(ldap_instance_t *db, const char *helper_name, void *ptr) {
+       if (strcmp(helper_name, "log") == 0)
+               db->log = (log_t *)ptr;
+       if (strcmp(helper_name, "putrr") == 0)
+               db->putrr = (dns_sdlz_putrr_t *)ptr;
+       if (strcmp(helper_name, "putnamedrr") == 0)
+               db->putnamedrr = (dns_sdlz_putnamedrr_t *)ptr;
+       if (strcmp(helper_name, "writeable_zone") == 0)
+               db->writeable_zone = (dns_dlz_writeablezone_t *)ptr;
+}
diff --git a/contrib/dlz/modules/ldap/testing/README b/contrib/dlz/modules/ldap/testing/README
new file mode 100644 (file)
index 0000000..69b1381
--- /dev/null
@@ -0,0 +1,10 @@
+These files were used for testing on Ubuntu Linux using OpenLDAP.
+
+- Move aside /etc/ldap/slapd.d
+- Move slapd.conf to /etc/ldap
+- Move dlz.schema to /etc/ldap/schema/dlz.schema
+- Run "/etc/init.d/slapd restart"
+- Run "ldapadd -x -f example.ldif -D 'cn=Manager,o=bind-dlz' -w secret"
+
+LDAP server is now loaded with example.com data from the file example.ldif
+
diff --git a/contrib/dlz/modules/ldap/testing/dlz.schema b/contrib/dlz/modules/ldap/testing/dlz.schema
new file mode 100644 (file)
index 0000000..6c79ab2
--- /dev/null
@@ -0,0 +1,187 @@
+#
+#
+# 1.3.6.1.4.1.18420.1.1.X is reserved for attribute types declared by the DLZ project.
+# 1.3.6.1.4.1.18420.1.2.X is reserved for object classes declared by the DLZ project.
+# 1.3.6.1.4.1.18420.1.3.X is reserved for PRIVATE extensions to the DLZ attribute
+#                     types and object classes that may be needed by end users
+#                     to add security, etc.  Attributes and object classes using
+#                     this OID MUST NOT be published outside of an organization
+#                     except to offer them for consideration to become part of the
+#                     standard attributes and object classes published by the DLZ project.
+
+attributetype ( 1.3.6.1.4.1.18420.1.1.10
+        NAME 'dlzZoneName'
+        DESC 'DNS zone name - domain name not including host name'
+        SUP name 
+        SINGLE-VALUE )
+        
+attributetype ( 1.3.6.1.4.1.18420.1.1.20
+       NAME 'dlzHostName'
+        DESC 'Host portion of a domain name'
+       SUP name
+        SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.18420.1.1.30
+        NAME 'dlzData'
+        DESC 'Data for the resource record'
+        SUP name
+        SINGLE-VALUE )       
+        
+attributetype ( 1.3.6.1.4.1.18420.1.1.40
+       NAME 'dlzType'
+        DESC 'DNS record type - A, SOA, NS, MX, etc...'
+        SUP name
+        SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.18420.1.1.50
+       NAME 'dlzSerial'
+        DESC 'SOA record serial number'
+        EQUALITY integerMatch
+        SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+        SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.18420.1.1.60
+       NAME 'dlzRefresh'
+        DESC 'SOA record refresh time in seconds'
+        EQUALITY integerMatch        
+       SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+        SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.18420.1.1.70
+       NAME 'dlzRetry'
+        DESC 'SOA retry time in seconds'
+        EQUALITY integerMatch        
+       SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+        SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.18420.1.1.80
+       NAME 'dlzExpire'
+        DESC 'SOA expire time in seconds'
+        EQUALITY integerMatch        
+       SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+        SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.18420.1.1.90
+       NAME 'dlzMinimum'
+        DESC 'SOA minimum time in seconds'
+        EQUALITY integerMatch        
+       SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+        SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.18420.1.1.100
+        NAME 'dlzAdminEmail'
+        DESC 'E-mail address of person responsible for this zone - @ should be replaced with . (period)'
+       SUP name
+        SINGLE-VALUE )
+
+attributetype ( 1.3.6.1.4.1.18420.1.1.110
+       NAME 'dlzPrimaryNS'
+        DESC 'Primary name server for this zone - should be host name not IP address'
+       SUP name
+        SINGLE-VALUE )
+        
+attributetype ( 1.3.6.1.4.1.18420.1.1.120
+       NAME 'dlzIPAddr'
+        DESC 'IP address - IPV4 should be in dot notation xxx.xxx.xxx.xxx IPV6 should be in colon notation xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx'
+       EQUALITY caseExactIA5Match 
+       SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{40}
+       SINGLE-VALUE )
+        
+attributetype ( 1.3.6.1.4.1.18420.1.1.130
+       NAME 'dlzCName'
+        DESC 'DNS cname'
+       SUP name
+        SINGLE-VALUE )
+        
+attributetype ( 1.3.6.1.4.1.18420.1.1.140
+       NAME 'dlzPreference'
+        DESC 'DNS MX record preference.  Lower numbers have higher preference'
+        EQUALITY integerMatch        
+       SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+        SINGLE-VALUE )
+        
+attributetype ( 1.3.6.1.4.1.18420.1.1.150
+       NAME 'dlzTTL'
+        DESC 'DNS time to live - how long this record can be cached by caching DNS servers'
+        EQUALITY integerMatch        
+       SYNTAX 1.3.6.1.4.1.1466.115.121.1.27
+        SINGLE-VALUE )
+        
+attributetype ( 1.3.6.1.4.1.18420.1.1.160
+       NAME 'dlzRecordID'
+       DESC 'Unique ID for each DLZ resource record'
+       SUP name
+       SINGLE-VALUE )
+
+#------------------------------------------------------------------------------
+# Object class definitions
+#------------------------------------------------------------------------------
+
+objectclass ( 1.3.6.1.4.1.18420.1.2.10
+       NAME 'dlzZone'         
+        DESC 'Zone name portion of a domain name'
+        SUP top STRUCTURAL
+       MUST ( objectclass $ dlzZoneName ) )
+        
+objectclass ( 1.3.6.1.4.1.18420.1.2.20
+       NAME 'dlzHost' 
+        DESC 'Host name portion of a domain name'
+        SUP top STRUCTURAL
+       MUST ( objectclass $ dlzHostName ) )
+        
+objectclass ( 1.3.6.1.4.1.18420.1.2.30
+       NAME 'dlzAbstractRecord' 
+        DESC 'Data common to all DNS record types'
+        SUP top ABSTRACT
+       MUST ( objectclass $ dlzRecordID $ dlzHostName $ dlzType $ dlzTTL ) )
+        
+objectclass ( 1.3.6.1.4.1.18420.1.2.40
+       NAME 'dlzGenericRecord' 
+        DESC 'Generic DNS record - useful when a specific object class has not been defined for a DNS record'
+        SUP dlzAbstractRecord STRUCTURAL
+       MUST ( dlzData ) )
+        
+objectclass ( 1.3.6.1.4.1.18420.1.2.50
+        NAME 'dlzARecord'
+        DESC 'DNS A record'
+        SUP dlzAbstractrecord STRUCTURAL
+        MUST ( dlzIPAddr ) )
+        
+objectclass ( 1.3.6.1.4.1.18420.1.2.60
+        NAME 'dlzNSRecord'
+        DESC 'DNS NS record'
+        SUP dlzGenericRecord STRUCTURAL )
+
+objectclass ( 1.3.6.1.4.1.18420.1.2.70
+        NAME 'dlzMXRecord'
+        DESC 'DNS MX record'
+        SUP dlzGenericRecord STRUCTURAL
+        MUST ( dlzPreference ) )
+                
+objectclass ( 1.3.6.1.4.1.18420.1.2.80
+        NAME 'dlzSOARecord'
+        DESC 'DNS SOA record'
+        SUP dlzAbstractRecord STRUCTURAL       
+        MUST ( dlzSerial $ dlzRefresh $ dlzRetry
+               $ dlzExpire $ dlzMinimum $ dlzAdminEmail $ dlzPrimaryNS ) )
+               
+objectclass ( 1.3.6.1.4.1.18420.1.2.90
+       NAME 'dlzTextRecord' 
+        DESC 'Text data with spaces should be wrapped in double quotes'
+        SUP dlzGenericRecord STRUCTURAL )
+        
+objectclass ( 1.3.6.1.4.1.18420.1.2.100
+        NAME 'dlzPTRRecord'
+        DESC 'DNS PTR record'
+        SUP dlzGenericRecord STRUCTURAL )
+        
+objectclass ( 1.3.6.1.4.1.18420.1.2.110
+        NAME 'dlzCNameRecord'
+        DESC 'DNS CName record'
+        SUP dlzGenericRecord STRUCTURAL )
+        
+objectclass ( 1.3.6.1.4.1.18420.1.2.120
+        NAME 'dlzXFR'
+        DESC 'Host allowed to perform zone transfer'
+        SUP top STRUCTURAL
+        MUST ( objectclass $ dlzRecordID $ dlzIPAddr ) )
diff --git a/contrib/dlz/modules/ldap/testing/example.ldif b/contrib/dlz/modules/ldap/testing/example.ldif
new file mode 100644 (file)
index 0000000..8362b1e
--- /dev/null
@@ -0,0 +1,168 @@
+# server suffix - o=bind-dlz
+
+dn: o=bind-dlz
+objectclass: organization
+o: bind-dlz
+
+dn: ou=dns,o=bind-dlz
+objectclass: organizationalUnit
+ou: dns
+
+dn: dlzZoneName=example.com,ou=dns,o=bind-dlz
+objectclass: dlzZone
+dlzZoneName: example.com
+
+dn: dlzHostName=@,dlzZoneName=example.com,ou=dns,o=bind-dlz
+objectclass: dlzHost
+dlzHostName: @
+
+dn: dlzHostName=www,dlzZoneName=example.com,ou=dns,o=bind-dlz
+objectclass: dlzHost
+dlzHostName: www
+
+dn: dlzHostName=mail,dlzZoneName=example.com,ou=dns,o=bind-dlz
+objectclass: dlzHost
+dlzHostName: mail
+
+dn: dlzHostName=backup,dlzZoneName=example.com,ou=dns,o=bind-dlz
+objectclass: dlzHost
+dlzHostName: backup
+
+dn: dlzHostName=ns1,dlzZoneName=example.com,ou=dns,o=bind-dlz
+objectclass: dlzHost
+dlzHostName: ns1
+
+dn: dlzHostName=ns2,dlzZoneName=example.com,ou=dns,o=bind-dlz
+objectclass: dlzHost
+dlzHostName: ns2
+
+dn: dlzHostName=~,dlzZoneName=example.com,ou=dns,o=bind-dlz
+objectclass: dlzHost
+dlzHostName: ~
+
+dn: dlzRecordID=1,dlzHostName=@,dlzZoneName=example.com,ou=dns,o=bind-dlz
+objectclass: dlzGenericRecord
+dlzRecordID: 1
+dlzHostName: @
+dlzType: txt
+dlzData: "this is a text record"
+dlzTTL: 10
+
+dn: dlzRecordID=2,dlzHostName=www,dlzZoneName=example.com,ou=dns,o=bind-dlz
+objectclass: dlzARecord
+dlzRecordID: 2
+dlzHostName: www
+dlzType: a
+dlzIPAddr: 192.168.0.1
+dlzTTL: 10
+
+dn: dlzRecordID=3,dlzHostName=mail,dlzZoneName=example.com,ou=dns,o=bind-dlz
+objectclass: dlzARecord
+dlzRecordID: 3
+dlzHostName: mail
+dlzType: a
+dlzIPAddr: 192.168.0.2
+dlzTTL: 10
+
+dn: dlzRecordID=4,dlzHostName=backup,dlzZoneName=example.com,ou=dns,o=bind-dlz
+objectclass: dlzARecord
+dlzRecordID: 4
+dlzHostName: backup
+dlzType: a
+dlzIPAddr: 192.168.0.3
+dlzTTL: 10
+
+dn: dlzRecordID=5,dlzHostName=@,dlzZoneName=example.com,ou=dns,o=bind-dlz
+objectclass: dlzMXRecord
+dlzRecordID: 5
+dlzHostName: @
+dlzType: mx
+dlzData: mail
+dlzPreference: 20
+dlzTTL: 10
+
+dn: dlzRecordID=6,dlzHostName=@,dlzZoneName=example.com,ou=dns,o=bind-dlz
+objectclass: dlzMXRecord
+dlzRecordID: 6
+dlzHostName: @
+dlzType: mx
+dlzData: backup
+dlzPreference: 40
+dlzTTL: 10
+
+dn: dlzRecordID=7,dlzHostName=www,dlzZoneName=example.com,ou=dns,o=bind-dlz
+objectclass: dlzMXRecord
+dlzRecordID: 7
+dlzHostName: www
+dlzType: mx
+dlzData: backup
+dlzPreference: 40
+dlzTTL: 10
+
+dn: dlzRecordID=8,dlzHostName=www,dlzZoneName=example.com,ou=dns,o=bind-dlz
+objectclass: dlzMXRecord
+dlzRecordID: 8
+dlzHostName: www
+dlzType: mx
+dlzData: mail
+dlzPreference: 20
+dlzTTL: 10
+
+dn: dlzRecordID=9,dlzHostName=ns1,dlzZoneName=example.com,ou=dns,o=bind-dlz
+objectclass: dlzARecord
+dlzRecordID: 9
+dlzHostName: ns1
+dlzType: a
+dlzIPAddr: 192.168.0.4
+dlzTTL: 10
+
+dn: dlzRecordID=10,dlzHostName=ns2,dlzZoneName=example.com,ou=dns,o=bind-dlz
+objectclass: dlzARecord
+dlzRecordID: 10
+dlzHostName: ns2
+dlzType: a
+dlzIPAddr: 192.168.0.5
+dlzTTL: 10
+
+dn: dlzRecordID=11,dlzHostName=@,dlzZoneName=example.com,ou=dns,o=bind-dlz
+objectclass: dlzSOARecord
+dlzRecordID: 11
+dlzHostName: @
+dlzType: soa
+dlzSerial: 2
+dlzRefresh: 2800
+dlzRetry: 7200
+dlzExpire: 604800
+dlzMinimum: 86400
+dlzAdminEmail: root.example.com.
+dlzPrimaryns: ns1.example.com.
+dlzTTL: 10
+
+dn: dlzRecordID=12,dlzHostName=@,dlzZoneName=example.com,ou=dns,o=bind-dlz
+objectclass: dlzNSRecord
+dlzRecordID: 12
+dlzHostName: @
+dlzType: ns
+dlzData: ns1.example.com.
+dlzTTL: 10
+
+dn: dlzRecordID=13,dlzHostName=@,dlzZoneName=example.com,ou=dns,o=bind-dlz
+objectclass: dlzNSRecord
+dlzRecordID: 13
+dlzHostName: @
+dlzType: ns
+dlzData: ns2
+dlzTTL: 10
+
+dn: dlzRecordID=14,dlzHostName=~,dlzZoneName=example.com,ou=dns,o=bind-dlz
+objectclass: dlzARecord
+dlzRecordID: 14
+dlzHostName: ~
+dlzType: a
+dlzIPAddr: 192.168.0.250
+dlzTTL: 10
+
+dn: dlzRecordID=15,dlzZoneName=example.com,ou=dns,o=bind-dlz
+objectclass: dlzXFR
+dlzRecordID: 15
+dlzIPAddr: 127.0.0.1
diff --git a/contrib/dlz/modules/ldap/testing/named.conf b/contrib/dlz/modules/ldap/testing/named.conf
new file mode 100644 (file)
index 0000000..e79a02e
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or 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.
+ */
+
+controls { };
+
+options {
+       directory ".";
+       port 5300;
+       pid-file "named.pid";
+       session-keyfile "session.key";
+       listen-on { any; };
+       listen-on-v6 { none; };
+       recursion no;
+};
+
+key rndc_key {
+       secret "1234abcd8765";
+       algorithm hmac-md5;
+};
+
+controls {
+       inet 127.0.0.1 port 9953 allow { any; } keys { rndc_key; };
+};
+
+dlz "test" {
+       database "dlopen ../dlz_ldap_dynamic.so 2
+        v3 simple {cn=Manager,o=bind-dlz} {secret} {127.0.0.1}
+        ldap:///dlzZoneName=$zone$,ou=dns,o=bind-dlz???objectclass=dlzZone
+        ldap:///dlzHostName=$record$,dlzZoneName=$zone$,ou=dns,o=bind-dlz?dlzTTL,dlzType,dlzPreference,dlzData,dlzIPAddr?sub?(&(objectclass=dlzAbstractRecord)(!(dlzType=soa)))
+        ldap:///dlzHostName=@,dlzZoneName=$zone$,ou=dns,o=bind-dlz?dlzTTL,dlzType,dlzData,dlzPrimaryNS,dlzAdminEmail,dlzSerial,dlzRefresh,dlzRetry,dlzExpire,dlzMinimum?sub?(&(objectclass=dlzAbstractRecord)(dlzType=soa))
+        ldap:///dlzZoneName=$zone$,ou=dns,o=bind-dlz?dlzTTL,dlzType,dlzHostName,dlzPreference,dlzData,dlzIPAddr,dlzPrimaryNS,dlzAdminEmail,dlzSerial,dlzRefresh,dlzRetry,dlzExpire,dlzMinimum?sub?(&(objectclass=dlzAbstractRecord)(!(dlzType=soa)))
+        ldap:///dlzZoneName=$zone$,ou=dns,o=bind-dlz??sub?(&(objectclass=dlzXFR)(dlzIPAddr=$client$))";
+};
diff --git a/contrib/dlz/modules/ldap/testing/slapd.conf b/contrib/dlz/modules/ldap/testing/slapd.conf
new file mode 100644 (file)
index 0000000..14c8ffb
--- /dev/null
@@ -0,0 +1,44 @@
+# this is the full path to the core.schema
+include               /etc/ldap/schema/core.schema
+
+# this is the full path to the dlz.schema
+include               /etc/ldap/schema/dlz.schema
+
+# these files hold the slapd process ID and program args when
+# slapd is started.
+pidfile         /var/run/slapd/slapd.pid
+argsfile        /var/run/slapd/slapd.args
+
+modulepath      /usr/lib/ldap
+moduleload      back_hdb
+
+# this allows ldap version 2 connections.  You should comment
+# it out if you don't need ldap version 2.
+allow bind_v2
+
+# this sets up the Berkeley DB database backend for LDAP to use.
+database      hdb
+
+# This is the root of the LDAP server.  You still need to add
+# an entry to this location via a LDIF file, or you won't be
+# able to add anything else into the LDAP server. 
+suffix                "o=bind-dlz"
+
+# this is the "username" you have to use when connecting to the
+# ldap server to make updates.  Type the whole thing exactly
+# as you see it as a parameter to ldapadd. 
+rootdn                "cn=Manager,o=bind-dlz"
+
+# this is the "password" you have to use when connecting to the
+# ldap server to make updates.
+rootpw                secret
+
+# this is the directory that the LDAP server will create the
+# Berkeley DB backend in.
+directory     /var/lib/ldap
+
+# this just adds some indexing to the LDAP server.
+# probably should have more to better optimize DLZ LDAP searches.
+index cn,sn,uid pres,eq
+index objectClass     eq
+
diff --git a/contrib/dlz/modules/wildcard/Makefile b/contrib/dlz/modules/wildcard/Makefile
new file mode 100644 (file)
index 0000000..20a5d4e
--- /dev/null
@@ -0,0 +1,20 @@
+prefix = /usr
+libdir = $(prefix)/lib/bind9
+
+CFLAGS=-fPIC -g -I../include
+
+all: dlz_wildcard_dynamic.so
+
+dlz_dbi.o: ../common/dlz_dbi.c
+       $(CC) $(CFLAGS) -c ../common/dlz_dbi.c
+
+dlz_wildcard_dynamic.so: dlz_wildcard_dynamic.c dlz_dbi.o
+       $(CC) $(CFLAGS) -shared -o dlz_wildcard_dynamic.so \
+               dlz_wildcard_dynamic.c dlz_dbi.o
+
+clean:
+       rm -f dlz_wildcard_dynamic.so *.o
+
+install: dlz_wildcard_dynamic.so
+       mkdir -p $(DESTDIR)$(libdir)
+       install dlz_wildcard_dynamic.so $(DESTDIR)$(libdir)
diff --git a/contrib/dlz/modules/wildcard/README b/contrib/dlz/modules/wildcard/README
new file mode 100644 (file)
index 0000000..b19009b
--- /dev/null
@@ -0,0 +1,31 @@
+The "wildcard" DLZ module provides a "template" zone for domains matching
+a wildcard name.  For example, the following DLZ configuration would match
+any zone name containing the string "example" and ending with .com, such
+as "thisexample.com", "exampleofthat.com", or "anexampleoftheotherthing.com".
+
+    dlz "test" {
+       database "dlopen ../dlz_wildcard_dynamic.so
+        *example*.com 10.53.* 1800
+        @      3600    SOA   {ns3.example.nil. support.example.nil. 42 14400 7200 2592000 600}
+        @      3600    NS     ns3.example.nil.
+        @      3600    NS     ns4.example.nil.
+        @      3600    NS     ns8.example.nil.
+        @      3600    MX     {5 mail.example.nil.}
+        ftp    86400   A      192.0.0.1
+        sql    86400   A      192.0.0.2
+        tmp    {}      A      192.0.0.3
+        www    86400   A      192.0.0.3
+        www    86400   AAAA   ::1
+        txt    300     TXT    {\"you requested $record$ in $zone$\"}
+        *      86400   A      192.0.0.100";
+    };
+
+For any zone name matchin the wildcard, it would return the data from
+the template.  "$zone$" is replaced with zone name: i.e., the shortest
+possible string of labels in the query name that matches the wildcard.
+"$record$" is replaced with the remainder of the query name.  In the
+example above, a query for "txt.thisexample.com/TXT" would return the
+string "you requested txt in thisexample.com".
+
+Any client whose source address matches the second wildcard ("10.53.*")
+is allowed to request a zone transfer.
diff --git a/contrib/dlz/modules/wildcard/dlz_wildcard_dynamic.c b/contrib/dlz/modules/wildcard/dlz_wildcard_dynamic.c
new file mode 100644 (file)
index 0000000..781ebd9
--- /dev/null
@@ -0,0 +1,738 @@
+/*
+ * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
+ * Copyright (C) 2012 Vadim Goncharov, Russia, vadim_nuclight@mail.ru.
+ *
+ * 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) 2013  Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001  Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and/or 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.
+ */
+
+/*
+ * This provides the externally loadable wildcard DLZ module.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+#include <dlz_minimal.h>
+#include <dlz_list.h>
+#include <dlz_dbi.h>
+
+#include <ctype.h>
+
+#define DE_CONST(konst, var) \
+       do { \
+               union { const void *k; void *v; } _u; \
+               _u.k = konst; \
+               var = _u.v; \
+       } while (0)
+
+/* fnmatch() return values. */
+#define        FNM_NOMATCH     1       /* Match failed. */
+
+/* fnmatch() flags. */
+#define        FNM_NOESCAPE    0x01    /* Disable backslash escaping. */
+#define        FNM_PATHNAME    0x02    /* Slash must be matched by slash. */
+#define        FNM_PERIOD      0x04    /* Period must be matched by period. */
+#define        FNM_LEADING_DIR 0x08    /* Ignore /<tail> after Imatch. */
+#define        FNM_CASEFOLD    0x10    /* Case insensitive search. */
+#define        FNM_IGNORECASE  FNM_CASEFOLD
+#define        FNM_FILE_NAME   FNM_PATHNAME
+
+/*
+ * Our data structures.
+ */
+
+typedef struct named_rr nrr_t;
+typedef DLZ_LIST(nrr_t) rr_list_t;
+
+typedef struct config_data {
+       char            *zone_pattern;
+       char            *axfr_pattern;
+       rr_list_t       rrs_list;
+       char            *zone;
+       char            *record;
+       char            *client;
+
+       /* Helper functions from the dlz_dlopen driver */
+       log_t *log;
+       dns_sdlz_putrr_t *putrr;
+       dns_sdlz_putnamedrr_t *putnamedrr;
+       dns_dlz_writeablezone_t *writeable_zone;
+} config_data_t;
+
+struct named_rr {
+       char            *name;
+       char            *type;
+       int             ttl;
+       query_list_t    *data;
+       DLZ_LINK(nrr_t) link;
+};
+
+/*
+ * Forward references
+ */
+static int
+rangematch(const char *, char, int, char **);
+
+static int
+fnmatch(const char *pattern, const char *string, int flags);
+
+static void
+b9_add_helper(struct config_data *cd, const char *helper_name, void *ptr);
+
+static const char *
+shortest_match(const char *pattern, const char *string);
+
+isc_result_t
+dlz_allnodes(const char *zone, void *dbdata, dns_sdlzallnodes_t *allnodes) {
+       config_data_t *cd = (config_data_t *) dbdata;
+       isc_result_t result;
+       char *querystring = NULL;
+       nrr_t *nrec;
+       int i = 0;
+
+       DE_CONST(zone, cd->zone);
+
+       /* Write info message to log */
+       cd->log(ISC_LOG_DEBUG(1),
+               "dlz_wildcard allnodes called for zone '%s'", zone);
+
+       result = ISC_R_FAILURE;
+
+       nrec = DLZ_LIST_HEAD(cd->rrs_list);
+       while (nrec != NULL) {
+               cd->record = nrec->name;
+
+               querystring = build_querystring(nrec->data);
+
+               if (querystring == NULL) {
+                       result = ISC_R_NOMEMORY;
+                       goto done;
+               }
+
+               cd->log(ISC_LOG_DEBUG(2),
+                       "dlz_wildcard allnodes entry num %d: calling "
+                       "putnamedrr(name=%s type=%s ttl=%d qs=%s)",
+                       i++, nrec->name, nrec->type, nrec->ttl, querystring);
+
+               result = cd->putnamedrr(allnodes, nrec->name, nrec->type,
+                                       nrec->ttl, querystring);
+               if (result != ISC_R_SUCCESS)
+                       goto done;
+
+               nrec = DLZ_LIST_NEXT(nrec, link);
+       }
+
+done:
+       cd->zone = NULL;
+
+       if (querystring != NULL)
+               free(querystring);
+
+       return (result);
+}
+
+isc_result_t
+dlz_allowzonexfr(void *dbdata, const char *name, const char *client) {
+       config_data_t *cd = (config_data_t *) dbdata;
+
+       UNUSED(name);
+
+       /* Write info message to log */
+       cd->log(ISC_LOG_DEBUG(1),
+               "dlz_wildcard allowzonexfr called for client '%s'", client);
+
+       if (fnmatch(cd->axfr_pattern, client, FNM_CASEFOLD) == 0)
+               return (ISC_R_SUCCESS);
+       else
+               return (ISC_R_NOTFOUND);
+}
+
+#if DLZ_DLOPEN_VERSION < 3
+isc_result_t
+dlz_findzonedb(void *dbdata, const char *name)
+#else
+isc_result_t
+dlz_findzonedb(void *dbdata, const char *name,
+              dns_clientinfomethods_t *methods,
+              dns_clientinfo_t *clientinfo)
+#endif
+{
+       config_data_t *cd = (config_data_t *) dbdata;
+       const char *p;
+
+#if DLZ_DLOPEN_VERSION >= 3
+       UNUSED(methods);
+       UNUSED(clientinfo);
+#endif
+
+       p = shortest_match(cd->zone_pattern, name);
+       if (p == NULL)
+               return (ISC_R_NOTFOUND);
+
+       /* Write info message to log */
+       cd->log(ISC_LOG_DEBUG(1),
+               "dlz_wildcard findzonedb matched '%s'", p);
+
+       return (ISC_R_SUCCESS);
+}
+
+#if DLZ_DLOPEN_VERSION == 1
+isc_result_t
+dlz_lookup(const char *zone, const char *name,
+          void *dbdata, dns_sdlzlookup_t *lookup)
+#else
+isc_result_t
+dlz_lookup(const char *zone, const char *name,
+          void *dbdata, dns_sdlzlookup_t *lookup,
+          dns_clientinfomethods_t *methods,
+          dns_clientinfo_t *clientinfo)
+#endif
+{
+       isc_result_t result;
+       config_data_t *cd = (config_data_t *) dbdata;
+       char *querystring = NULL;
+       const char *p;
+       char *namebuf;
+       nrr_t *nrec;
+       isc_boolean_t origin = ISC_TRUE;
+
+#if DLZ_DLOPEN_VERSION >= 2
+       UNUSED(methods);
+       UNUSED(clientinfo);
+#endif
+
+       p = shortest_match(cd->zone_pattern, zone);
+       if (p == NULL)
+               return (ISC_R_NOTFOUND);
+
+       DE_CONST(name, cd->record);
+       DE_CONST(p, cd->zone);
+
+       if ((p != zone) && (strcmp(name, "@") == 0 || strcmp(name, zone) == 0))
+       {
+               size_t len = p - zone;
+               namebuf = malloc(len);
+               strncpy(namebuf, zone, len - 1);
+               namebuf[len - 1] = '\0';
+               cd->record = namebuf;
+               origin = ISC_FALSE;
+       } else if (p == zone)
+               cd->record = "@";
+
+       /* Write info message to log */
+       cd->log(ISC_LOG_DEBUG(1),
+               "dlz_wildcard_dynamic: lookup for '%s' in '%s': "
+               "trying '%s' in '%s'",
+               name, zone, cd->record, cd->zone);
+
+       result = ISC_R_NOTFOUND;
+       nrec = DLZ_LIST_HEAD(cd->rrs_list);
+       while (nrec != NULL) {
+               nrr_t *next = DLZ_LIST_NEXT(nrec, link);
+               if (strcmp(cd->record, nrec->name) == 0) {
+                       /* We handle authority data in dlz_authority() */
+                       if (strcmp(nrec->type, "SOA") == 0 ||
+                           strcmp(nrec->type, "NS") == 0)
+                       {
+                               nrec = next;
+                               continue;
+                       }
+
+                       querystring = build_querystring(nrec->data);
+                       if (querystring == NULL) {
+                               result = ISC_R_NOMEMORY;
+                               goto done;
+                       }
+
+                       result = cd->putrr(lookup, nrec->type,
+                                          nrec->ttl, querystring);
+                       if (result != ISC_R_SUCCESS)
+                               goto done;
+
+fprintf(stderr, "setting result to success on %s/%s\n", nrec->name, nrec->type);
+                       result = ISC_R_SUCCESS;
+
+                       free(querystring);
+                       querystring = NULL;
+               }
+               nrec = next;
+       }
+
+done:
+       cd->zone = NULL;
+       cd->record = NULL;
+
+       if (querystring != NULL)
+               free(querystring);
+
+       return (result);
+}
+
+isc_result_t
+dlz_authority(const char *zone, void *dbdata, dns_sdlzlookup_t *lookup) {
+       isc_result_t result;
+       config_data_t *cd = (config_data_t *) dbdata;
+       char *querystring = NULL;
+       nrr_t *nrec;
+       const char *p, *name = "@";
+
+       p = shortest_match(cd->zone_pattern, zone);
+       if (p == NULL)
+               return (ISC_R_NOTFOUND);
+
+       DE_CONST(p, cd->zone);
+
+       /* Write info message to log */
+       cd->log(ISC_LOG_DEBUG(1),
+               "dlz_wildcard_dynamic: authority for '%s'", zone);
+
+       result = ISC_R_NOTFOUND;
+       nrec = DLZ_LIST_HEAD(cd->rrs_list);
+       while (nrec != NULL) {
+               isc_boolean_t origin;
+               if (strcmp("@", nrec->name) == 0) {
+                       isc_result_t presult;
+
+                       querystring = build_querystring(nrec->data);
+                       if (querystring == NULL) {
+                               result = ISC_R_NOMEMORY;
+                               goto done;
+                       }
+
+                       presult = cd->putrr(lookup, nrec->type,
+                                          nrec->ttl, querystring);
+                       if (presult != ISC_R_SUCCESS) {
+                               result = presult;
+                               goto done;
+                       }
+
+                       result = ISC_R_SUCCESS;
+
+                       free(querystring);
+                       querystring = NULL;
+               }
+               nrec = DLZ_LIST_NEXT(nrec, link);
+       }
+
+done:
+       cd->zone = NULL;
+
+       if (querystring != NULL)
+               free(querystring);
+
+       return (result);
+}
+
+static void
+destroy_rrlist(config_data_t *cd) {
+       nrr_t *trec, *nrec;
+
+       nrec = DLZ_LIST_HEAD(cd->rrs_list);
+
+       while (nrec != NULL) {
+               trec = nrec;
+
+               destroy_querylist(&trec->data);
+
+               if (trec->name != NULL)
+                       free(trec->name);
+               if (trec->type != NULL)
+                       free(trec->type);
+               trec->name = trec->type = NULL;
+
+               /* Get the next record, before we destroy this one. */
+               nrec = DLZ_LIST_NEXT(nrec, link);
+
+               free(trec);
+       }
+}
+
+isc_result_t
+dlz_create(const char *dlzname, unsigned int argc, char *argv[],
+          void **dbdata, ...)
+{
+       config_data_t *cd;
+       char *endp;
+       int i, def_ttl;
+       nrr_t *trec = NULL;
+       isc_result_t result;
+       const char *helper_name;
+       va_list ap;
+
+       if (argc < 8 || argc % 4 != 0)
+               return (ISC_R_FAILURE);
+
+       cd = calloc(1, sizeof(config_data_t));
+       if (cd == NULL)
+               return (ISC_R_NOMEMORY);
+       memset(cd, 0, sizeof(config_data_t));
+
+       /* Fill in the helper functions */
+       va_start(ap, dbdata);
+       while ((helper_name = va_arg(ap, const char*)) != NULL)
+               b9_add_helper(cd, helper_name, va_arg(ap, void*));
+       va_end(ap);
+
+       /*
+        * Write info message to log
+        */
+       cd->log(ISC_LOG_INFO,
+               "Loading '%s' using DLZ_wildcard driver. "
+               "Zone: %s, AXFR allowed for: %s, $TTL: %s",
+               dlzname, argv[1], argv[2], argv[3]);
+
+       /* initialize the records list here to simplify cleanup */
+       DLZ_LIST_INIT(cd->rrs_list);
+
+       cd->zone_pattern = strdup(argv[1]);
+       if (cd->zone_pattern == NULL)
+               goto cleanup;
+
+       cd->axfr_pattern = strdup(argv[2]);
+       if (cd->axfr_pattern == NULL)
+               goto cleanup;
+
+       def_ttl = strtol(argv[3], &endp, 10);
+       if (*endp != '\0' || def_ttl < 0) {
+               def_ttl = 3600;
+               cd->log(ISC_LOG_ERROR, "default TTL invalid, using 3600");
+       }
+
+       for (i = 4; i < argc; i += 4) {
+               result = ISC_R_NOMEMORY;
+
+               trec = malloc(sizeof(nrr_t));
+               if (trec == NULL)
+                       goto full_cleanup;
+
+               memset(trec, 0, sizeof(nrr_t));
+
+               /* Initialize the record link */
+               DLZ_LINK_INIT(trec, link);
+               /* Append the record to the list */
+               DLZ_LIST_APPEND(cd->rrs_list, trec, link);
+
+               trec->name = strdup(argv[i]);
+               if (trec->name == NULL)
+                       goto full_cleanup;
+
+               trec->type = strdup(argv[i + 2]);
+               if (trec->type == NULL)
+                       goto full_cleanup;
+
+               trec->ttl = strtol(argv[i + 1], &endp, 10);
+               if (argv[i + 1][0] == '\0' || *endp != '\0' || trec->ttl < 0)
+                       trec->ttl = def_ttl;
+
+               result = build_querylist(argv[i + 3], &cd->zone,
+                                        &cd->record, &cd->client,
+                                        &trec->data, 0, cd->log);
+               /* If unsuccessful, log err msg and cleanup */
+               if (result != ISC_R_SUCCESS) {
+                       cd->log(ISC_LOG_ERROR,
+                               "Could not build RR data list at argv[%d]",
+                               i + 3);
+                       goto full_cleanup;
+               }
+       }
+
+       *dbdata = cd;
+
+       return (ISC_R_SUCCESS);
+
+full_cleanup:
+       destroy_rrlist(cd);
+
+cleanup:
+       if (cd->zone_pattern != NULL)
+               free(cd->zone_pattern);
+       if (cd->axfr_pattern != NULL)
+               free(cd->axfr_pattern);
+       free(cd);
+
+       return (result);
+}
+
+void
+dlz_destroy(void *dbdata) {
+       config_data_t *cd = (config_data_t *) dbdata;
+
+       /*
+        * Write debugging message to log
+        */
+       cd->log(ISC_LOG_DEBUG(2), "Unloading DLZ_wildcard driver.");
+
+       destroy_rrlist(cd);
+
+       free(cd->zone_pattern);
+       free(cd->axfr_pattern);
+       free(cd);
+}
+
+
+/*
+ * Return the version of the API
+ */
+int
+dlz_version(unsigned int *flags) {
+       UNUSED(flags);
+       /* XXX: ok to set DNS_SDLZFLAG_THREADSAFE here? */
+       return (DLZ_DLOPEN_VERSION);
+}
+
+/*
+ * Register a helper function from the bind9 dlz_dlopen driver
+ */
+static void
+b9_add_helper(struct config_data *cd, const char *helper_name, void *ptr) {
+       if (strcmp(helper_name, "log") == 0)
+               cd->log = (log_t *)ptr;
+       if (strcmp(helper_name, "putrr") == 0)
+               cd->putrr = (dns_sdlz_putrr_t *)ptr;
+       if (strcmp(helper_name, "putnamedrr") == 0)
+               cd->putnamedrr = (dns_sdlz_putnamedrr_t *)ptr;
+       if (strcmp(helper_name, "writeable_zone") == 0)
+               cd->writeable_zone = (dns_dlz_writeablezone_t *)ptr;
+}
+
+static const char *
+shortest_match(const char *pattern, const char *string) {
+       const char *p = string;
+       if (pattern == NULL || p == NULL || *p == '\0')
+               return (NULL);
+
+       p += strlen(p);
+       while (p-- > string) {
+               if (*p == '.') {
+                       if (fnmatch(pattern, p + 1, FNM_CASEFOLD) == 0)
+                               return (p + 1);
+               }
+       }
+       if (fnmatch(pattern, string, FNM_CASEFOLD) == 0)
+               return (string);
+
+       return (NULL);
+}
+
+/*
+ * The helper functions stolen from the FreeBSD kernel (sys/libkern/fnmatch.c).
+ *
+ * Why don't we use fnmatch(3) from libc? Because it is not thread-safe, and
+ * it is not thread-safe because it supports multibyte characters. But here,
+ * in BIND, we want to be thread-safe and don't need multibyte - DNS names are
+ * always ASCII.
+ */
+#define        EOS     '\0'
+
+#define RANGE_MATCH     1
+#define RANGE_NOMATCH   0
+#define RANGE_ERROR     (-1)
+
+static int
+fnmatch(const char *pattern, const char *string, int flags) {
+       const char *stringstart;
+       char *newp;
+       char c, test;
+
+       for (stringstart = string;;)
+               switch (c = *pattern++) {
+               case EOS:
+                       if ((flags & FNM_LEADING_DIR) && *string == '/')
+                               return (0);
+                       return (*string == EOS ? 0 : FNM_NOMATCH);
+               case '?':
+                       if (*string == EOS)
+                               return (FNM_NOMATCH);
+                       if (*string == '/' && (flags & FNM_PATHNAME))
+                               return (FNM_NOMATCH);
+                       if (*string == '.' && (flags & FNM_PERIOD) &&
+                           (string == stringstart ||
+                           ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
+                               return (FNM_NOMATCH);
+                       ++string;
+                       break;
+               case '*':
+                       c = *pattern;
+                       /* Collapse multiple stars. */
+                       while (c == '*')
+                               c = *++pattern;
+
+                       if (*string == '.' && (flags & FNM_PERIOD) &&
+                           (string == stringstart ||
+                           ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
+                               return (FNM_NOMATCH);
+
+                       /* Optimize for pattern with * at end or before /. */
+                       if (c == EOS)
+                               if (flags & FNM_PATHNAME)
+                                       return ((flags & FNM_LEADING_DIR) ||
+                                           index(string, '/') == NULL ?
+                                           0 : FNM_NOMATCH);
+                               else
+                                       return (0);
+                       else if (c == '/' && flags & FNM_PATHNAME) {
+                               if ((string = index(string, '/')) == NULL)
+                                       return (FNM_NOMATCH);
+                               break;
+                       }
+
+                       /* General case, use recursion. */
+                       while ((test = *string) != EOS) {
+                               if (!fnmatch(pattern, string,
+                                            flags & ~FNM_PERIOD))
+                                       return (0);
+                               if (test == '/' && flags & FNM_PATHNAME)
+                                       break;
+                               ++string;
+                       }
+                       return (FNM_NOMATCH);
+               case '[':
+                       if (*string == EOS)
+                               return (FNM_NOMATCH);
+                       if (*string == '/' && (flags & FNM_PATHNAME))
+                               return (FNM_NOMATCH);
+                       if (*string == '.' && (flags & FNM_PERIOD) &&
+                           (string == stringstart ||
+                           ((flags & FNM_PATHNAME) && *(string - 1) == '/')))
+                               return (FNM_NOMATCH);
+
+                       switch (rangematch(pattern, *string, flags, &newp)) {
+                       case RANGE_ERROR:
+                               goto norm;
+                       case RANGE_MATCH:
+                               pattern = newp;
+                               break;
+                       case RANGE_NOMATCH:
+                               return (FNM_NOMATCH);
+                       }
+                       ++string;
+                       break;
+               case '\\':
+                       if (!(flags & FNM_NOESCAPE)) {
+                               if ((c = *pattern++) == EOS) {
+                                       c = '\\';
+                                       --pattern;
+                               }
+                       }
+                       /* FALLTHROUGH */
+               default:
+               norm:
+                       if (c == *string)
+                               ;
+                       else if ((flags & FNM_CASEFOLD) &&
+                                (tolower((unsigned char)c) ==
+                                 tolower((unsigned char)*string)))
+                               ;
+                       else
+                               return (FNM_NOMATCH);
+                       string++;
+                       break;
+               }
+       /* NOTREACHED */
+}
+
+static int
+rangematch(const char *pattern, char test, int flags, char **newp) {
+       int negate, ok;
+       char c, c2;
+
+       /*
+        * A bracket expression starting with an unquoted circumflex
+        * character produces unspecified results (IEEE 1003.2-1992,
+        * 3.13.2).  This implementation treats it like '!', for
+        * consistency with the regular expression syntax.
+        * J.T. Conklin (conklin@ngai.kaleida.com)
+        */
+       if ( (negate = (*pattern == '!' || *pattern == '^')) )
+               ++pattern;
+
+       if (flags & FNM_CASEFOLD)
+               test = tolower((unsigned char)test);
+
+       /*
+        * A right bracket shall lose its special meaning and represent
+        * itself in a bracket expression if it occurs first in the list.
+        * -- POSIX.2 2.8.3.2
+        */
+       ok = 0;
+       c = *pattern++;
+       do {
+               if (c == '\\' && !(flags & FNM_NOESCAPE))
+                       c = *pattern++;
+               if (c == EOS)
+                       return (RANGE_ERROR);
+
+               if (c == '/' && (flags & FNM_PATHNAME))
+                       return (RANGE_NOMATCH);
+
+               if (flags & FNM_CASEFOLD)
+                       c = tolower((unsigned char)c);
+
+               if (*pattern == '-'
+                   && (c2 = *(pattern+1)) != EOS && c2 != ']') {
+                       pattern += 2;
+                       if (c2 == '\\' && !(flags & FNM_NOESCAPE))
+                               c2 = *pattern++;
+                       if (c2 == EOS)
+                               return (RANGE_ERROR);
+
+                       if (flags & FNM_CASEFOLD)
+                               c2 = tolower((unsigned char)c2);
+
+                       if (c <= test && test <= c2)
+                               ok = 1;
+               } else if (c == test)
+                       ok = 1;
+       } while ((c = *pattern++) != ']');
+
+       *newp = (char *)(uintptr_t)pattern;
+       return (ok == negate ? RANGE_NOMATCH : RANGE_MATCH);
+}
diff --git a/contrib/dlz/modules/wildcard/testing/named.conf b/contrib/dlz/modules/wildcard/testing/named.conf
new file mode 100644 (file)
index 0000000..0192e18
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2013  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * Permission to use, copy, modify, and/or 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.
+ */
+
+controls { };
+
+options {
+       directory ".";
+       port 5300;
+       pid-file "named.pid";
+       session-keyfile "session.key";
+       listen-on { any; };
+       listen-on-v6 { none; };
+       recursion no;
+};
+
+key rndc_key {
+       secret "1234abcd8765";
+       algorithm hmac-md5;
+};
+
+controls {
+       inet 127.0.0.1 port 9953 allow { any; } keys { rndc_key; };
+};
+
+/*
+ * This will match any zone name containing the string "example" and
+ * ending with .com, such as "thisexample.com", "exampleofthat.com",
+ * or "anexampleoftheotherthing.com".
+ */
+dlz "test" {
+       database "dlopen ../dlz_wildcard_dynamic.so
+        *example*.com 10.53.* 1800
+        @      3600    SOA   {ns3.example.nil. support.example.nil. 42 14400 7200 2592000 600}
+        @      3600    NS     ns3.example.nil.
+        @      3600    NS     ns4.example.nil.
+        @      3600    NS     ns8.example.nil.
+        @      3600    MX     {5 mail.example.nil.}
+        ftp    86400   A      192.0.0.1
+        sql    86400   A      192.0.0.2
+        tmp    {}      A      192.0.0.3
+        www    86400   A      192.0.0.3
+        www    86400   AAAA   ::1
+        txt    300     TXT    {\"you requested $record$ in $zone$\"}
+        *      86400   A      192.0.0.100";
+};
index 9197f923ef4ab779a8e898d792004f4bba671b8b..dcf39f7a0eb7628de8635c67e975c796e29668dd 100644 (file)
@@ -1205,7 +1205,6 @@ settask(dns_db_t *db, isc_task_t *task) {
        UNUSED(task);
 }
 
-
 /*
  * getoriginnode() is used by the update code to find the
  * dns_rdatatype_dnskey record for a zone
@@ -1222,7 +1221,7 @@ getoriginnode(dns_db_t *db, dns_dbnode_t **nodep) {
        result = findnodeext(db, &sdlz->common.origin, ISC_FALSE,
                             NULL, NULL, nodep);
        if (result != ISC_R_SUCCESS)
-               sdlz_log(ISC_LOG_ERROR, "sdlz getoriginnode failed : %s",
+               sdlz_log(ISC_LOG_ERROR, "sdlz getoriginnode failed: %s",
                         isc_result_totext(result));
        return (result);
 }