/*
* 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.
*/
/*
* 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"
/* 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;
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;
}
/*%
* 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;
}
/*
* 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;
}
/*%
* 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);
}
/*
* 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;
}
/*%
* 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);
}
/*
* 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 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;
}