diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 07:24:22 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 07:24:22 +0000 |
commit | 45d6379135504814ab723b57f0eb8be23393a51d (patch) | |
tree | d4f2ec4acca824a8446387a758b0ce4238a4dffa /contrib/dlz/drivers/dlz_bdb_driver.c | |
parent | Initial commit. (diff) | |
download | bind9-upstream/1%9.16.44.tar.xz bind9-upstream/1%9.16.44.zip |
Adding upstream version 1:9.16.44.upstream/1%9.16.44upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'contrib/dlz/drivers/dlz_bdb_driver.c')
-rw-r--r-- | contrib/dlz/drivers/dlz_bdb_driver.c | 780 |
1 files changed, 780 insertions, 0 deletions
diff --git a/contrib/dlz/drivers/dlz_bdb_driver.c b/contrib/dlz/drivers/dlz_bdb_driver.c new file mode 100644 index 0000000..5cce7ce --- /dev/null +++ b/contrib/dlz/drivers/dlz_bdb_driver.c @@ -0,0 +1,780 @@ +/* + * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl. + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* + * Copyright (C) 1999-2001, 2016 Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifdef DLZ_BDB +#include <db.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <isc/mem.h> +#include <isc/print.h> +#include <isc/result.h> +#include <isc/util.h> + +#include <dns/log.h> +#include <dns/result.h> +#include <dns/sdlz.h> + +#include <dlz/dlz_bdb_driver.h> +#include <named/globals.h> + +static dns_sdlzimplementation_t *dlz_bdb = NULL; + +/* should the bdb driver use threads. */ +#define bdb_threads DB_THREAD + +/* BDB database names */ +#define dlz_data "dns_data" +#define dlz_zone "dns_zone" +#define dlz_host "dns_host" +#define dlz_client "dns_client" + +/*% + * This structure contains all the Berkeley DB handles + * for this instance of the BDB driver. + */ + +typedef struct bdb_instance { + DB_ENV *dbenv; /*%< BDB environment */ + DB *data; /*%< dns_data database handle */ + DB *zone; /*%< zone database handle */ + DB *host; /*%< host database handle */ + DB *client; /*%< client database handle */ + isc_mem_t *mctx; /*%< memory context */ +} bdb_instance_t; + +typedef struct parsed_data { + char *zone; + char *host; + char *type; + int ttl; + char *data; +} parsed_data_t; + +/* forward reference */ + +static isc_result_t +bdb_findzone(void *driverarg, void *dbdata, const char *name, + dns_clientinfomethods_t *methods, dns_clientinfo_t *clientinfo); + +/*% + * Parses the DBT from the Berkeley DB into a parsed_data record + * The parsed_data record should be allocated before and passed into the + * bdb_parse_data function. The char (type & data) fields should not + * be "free"d as that memory is part of the DBT data field. It will be + * "free"d when the DBT is freed. + */ + +static isc_result_t +bdb_parse_data(char *in, parsed_data_t *pd) { + char *endp, *ttlStr; + char *tmp = in; + char *lastchar = (char *)&tmp[strlen(tmp) + 1]; + + /*% + * String should be formatted as: + * zone(a space)host(a space)ttl(a space)type(a space)remaining data + * examples: + * example.com www 10 A 127.0.0.1 + * example.com mail 10 A 127.0.0.2 + * example.com @ 10 MX 20 mail.example.com + */ + + /* save pointer to zone */ + pd->zone = tmp; + + /* find space after zone and change it to a '\0' */ + tmp = strchr(tmp, ' '); + /* verify we found a space */ + if (tmp == NULL) { + return (ISC_R_FAILURE); + } + /* change the space to a null (string terminator) */ + tmp[0] = '\0'; + /* make sure it is safe to increment pointer */ + if (++tmp > lastchar) { + return (ISC_R_FAILURE); + } + + /* save pointer to host */ + pd->host = tmp; + + /* find space after type and change it to a '\0' */ + tmp = strchr(tmp, ' '); + /* verify we found a space */ + if (tmp == NULL) { + return (ISC_R_FAILURE); + } + /* change the space to a null (string terminator) */ + tmp[0] = '\0'; + /* make sure it is safe to increment pointer */ + if (++tmp > lastchar) { + return (ISC_R_FAILURE); + } + + /* save pointer to dns type */ + pd->type = tmp; + + /* find space after type and change it to a '\0' */ + tmp = strchr(tmp, ' '); + /* verify we found a space */ + if (tmp == NULL) { + return (ISC_R_FAILURE); + } + /* change the space to a null (string terminator) */ + tmp[0] = '\0'; + /* make sure it is safe to increment pointer */ + if (++tmp > lastchar) { + return (ISC_R_FAILURE); + } + + /* save pointer to dns ttl */ + ttlStr = tmp; + + /* find space after ttl and change it to a '\0' */ + tmp = strchr(tmp, ' '); + /* verify we found a space */ + if (tmp == NULL) { + return (ISC_R_FAILURE); + } + /* change the space to a null (string terminator) */ + tmp[0] = '\0'; + /* make sure it is safe to increment pointer */ + if (++tmp > lastchar) { + return (ISC_R_FAILURE); + } + + /* save pointer to remainder of DNS data */ + pd->data = tmp; + + /* convert ttl string to integer */ + pd->ttl = strtol(ttlStr, &endp, 10); + if (*endp != '\0' || pd->ttl < 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "BDB driver ttl must be a positive number"); + return (ISC_R_FAILURE); + } + + /* if we get this far everything should have worked. */ + return (ISC_R_SUCCESS); +} + +/* + * DLZ methods + */ + +static isc_result_t +bdb_allowzonexfr(void *driverarg, void *dbdata, const char *name, + const char *client) { + isc_result_t result; + bdb_instance_t *db = (bdb_instance_t *)dbdata; + DBC *client_cursor = NULL; + DBT key, data; + + /* check to see if we are authoritative for the zone first. */ + result = bdb_findzone(driverarg, dbdata, name, NULL, NULL); + if (result != ISC_R_SUCCESS) { + return (ISC_R_NOTFOUND); + } + + memset(&key, 0, sizeof(DBT)); + key.flags = DB_DBT_MALLOC; + key.data = strdup(name); + if (key.data == NULL) { + result = ISC_R_NOMEMORY; + goto xfr_cleanup; + } + key.size = strlen(key.data); + + memset(&data, 0, sizeof(DBT)); + data.flags = DB_DBT_MALLOC; + data.data = strdup(client); + if (data.data == NULL) { + result = ISC_R_NOMEMORY; + goto xfr_cleanup; + } + data.size = strlen(data.data); + + /* get a cursor to loop through zone data */ + if (db->client->cursor(db->client, NULL, &client_cursor, 0) != 0) { + result = ISC_R_FAILURE; + goto xfr_cleanup; + } + + switch (client_cursor->c_get(client_cursor, &key, &data, DB_GET_BOTH)) { + case DB_NOTFOUND: + case DB_SECONDARY_BAD: + result = ISC_R_NOTFOUND; + break; + case 0: + result = ISC_R_SUCCESS; + break; + default: + result = ISC_R_FAILURE; + } + +xfr_cleanup: + + /* free any memory duplicate string in the key field */ + if (key.data != NULL) { + free(key.data); + } + + /* free any memory allocated to the data field. */ + if (data.data != NULL) { + free(data.data); + } + + /* get rid of zone_cursor */ + if (client_cursor != NULL) { + client_cursor->c_close(client_cursor); + } + + return (result); +} + +static isc_result_t +bdb_allnodes(const char *zone, void *driverarg, void *dbdata, + dns_sdlzallnodes_t *allnodes) { + isc_result_t result = ISC_R_NOTFOUND; + bdb_instance_t *db = (bdb_instance_t *)dbdata; + DBC *zone_cursor = NULL; + DBT key, data; + int flags; + int bdbres; + parsed_data_t pd; + char *tmp = NULL, *tmp_zone; + + UNUSED(driverarg); + + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + + key.data = tmp_zone = strdup(zone); + + if (key.data == NULL) { + return (ISC_R_NOMEMORY); + } + + key.size = strlen(key.data); + + /* get a cursor to loop through zone data */ + if (db->zone->cursor(db->zone, NULL, &zone_cursor, 0) != 0) { + result = ISC_R_FAILURE; + goto allnodes_cleanup; + } + + flags = DB_SET; + + while ((bdbres = zone_cursor->c_get(zone_cursor, &key, &data, flags)) == + 0) + { + flags = DB_NEXT_DUP; + + tmp = realloc(tmp, data.size + 1); + if (tmp == NULL) { + goto allnodes_cleanup; + } + + strncpy(tmp, data.data, data.size); + tmp[data.size] = '\0'; + + if (bdb_parse_data(tmp, &pd) != ISC_R_SUCCESS) { + goto allnodes_cleanup; + } + + result = dns_sdlz_putnamedrr(allnodes, pd.host, pd.type, pd.ttl, + pd.data); + if (result != ISC_R_SUCCESS) { + goto allnodes_cleanup; + } + } /* end while loop */ + +allnodes_cleanup: + + if (tmp != NULL) { + free(tmp); + } + + /* free any memory duplicate string in the key field */ + if (tmp_zone != NULL) { + free(tmp_zone); + } + + /* get rid of zone_cursor */ + if (zone_cursor != NULL) { + zone_cursor->c_close(zone_cursor); + } + + return (result); +} + +/*% + * Performs BDB cleanup. + * Used by bdb_create if there is an error starting up. + * Used by bdb_destroy when the driver is shutting down. + */ + +static void +bdb_cleanup(bdb_instance_t *db) { + isc_mem_t *mctx; + + /* close databases */ + if (db->data != NULL) { + db->data->close(db->data, 0); + } + if (db->host != NULL) { + db->host->close(db->host, 0); + } + if (db->zone != NULL) { + db->zone->close(db->zone, 0); + } + if (db->client != NULL) { + db->client->close(db->client, 0); + } + + /* close environment */ + if (db->dbenv != NULL) { + db->dbenv->close(db->dbenv, 0); + } + + /* cleanup memory */ + if (db->mctx != NULL) { + /* save mctx for later */ + mctx = db->mctx; + /* return, and detach the memory */ + isc_mem_putanddetach(&mctx, db, sizeof(bdb_instance_t)); + } +} + +static isc_result_t +bdb_findzone(void *driverarg, void *dbdata, const char *name, + dns_clientinfomethods_t *methods, dns_clientinfo_t *clientinfo) { + isc_result_t result; + bdb_instance_t *db = (bdb_instance_t *)dbdata; + DBC *zone_cursor = NULL; + DBT key, data; + + UNUSED(driverarg); + UNUSED(methods); + UNUSED(clientinfo); + + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + data.flags = DB_DBT_MALLOC; + + key.data = strdup(name); + + if (key.data == NULL) { + return (ISC_R_NOMEMORY); + } + + key.size = strlen(key.data); + + /* get a cursor to loop through zone data */ + if (db->zone->cursor(db->zone, NULL, &zone_cursor, 0) != 0) { + result = ISC_R_NOTFOUND; + goto findzone_cleanup; + } + + switch (zone_cursor->c_get(zone_cursor, &key, &data, DB_SET)) { + case DB_NOTFOUND: + case DB_SECONDARY_BAD: + result = ISC_R_NOTFOUND; + break; + case 0: + result = ISC_R_SUCCESS; + break; + default: + result = ISC_R_FAILURE; + } + +findzone_cleanup: + + /* free any memory duplicate string in the key field */ + if (key.data != NULL) { + free(key.data); + } + + /* free any memory allocated to the data field. */ + if (data.data != NULL) { + free(data.data); + } + + /* get rid of zone_cursor */ + if (zone_cursor != NULL) { + zone_cursor->c_close(zone_cursor); + } + + return (result); +} + +static isc_result_t +bdb_lookup(const char *zone, const char *name, void *driverarg, void *dbdata, + dns_sdlzlookup_t *lookup, dns_clientinfomethods_t *methods, + dns_clientinfo_t *clientinfo) { + isc_result_t result = ISC_R_NOTFOUND; + bdb_instance_t *db = (bdb_instance_t *)dbdata; + DBC *zone_cursor = NULL; + DBC *host_cursor = NULL; + DBC *join_cursor = NULL; + DBT key, data; + DBC *cur_arr[3]; + int bdbres; + parsed_data_t pd; + char *tmp_zone, *tmp_host = NULL; + char *tmp = NULL; + + UNUSED(driverarg); + UNUSED(methods); + UNUSED(clientinfo); + + memset(&key, 0, sizeof(DBT)); + memset(&data, 0, sizeof(DBT)); + + /* set zone key */ + key.data = tmp_zone = strdup(zone); + if (key.data == NULL) { + result = ISC_R_NOMEMORY; + goto lookup_cleanup; + } + key.size = strlen(key.data); + + /* get a cursor to loop through zone data */ + if (db->zone->cursor(db->zone, NULL, &zone_cursor, 0) != 0) { + result = ISC_R_FAILURE; + goto lookup_cleanup; + } + + /* initialize zone_cursor with zone_key */ + if (zone_cursor->c_get(zone_cursor, &key, &data, DB_SET) != 0) { + result = ISC_R_NOTFOUND; + goto lookup_cleanup; + } + + /* set host key */ + key.data = tmp_host = strdup(name); + if (key.data == NULL) { + result = ISC_R_NOMEMORY; + goto lookup_cleanup; + } + key.size = strlen(key.data); + + /* get a cursor to loop through host data */ + if (db->host->cursor(db->host, NULL, &host_cursor, 0) != 0) { + result = ISC_R_FAILURE; + goto lookup_cleanup; + } + + /* initialize host_cursor with host_key */ + if (host_cursor->c_get(host_cursor, &key, &data, DB_SET) != 0) { + result = ISC_R_NOTFOUND; + goto lookup_cleanup; + } + + cur_arr[0] = zone_cursor; + cur_arr[1] = host_cursor; + cur_arr[2] = NULL; + + db->data->join(db->data, cur_arr, &join_cursor, 0); + + while ((bdbres = join_cursor->c_get(join_cursor, &key, &data, 0)) == 0) + { + tmp = realloc(tmp, data.size + 1); + if (tmp == NULL) { + goto lookup_cleanup; + } + + strncpy(tmp, data.data, data.size); + tmp[data.size] = '\0'; + + if (bdb_parse_data(tmp, &pd) != ISC_R_SUCCESS) { + goto lookup_cleanup; + } + + result = dns_sdlz_putrr(lookup, pd.type, pd.ttl, pd.data); + + if (result != ISC_R_SUCCESS) { + goto lookup_cleanup; + } + } /* end while loop */ + +lookup_cleanup: + + if (tmp != NULL) { + free(tmp); + } + if (tmp_zone != NULL) { + free(tmp_zone); + } + if (tmp_host != NULL) { + free(tmp_host); + } + + /* get rid of the joined cusor */ + if (join_cursor != NULL) { + join_cursor->c_close(join_cursor); + } + + /* get rid of zone_cursor */ + if (zone_cursor != NULL) { + zone_cursor->c_close(zone_cursor); + } + + /* get rid of host_cursor */ + if (host_cursor != NULL) { + host_cursor->c_close(host_cursor); + } + + return (result); +} + +/*% Initializes, sets flags and then opens Berkeley databases. */ + +static isc_result_t +bdb_opendb(DB_ENV *db_env, DBTYPE db_type, DB **db, const char *db_name, + char *db_file, int flags) { + int result; + + /* Initialize the database. */ + if ((result = db_create(db, db_env, 0)) != 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "BDB could not initialize %s database. " + "BDB error: %s", + db_name, db_strerror(result)); + return (ISC_R_FAILURE); + } + + /* set database flags. */ + if ((result = (*db)->set_flags(*db, flags)) != 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "BDB could not set flags for %s database. " + "BDB error: %s", + db_name, db_strerror(result)); + return (ISC_R_FAILURE); + } + + /* open the database. */ + if ((result = (*db)->open(*db, NULL, db_file, db_name, db_type, + DB_RDONLY | bdb_threads, 0)) != 0) + { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "BDB could not open %s database in %s. " + "BDB error: %s", + db_name, db_file, db_strerror(result)); + return (ISC_R_FAILURE); + } + + return (ISC_R_SUCCESS); +} + +static isc_result_t +bdb_create(const char *dlzname, unsigned int argc, char *argv[], + void *driverarg, void **dbdata) { + isc_result_t result; + int bdbres; + bdb_instance_t *db = NULL; + + UNUSED(dlzname); + UNUSED(driverarg); + + /* verify we have 3 arg's passed to the driver */ + if (argc != 3) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "Berkeley DB driver requires at least " + "2 command line args."); + return (ISC_R_FAILURE); + } + + /* allocate and zero memory for driver structure */ + db = isc_mem_get(named_g_mctx, sizeof(bdb_instance_t)); + memset(db, 0, sizeof(bdb_instance_t)); + + /* attach to the memory context */ + isc_mem_attach(named_g_mctx, &db->mctx); + + /* create BDB environment + * Basically BDB allocates and assigns memory to db->dbenv + */ + bdbres = db_env_create(&db->dbenv, 0); + if (bdbres != 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "BDB environment could not be created. " + "BDB error: %s", + db_strerror(bdbres)); + result = ISC_R_FAILURE; + goto init_cleanup; + } + + /* open BDB environment */ + bdbres = db->dbenv->open( + db->dbenv, argv[1], + DB_INIT_CDB | DB_INIT_MPOOL | bdb_threads | DB_CREATE, 0); + if (bdbres != 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "BDB environment at '%s' could not be opened. " + "BDB error: %s", + argv[1], db_strerror(bdbres)); + result = ISC_R_FAILURE; + goto init_cleanup; + } + + /* open dlz_data database. */ + result = bdb_opendb(db->dbenv, DB_UNKNOWN, &db->data, dlz_data, argv[2], + 0); + if (result != ISC_R_SUCCESS) { + goto init_cleanup; + } + + /* open dlz_host database. */ + result = bdb_opendb(db->dbenv, DB_UNKNOWN, &db->host, dlz_host, argv[2], + DB_DUP | DB_DUPSORT); + if (result != ISC_R_SUCCESS) { + goto init_cleanup; + } + + /* open dlz_zone database. */ + result = bdb_opendb(db->dbenv, DB_UNKNOWN, &db->zone, dlz_zone, argv[2], + DB_DUP | DB_DUPSORT); + if (result != ISC_R_SUCCESS) { + goto init_cleanup; + } + + /* open dlz_client database. */ + result = bdb_opendb(db->dbenv, DB_UNKNOWN, &db->client, dlz_client, + argv[2], DB_DUP | DB_DUPSORT); + if (result != ISC_R_SUCCESS) { + goto init_cleanup; + } + + /* associate the host secondary database with the primary database */ + bdbres = db->data->associate(db->data, NULL, db->host, NULL, 0); + if (bdbres != 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "BDB could not associate %s database with %s. " + "BDB error: %s", + dlz_host, dlz_data, db_strerror(bdbres)); + result = ISC_R_FAILURE; + goto init_cleanup; + } + + /* associate the zone secondary database with the primary database */ + bdbres = db->data->associate(db->data, NULL, db->zone, NULL, 0); + if (bdbres != 0) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "BDB could not associate %s database with %s. " + "BDB error: %s", + dlz_zone, dlz_data, db_strerror(bdbres)); + result = ISC_R_FAILURE; + goto init_cleanup; + } + + *dbdata = db; + + return (ISC_R_SUCCESS); + +init_cleanup: + + bdb_cleanup(db); + return (result); +} + +static void +bdb_destroy(void *driverarg, void *dbdata) { + UNUSED(driverarg); + + bdb_cleanup((bdb_instance_t *)dbdata); +} + +/* bdb_authority not needed as authority data is returned by lookup */ +static dns_sdlzmethods_t dlz_bdb_methods = { + bdb_create, + bdb_destroy, + bdb_findzone, + bdb_lookup, + NULL, + bdb_allnodes, + bdb_allowzonexfr, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, +}; + +/*% + * Wrapper around dns_sdlzregister(). + */ +isc_result_t +dlz_bdb_init(void) { + isc_result_t result; + + /* + * Write debugging message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DLZ, + ISC_LOG_DEBUG(2), "Registering DLZ bdb driver."); + + result = dns_sdlzregister("bdb", &dlz_bdb_methods, NULL, + DNS_SDLZFLAG_RELATIVEOWNER | + DNS_SDLZFLAG_RELATIVERDATA | + DNS_SDLZFLAG_THREADSAFE, + named_g_mctx, &dlz_bdb); + if (result != ISC_R_SUCCESS) { + UNEXPECTED_ERROR(__FILE__, __LINE__, + "dns_sdlzregister() failed: %s", + isc_result_totext(result)); + result = ISC_R_UNEXPECTED; + } + + return (result); +} + +/*% + * Wrapper around dns_sdlzunregister(). + */ +void +dlz_bdb_clear(void) { + /* + * Write debugging message to log + */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DLZ, + ISC_LOG_DEBUG(2), "Unregistering DLZ bdb driver."); + + if (dlz_bdb != NULL) { + dns_sdlzunregister(&dlz_bdb); + } +} + +#endif /* ifdef DLZ_BDB */ |