diff options
Diffstat (limited to 'contrib')
-rw-r--r-- | contrib/dlz/example/README | 15 | ||||
-rw-r--r-- | contrib/dlz/example/dlz_example.c | 2 | ||||
-rw-r--r-- | contrib/dlz/modules/bdbhpt/Makefile | 18 | ||||
-rw-r--r-- | contrib/dlz/modules/bdbhpt/README.md | 89 | ||||
-rw-r--r-- | contrib/dlz/modules/bdbhpt/dlz_bdbhpt_dynamic.c | 836 | ||||
-rwxr-xr-x | contrib/dlz/modules/bdbhpt/testing/bdbhpt-populate.pl | 232 | ||||
-rw-r--r-- | contrib/dlz/modules/bdbhpt/testing/dns-data.txt | 19 | ||||
-rw-r--r-- | contrib/dlz/modules/dlz_minimal.h (renamed from contrib/dlz/example/dlz_minimal.h) | 37 |
8 files changed, 1227 insertions, 21 deletions
diff --git a/contrib/dlz/example/README b/contrib/dlz/example/README index 3468b6d8..eb99b48b 100644 --- a/contrib/dlz/example/README +++ b/contrib/dlz/example/README @@ -55,8 +55,8 @@ a particular name depending on the network from which the query arrived.) IMPLEMENTATION NOTES: The minimal set of type definitions, prototypes, and macros needed -for implementing a DLZ driver is in dlz_minimal.h. Copy this header -file into your source tree when creating an external DLZ module. +for implementing a DLZ driver is in ../modules/dlz_minimal.h. Copy this +header file into your source tree when creating an external DLZ module. The DLZ dlopen driver provides a set of callback functions: @@ -90,11 +90,12 @@ are mandatory, others optional). Required for alL external DLZ modules, to indicate the version number of the DLZ dlopen driver that this module supports. It should return - the value DLZ_DLOPEN_VERSION, which is defined in dlz_minimal.h and - is currently 2. 'flags' is updated to indicate capabilities - of the module. In particular, if the module is thread-safe then it - sets 'flags' to include DNS_SDLZFLAG_THREADSAFE. (Other capability - flags may be added in the future.) + the value DLZ_DLOPEN_VERSION, which is defined in the file + contrib/dlz/modules/dlz_minimal.h and is currently 3. 'flags' is + updated to indicate capabilities of the module. In particular, if + the module is thread-safe then it sets 'flags' to include + DNS_SDLZFLAG_THREADSAFE. (Other capability flags may be added in + the future.) - isc_result_t dlz_create(const char *dlzname, unsigned int argc, char *argv[], diff --git a/contrib/dlz/example/dlz_example.c b/contrib/dlz/example/dlz_example.c index efd1f9c5..0ebc18e2 100644 --- a/contrib/dlz/example/dlz_example.c +++ b/contrib/dlz/example/dlz_example.c @@ -27,7 +27,7 @@ #include <stdarg.h> #include <stdint.h> -#include "dlz_minimal.h" +#include "../modules/dlz_minimal.h" #ifdef WIN32 #define STRTOK_R(a, b, c) strtok_s(a, b, c) diff --git a/contrib/dlz/modules/bdbhpt/Makefile b/contrib/dlz/modules/bdbhpt/Makefile new file mode 100644 index 00000000..76f590a8 --- /dev/null +++ b/contrib/dlz/modules/bdbhpt/Makefile @@ -0,0 +1,18 @@ +prefix = /usr +libdir = $(prefix)/lib/bind9 + +CFLAGS=-fPIC -g +BDB_LIBS=-ldb + +all: dlz_bdbhpt_dynamic.so + +dlz_bdbhpt_dynamic.so: + $(CC) $(CFLAGS) -shared -o dlz_bdbhpt_dynamic.so \ + dlz_bdbhpt_dynamic.c $(BDB_LIBS) + +clean: + rm -f dlz_bdbhpt_dynamic.so + +install: dlz_bdbhpt_dynamic.so + mkdir -p $(DESTDIR)$(libdir) + install dlz_bdbhpt_dynamic.so $(DESTDIR)$(libdir) diff --git a/contrib/dlz/modules/bdbhpt/README.md b/contrib/dlz/modules/bdbhpt/README.md new file mode 100644 index 00000000..10dd81e2 --- /dev/null +++ b/contrib/dlz/modules/bdbhpt/README.md @@ -0,0 +1,89 @@ +dlz-bdbhpt-dynamic +================== + +A Bind 9 Dynamically Loadable BerkeleyDB High Performance Text Driver + +Summary +------- + +This is an attempt to port the original Bind 9 DLZ bdbhpt_driver.c as +found in the Bind 9 source tree into the new DLZ dlopen driver API. +The goals of this project are as follows: + +* Provide DLZ facilities to OEM-supported Bind distributions +* Support both v1 (Bind 9.8) and v2 (Bind 9.9) of the dlopen() DLZ API + +Requirements +------------ + +You will need the following: + * Bind 9.8 or higher with the DLZ dlopen driver enabled + * BerkeleyDB libraries and header files + * A C compiler + +This distribution have been successfully installed and tested on +Ubuntu 12.04. + +Installation +------------ + +With the above requirements satisfied perform the following steps: + +1. Ensure the symlink for dlz_minimal.h points at the correct header + file matching your Bind version +2. Run: make +3. Run: sudo make install # this will install dlz_bdbhpt_dynamic.so + into /usr/lib/bind9/ +4. Add a DLZ statement similar to the example shown in + example/dlz.conf into your Bind configuration +5. Ensure your BerkeleyDB home-directory exists and can be written to + by the bind user +6. If you're running an AppArmor enabled Bind, consider adding content + included within example/apparmor.d-local-usr.sbin.named within + /etc/apparmor.d/local/usr.sbin.named +7. Use the included testing/bdbhpt-populate.pl script to provide some + data for initial testing + +Usage +----- + +Example usage is as follows: + +``` +dlz "bdbhpt_dynamic" { + database "dlopen /usr/lib/bind9/dlz_bdbhpt_dynamic.so T /var/cache/bind/dlz dnsdata.db"; +}; +``` + +The arguments for the "database" line above are as follows: + +1. dlopen - Use the dlopen DLZ driver to dynamically load our compiled + driver +2. The full path to your built dlz_bdbhpt_dynamic.so +3. Single character specifying the mode to open your BerkeleyDB + environment: + * T - Transactional Mode - Highest safety, lowest speed. + * C - Concurrent Mode - Lower safety (no rollback), higher speed. + * P - Private Mode - No interprocess communication & no locking. + Lowest safety, highest speed. +4. Directory containing your BerkeleyDB - this is where the BerkeleyDB + environment will be created. +5. Filename within this directory containing your BerkeleyDB tables. + +A copy of the above Bind configuration is included within +example/dlz.conf. + +Author +------ + +The person responsible for this is: + + Mark Goldfinch <g@g.org.nz> + +The code is maintained at: + + https://github.com/goldie80/dlz-bdbhpt-dynamic + +There is very little in the way of original code in this work, +however, original license conditions from both bdbhpt_driver.c and +dlz_example.c are maintained in the dlz_bdbhpt_dynamic.c. diff --git a/contrib/dlz/modules/bdbhpt/dlz_bdbhpt_dynamic.c b/contrib/dlz/modules/bdbhpt/dlz_bdbhpt_dynamic.c new file mode 100644 index 00000000..ecfa1018 --- /dev/null +++ b/contrib/dlz/modules/bdbhpt/dlz_bdbhpt_dynamic.c @@ -0,0 +1,836 @@ +/* + * 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) 2011 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. + */ + +/* $Id$ */ + +/* + * This is simply a merge of Andrew Tridgell's dlz_example.c and the + * original bdb_bdbhpt_driver.c + * + * This provides the externally loadable bdbhpt DLZ driver, without + * update support + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <stdint.h> + +#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 + +/* should the bdb driver use threads. */ +#ifdef ISC_PLATFORM_USETHREADS +#define bdbhpt_threads DB_THREAD +#else +#define bdbhpt_threads 0 +#endif + +/* bdbhpt database names */ +#define dlz_data "dns_data" +#define dlz_zone "dns_zone" +#define dlz_xfr "dns_xfr" +#define dlz_client "dns_client" + +#define dlz_bdbhpt_dynamic_version "0.1" + +/* + * This structure contains all our DB handles and helper functions we + * inherit from the dlz_dlopen driver + * + */ +typedef struct bdbhpt_instance { + DB_ENV *dbenv; /* bdbhpt environment */ + DB *data; /* dns_data database handle */ + DB *zone; /* zone database handle */ + DB *xfr; /* zone xfr database handle */ + DB *client; /* client database handle */ + + /* 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; +} bdbhpt_parsed_data_t; + +/* + * Reverses a string in place. + */ +static char +*bdbhpt_strrev(char *str) { + char *p1, *p2; + + if (! str || ! *str) + return str; + for (p1 = str, p2 = str + strlen(str) - 1; p2 > p1; ++p1, --p2) { + *p1 ^= *p2; + *p2 ^= *p1; + *p1 ^= *p2; + } + return str; +} + +/* + * Parses the DBT from the Berkeley DB into a parsed_data record + * The parsed_data record should be allocated before and passed into the + * bdbhpt_parse_data function. The char (type & data) fields should not + * be "free"d as that memory is part of the DBT data field. It will be + * "free"d when the DBT is freed. + */ +static isc_result_t +bdbhpt_parse_data(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; +} + +/* + * Return the version of the API + */ +int +dlz_version(unsigned int *flags) { + UNUSED(flags); + return (DLZ_DLOPEN_VERSION); +} + +/* + * Remember a helper function from the bind9 dlz_dlopen driver + */ +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; +} + +/* + * 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); +} + + +/* + * See if we handle a given zone + */ +#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; + 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); +#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; +} + +/* + * Look up one record in the database. + * + */ +#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; + 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); +#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 */ + + 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; +} + + +/* + * See if a zone transfer is allowed + */ +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; +} + +/* + * 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). + */ +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; +} diff --git a/contrib/dlz/modules/bdbhpt/testing/bdbhpt-populate.pl b/contrib/dlz/modules/bdbhpt/testing/bdbhpt-populate.pl new file mode 100755 index 00000000..909976a2 --- /dev/null +++ b/contrib/dlz/modules/bdbhpt/testing/bdbhpt-populate.pl @@ -0,0 +1,232 @@ +#!/usr/bin/perl -w +use strict; +use BerkeleyDB; +use Getopt::Long; + +my $opt = {}; +if (!GetOptions($opt, qw/bdb|b:s input|i:s zones|z:s help|h/)) { + usage('GetOptions processing failed.'); + exit 1; +} + +if ($opt->{help}) { + usage(); + exit 0; +} + +my $db_file = $opt->{bdb}; +if (!defined $db_file || $db_file eq '') { + usage('Please specify an output BerkeleyDB filename.'); + exit 1; +} + +my $input_file = $opt->{input}; +if (!defined $input_file || $input_file eq '') { + usage('Please specify an input records file.'); + exit 1; +} + +my $zone_list = $opt->{zones}; +if (!defined $zone_list || $zone_list eq '') { + usage('Please specify a space seperated list of zones'); + exit 1; +} + +my $records = []; +my $unique_names = []; +populate_records(records=>$records, input_file=>$input_file, unique_names=>$unique_names); + +my $flags = DB_CREATE; + +my $dns_data = new BerkeleyDB::Hash + -Filename => $db_file, + -Flags => $flags, + -Property => DB_DUP | DB_DUPSORT, + -Subname => "dns_data" + || die "Cannot create dns_data: $BerkeleyDB::Error"; + +my $replId = 0; +my @zones = split(/\s+/, $zone_list); +foreach my $zone (@zones) { + foreach my $r (@$records) { + my $name = $r->{name}; + my $ttl = $r->{ttl}; + my $type = $r->{type}; + my $data = $r->{data}; + + $data =~ s/\%zone\%/$zone/g; + $data =~ s/\%driver\%/bdbhpt-dynamic/g; + + my $row_name = "$zone $name"; + my $row_value = "$replId $name $ttl $type $data"; + if ($dns_data->db_put($row_name, $row_value) != 0) { + die "Cannot add record '$row_name' -> '$row_value' to dns_data: $BerkeleyDB::Error"; + } + $replId++; + } +} + +$dns_data->db_close(); + +my $dns_xfr = new BerkeleyDB::Hash + -Filename => $db_file, + -Flags => $flags, + -Property => DB_DUP | DB_DUPSORT, + -Subname => "dns_xfr" + or die "Cannot create dns_xfr: $BerkeleyDB::Error"; + +foreach my $zone (@zones) { + foreach my $name (@$unique_names) { + if ($dns_xfr->db_put($zone, $name) != 0) { + die "Cannot add record '$zone' -> '$name' to dns_xfr: $BerkeleyDB::Error"; + } + } +} + +$dns_xfr->db_close(); + +my $dns_client = new BerkeleyDB::Hash + -Filename => $db_file, + -Flags => $flags, + -Property => DB_DUP | DB_DUPSORT, + -Subname => "dns_client" + or die "Cannot create dns_client: $BerkeleyDB::Error"; + +foreach my $zone (@zones) { + my $ip = '127.0.0.1'; + if ($dns_client->db_put($zone, $ip) != 0) { + die "Cannot add record '$zone' -> '$ip' to dns_client: $BerkeleyDB::Error"; + } +} + +$dns_client->db_close(); + +my $dns_zone = new BerkeleyDB::Btree + -Filename => $db_file, + -Flags => $flags, + -Property => 0, + -Subname => "dns_zone" + or die "Cannot create dns_zone: $BerkeleyDB::Error"; + +foreach my $zone (@zones) { + my $reversed_zone = reverse($zone); + if ($dns_zone->db_put($reversed_zone, "1") != 0) { + die "Cannot add record '$reversed_zone' -> '1' to dns_zone: $BerkeleyDB::Error"; + } +}; + +$dns_zone->db_close(); + +exit 0; + +sub usage { + my ($message) = @_; + if (defined $message && $message ne '') { + print STDERR $message . "\n\n"; + } + + print STDERR "usage: $0 --bdb=<bdb-file> --input=<input-file> --zones=<zone-list>\n\n"; + print STDERR "\tbdb-file: The output BerkeleyDB file you wish to create and use with bdbhpt-dynamic\n\n"; + print STDERR "\tinput-file: The input text-file containing records to populate within your zones\n\n"; + print STDERR "\tzone-list: The space-seperated list of zones you wish to create\n\n"; +} + +sub populate_records { + my (%args) = @_; + my $records = $args{records}; + my $input_file = $args{input_file}; + my $unique_names = $args{unique_names}; + + my %unique; + + open(RECORDS, $input_file) || die "unable to open $input_file: $!"; + while (<RECORDS>) { + chomp; + s/\#.*$//; + s/^\s+//; + if ($_ eq '') { + next; + } + my ($name, $ttl, $type, $data) = split(/\s+/, $_, 4); + my $record = { name=>$name, ttl=>$ttl, type=>$type, data=>$data }; + if (validate_record($record)) { + push @$records, $record; + $unique{$name} = 1; + } + } + close(RECORDS); + + foreach my $name (sort keys %unique) { + push @$unique_names, $name; + } +} + +# This could probably do more in-depth tests, but these tests are better than nothing! +sub validate_record { + my ($r) = @_; + + # http://en.wikipedia.org/wiki/List_of_DNS_record_types + my @TYPES = qw/A AAAA AFSDB APL CERT CNAME DHCID DLV DNAME DNSKEY DS HIP IPSECKEY KEY KX LOC MX NAPTR NS NSEC NSEC3 NSEC3PARAM PTR RRSIG RP SIG SOA SPF SRV SSHFP TA TKEY TLSA TSIG TXT/; + my $VALID_TYPE = {}; + foreach my $t (@TYPES) { + $VALID_TYPE->{$t} = 1; + } + + if (!defined $r->{name} || $r->{name} eq '') { + die "Record name must be set"; + } + + if (!defined $r->{ttl} || $r->{ttl} eq '') { + die "Record TTL must be set"; + } + + if ($r->{ttl} =~ /\D/ || $r->{ttl} < 0) { + die "Record TTL must be an integer 0 or greater"; + } + + if (!defined $r->{type} || $r->{type} eq '') { + die "Record type must be set"; + } + + if (!$VALID_TYPE->{$r->{type}}) { + die "Unsupported record type: $r->{type}"; + } + + # Lets do some data validation for the records which will cause bind to crash if they're wrong + if ($r->{type} eq 'SOA') { + my $soa_error = "SOA records must take the form: 'server email refresh retry expire negative_cache_ttl'"; + my ($server, $email, $version, $refresh, $retry, $expire, $negative_cache_ttl) = split(/\s+/, $r->{data}); + if (!defined $server || $server eq '') { + die "$soa_error, missing server"; + } + if (!defined $email || $email eq '') { + die "$soa_error, missing email"; + } + if (!defined $refresh || $refresh eq '') { + die "$soa_error, missing refresh"; + } + if ($refresh =~ /\D/ || $refresh <= 0) { + die "$soa_error, refresh must be an integer greater than 0"; + } + if (!defined $retry || $retry eq '') { + die "$soa_error, missing retry"; + } + if ($retry =~ /\D/ || $retry <= 0) { + die "$soa_error, retry must be an integer greater than 0"; + } + if (!defined $expire || $expire eq '') { + die "$soa_error, missing expire"; + } + if ($expire =~ /\D/ || $expire <= 0) { + die "$soa_error, expire must be an integer greater than 0"; + } + if (!defined $negative_cache_ttl || $negative_cache_ttl eq '') { + die "$soa_error, missing negative cache ttl"; + } + if ($negative_cache_ttl =~ /\D/ || $negative_cache_ttl <= 0) { + die "$soa_error, negative cache ttl must be an integer greater than 0"; + } + } + + return 1; +} diff --git a/contrib/dlz/modules/bdbhpt/testing/dns-data.txt b/contrib/dlz/modules/bdbhpt/testing/dns-data.txt new file mode 100644 index 00000000..7ad5be90 --- /dev/null +++ b/contrib/dlz/modules/bdbhpt/testing/dns-data.txt @@ -0,0 +1,19 @@ +# Name TTL Type Data +@ 3600 SOA ns1.%zone%. root.%zone%. 2012071700 604800 86400 2419200 10800 +@ 3600 NS ns1.%zone% +@ 3600 MX 5 mx1.%zone% +@ 3600 MX 10 mx2.%zone% +@ 3600 TXT This zone brought to you by %driver%! +jabber 3600 A 127.0.0.1 +mx1 3600 A 127.0.0.2 +mx2 3600 A 127.0.0.3 +jabber 3600 A 127.0.0.4 +ns1 3600 A 127.0.0.5 +ns1 3600 AAAA ::1 +voip 3600 A 127.0.0.6 +www 3600 CNAME www1.%zone% +www1 3600 A 127.0.0.7 +_sip._udp 3600 SRV 5 0 5060 voip.%zone% +_jabber._tcp 3600 SRV 5 0 5269 jabber.%zone% +_xmpp-client._tcp 3600 SRV 5 0 5222 jabber.%zone% +_xmpp-server._tcp 3600 SRV 5 0 5269 jabber.%zone% diff --git a/contrib/dlz/example/dlz_minimal.h b/contrib/dlz/modules/dlz_minimal.h index b0f41e77..44fb37d4 100644 --- a/contrib/dlz/example/dlz_minimal.h +++ b/contrib/dlz/modules/dlz_minimal.h @@ -36,7 +36,12 @@ typedef unsigned int isc_result_t; typedef int isc_boolean_t; typedef uint32_t dns_ttl_t; +/* + * Define DLZ_DLOPEN_VERSION to different values to use older versions + * of the interface + */ #define DLZ_DLOPEN_VERSION 2 +#define DLZ_DLOPEN_AGE 0 /* return this in flags to dlz_version() if thread safe */ #define DNS_SDLZFLAG_THREADSAFE 0x00000001U @@ -70,12 +75,13 @@ typedef void *dns_sdlzlookup_t; typedef void *dns_sdlzallnodes_t; typedef void *dns_view_t; +#if DLZ_DLOPEN_VERSION > 1 /* * Method and type definitions needed for retrieval of client info * from the caller. */ typedef struct isc_sockaddr { - union { + union { struct sockaddr sa; struct sockaddr_in sin; struct sockaddr_in6 sin6; @@ -84,7 +90,7 @@ typedef struct isc_sockaddr { #endif } type; unsigned int length; - void * link; + void * link; } isc_sockaddr_t; #define DNS_CLIENTINFO_VERSION 1 @@ -104,6 +110,7 @@ typedef struct dns_clientinfomethods { uint16_t age; dns_clientinfo_sourceip_t sourceip; } dns_clientinfomethods_t; +#endif /* DLZ_DLOPEN_VERSION > 1 */ /* * Method definitions for callbacks provided by the dlopen driver @@ -129,7 +136,6 @@ typedef isc_result_t dns_dlz_writeablezone_t(dns_view_t *view, * prototypes for the functions you can include in your module */ - /* * dlz_version() is required for all DLZ external drivers. It should * return DLZ_DLOPEN_VERSION. 'flags' is updated to indicate capabilities @@ -145,7 +151,7 @@ dlz_version(unsigned int *flags); */ isc_result_t dlz_create(const char *dlzname, unsigned int argc, char *argv[], - void **dbdata, ...); + void **dbdata, ...); /* * dlz_destroy() is optional, and will be called when the driver is @@ -163,11 +169,17 @@ dlz_findzonedb(void *dbdata, const char *name); /* * dlz_lookup is required for all DLZ external drivers */ +#if DLZ_DLOPEN_VERSION == 1 +isc_result_t +dlz_lookup(const char *zone, const char *name, void *dbdata, + dns_sdlzlookup_t *lookup); +#else /* DLZ_DLOPEN_VERSION > 1 */ 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 /* DLZ_DLOPEN_VERSION */ /* * dlz_allowzonexfr() is optional, and should be supplied if you want to @@ -176,7 +188,6 @@ dlz_lookup(const char *zone, const char *name, void *dbdata, isc_result_t dlz_allowzonexfr(void *dbdata, const char *name, const char *client); - /* * dlz_allnodes() is optional, but must be supplied if supply a * dlz_allowzonexfr() function @@ -197,7 +208,7 @@ dlz_newversion(const char *zone, void *dbdata, void **versionp); */ void dlz_closeversion(const char *zone, isc_boolean_t commit, void *dbdata, - void **versionp); + void **versionp); /* * dlz_configure() is optional, but must be supplied if you want to support @@ -210,11 +221,10 @@ dlz_configure(dns_view_t *view, void *dbdata); * dlz_ssumatch() is optional, but must be supplied if you want to support * dynamic updates */ - isc_boolean_t dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr, - const char *type, const char *key, uint32_t keydatalen, - uint8_t *keydata, void *dbdata); + const char *type, const char *key, uint32_t keydatalen, + uint8_t *keydata, void *dbdata); /* * dlz_addrdataset() is optional, but must be supplied if you want to @@ -222,14 +232,15 @@ dlz_ssumatch(const char *signer, const char *name, const char *tcpaddr, */ isc_result_t dlz_addrdataset(const char *name, const char *rdatastr, void *dbdata, - void *version); + void *version); -/* dlz_subrdataset() is optional, but must be supplied if you want to +/* + * dlz_subrdataset() is optional, but must be supplied if you want to * support dynamic updates */ isc_result_t dlz_subrdataset(const char *name, const char *rdatastr, void *dbdata, - void *version); + void *version); /* * dlz_delrdataset() is optional, but must be supplied if you want to @@ -237,4 +248,4 @@ dlz_subrdataset(const char *name, const char *rdatastr, void *dbdata, */ isc_result_t dlz_delrdataset(const char *name, const char *type, void *dbdata, - void *version); + void *version); |