+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]
result = dns_sdlzregister("dlopen", &dlz_dlopen_methods, NULL,
DNS_SDLZFLAG_RELATIVEOWNER |
+ DNS_SDLZFLAG_RELATIVERDATA |
DNS_SDLZFLAG_THREADSAFE,
mctx, &dlz_dlopen);
result = dns_sdlzregister("dlopen", &dlz_dlopen_methods, NULL,
DNS_SDLZFLAG_RELATIVEOWNER |
+ DNS_SDLZFLAG_RELATIVERDATA |
DNS_SDLZFLAG_THREADSAFE,
mctx, &dlz_dlopen);
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)
#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
*/
#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;
}
/*
*/
#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,
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;
}
+
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+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)
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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 */
* tree.
*/
+#ifndef DLZ_MINIMAL_H
+#define DLZ_MINIMAL_H 1
+
#include <sys/types.h>
#include <sys/socket.h>
#ifdef ISC_PLATFORM_HAVESYSUNH
#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
#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
#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)
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
isc_result_t
dlz_delrdataset(const char *name, const char *type, void *dbdata,
void *version);
+
+#endif /* DLZ_MINIMAL_H */
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+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)
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+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
+
--- /dev/null
+#
+#
+# 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 ) )
--- /dev/null
+# 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
--- /dev/null
+/*
+ * 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$))";
+};
--- /dev/null
+# 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
+
--- /dev/null
+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)
--- /dev/null
+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.
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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";
+};
UNUSED(task);
}
-
/*
* getoriginnode() is used by the update code to find the
* dns_rdatatype_dnskey record for a zone
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);
}