diff options
Diffstat (limited to 'lib/dns/dlz.c')
-rw-r--r-- | lib/dns/dlz.c | 541 |
1 files changed, 541 insertions, 0 deletions
diff --git a/lib/dns/dlz.c b/lib/dns/dlz.c new file mode 100644 index 0000000..6d77f5b --- /dev/null +++ b/lib/dns/dlz.c @@ -0,0 +1,541 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 AND 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 https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* + * 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. + */ + +/*! \file */ + +/*** + *** Imports + ***/ + +#include <stdbool.h> + +#include <isc/buffer.h> +#include <isc/commandline.h> +#include <isc/magic.h> +#include <isc/mem.h> +#include <isc/once.h> +#include <isc/rwlock.h> +#include <isc/string.h> +#include <isc/util.h> + +#include <dns/db.h> +#include <dns/dlz.h> +#include <dns/fixedname.h> +#include <dns/log.h> +#include <dns/master.h> +#include <dns/ssu.h> +#include <dns/zone.h> + +/*** + *** Supported DLZ DB Implementations Registry + ***/ + +static ISC_LIST(dns_dlzimplementation_t) dlz_implementations; +static isc_rwlock_t dlz_implock; +static isc_once_t once = ISC_ONCE_INIT; + +static void +dlz_initialize(void) { + isc_rwlock_init(&dlz_implock, 0, 0); + ISC_LIST_INIT(dlz_implementations); +} + +/*% + * Searches the dlz_implementations list for a driver matching name. + */ +static dns_dlzimplementation_t * +dlz_impfind(const char *name) { + dns_dlzimplementation_t *imp; + + for (imp = ISC_LIST_HEAD(dlz_implementations); imp != NULL; + imp = ISC_LIST_NEXT(imp, link)) + { + if (strcasecmp(name, imp->name) == 0) { + return (imp); + } + } + return (NULL); +} + +/*** + *** Basic DLZ Methods + ***/ + +isc_result_t +dns_dlzallowzonexfr(dns_view_t *view, const dns_name_t *name, + const isc_sockaddr_t *clientaddr, dns_db_t **dbp) { + isc_result_t result = ISC_R_NOTFOUND; + dns_dlzallowzonexfr_t allowzonexfr; + dns_dlzdb_t *dlzdb; + + /* + * Performs checks to make sure data is as we expect it to be. + */ + REQUIRE(name != NULL); + REQUIRE(dbp != NULL && *dbp == NULL); + + /* + * Find a driver in which the zone exists and transfer is supported + */ + for (dlzdb = ISC_LIST_HEAD(view->dlz_searched); dlzdb != NULL; + dlzdb = ISC_LIST_NEXT(dlzdb, link)) + { + REQUIRE(DNS_DLZ_VALID(dlzdb)); + + allowzonexfr = dlzdb->implementation->methods->allowzonexfr; + result = (*allowzonexfr)(dlzdb->implementation->driverarg, + dlzdb->dbdata, dlzdb->mctx, + view->rdclass, name, clientaddr, dbp); + + /* + * In these cases, we found the right database. Non-success + * result codes indicate the zone might not transfer. + */ + switch (result) { + case ISC_R_SUCCESS: + case ISC_R_NOPERM: + case ISC_R_DEFAULT: + return (result); + default: + break; + } + } + + if (result == ISC_R_NOTIMPLEMENTED) { + result = ISC_R_NOTFOUND; + } + + return (result); +} + +isc_result_t +dns_dlzcreate(isc_mem_t *mctx, const char *dlzname, const char *drivername, + unsigned int argc, char *argv[], dns_dlzdb_t **dbp) { + dns_dlzimplementation_t *impinfo; + isc_result_t result; + dns_dlzdb_t *db = NULL; + + /* + * initialize the dlz_implementations list, this is guaranteed + * to only really happen once. + */ + RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS); + + /* + * Performs checks to make sure data is as we expect it to be. + */ + REQUIRE(dbp != NULL && *dbp == NULL); + REQUIRE(dlzname != NULL); + REQUIRE(drivername != NULL); + REQUIRE(mctx != NULL); + + /* write log message */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DLZ, + ISC_LOG_INFO, "Loading '%s' using driver %s", dlzname, + drivername); + + /* lock the dlz_implementations list so we can search it. */ + RWLOCK(&dlz_implock, isc_rwlocktype_read); + + /* search for the driver implementation */ + impinfo = dlz_impfind(drivername); + if (impinfo == NULL) { + RWUNLOCK(&dlz_implock, isc_rwlocktype_read); + + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "unsupported DLZ database driver '%s'." + " %s not loaded.", + drivername, dlzname); + + return (ISC_R_NOTFOUND); + } + + /* Allocate memory to hold the DLZ database driver */ + db = isc_mem_get(mctx, sizeof(dns_dlzdb_t)); + + /* Make sure memory region is set to all 0's */ + memset(db, 0, sizeof(dns_dlzdb_t)); + + ISC_LINK_INIT(db, link); + db->implementation = impinfo; + if (dlzname != NULL) { + db->dlzname = isc_mem_strdup(mctx, dlzname); + } + + /* Create a new database using implementation 'drivername'. */ + result = ((impinfo->methods->create)(mctx, dlzname, argc, argv, + impinfo->driverarg, &db->dbdata)); + + /* mark the DLZ driver as valid */ + if (result == ISC_R_SUCCESS) { + RWUNLOCK(&dlz_implock, isc_rwlocktype_read); + db->magic = DNS_DLZ_MAGIC; + isc_mem_attach(mctx, &db->mctx); + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "DLZ driver loaded successfully."); + *dbp = db; + return (ISC_R_SUCCESS); + } else { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_ERROR, + "DLZ driver failed to load."); + } + + /* impinfo->methods->create failed. */ + RWUNLOCK(&dlz_implock, isc_rwlocktype_read); + isc_mem_free(mctx, db->dlzname); + isc_mem_put(mctx, db, sizeof(dns_dlzdb_t)); + return (result); +} + +void +dns_dlzdestroy(dns_dlzdb_t **dbp) { + dns_dlzdestroy_t destroy; + dns_dlzdb_t *db; + + /* Write debugging message to log */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DLZ, + ISC_LOG_DEBUG(2), "Unloading DLZ driver."); + + /* + * Perform checks to make sure data is as we expect it to be. + */ + REQUIRE(dbp != NULL && DNS_DLZ_VALID(*dbp)); + + db = *dbp; + *dbp = NULL; + + if (db->ssutable != NULL) { + dns_ssutable_detach(&db->ssutable); + } + + /* call the drivers destroy method */ + if (db->dlzname != NULL) { + isc_mem_free(db->mctx, db->dlzname); + } + destroy = db->implementation->methods->destroy; + (*destroy)(db->implementation->driverarg, db->dbdata); + /* return memory and detach */ + isc_mem_putanddetach(&db->mctx, db, sizeof(dns_dlzdb_t)); +} + +/*% + * Registers a DLZ driver. This basically just adds the dlz + * driver to the list of available drivers in the dlz_implementations list. + */ +isc_result_t +dns_dlzregister(const char *drivername, const dns_dlzmethods_t *methods, + void *driverarg, isc_mem_t *mctx, + dns_dlzimplementation_t **dlzimp) { + dns_dlzimplementation_t *dlz_imp; + + /* Write debugging message to log */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DLZ, + ISC_LOG_DEBUG(2), "Registering DLZ driver '%s'", + drivername); + + /* + * Performs checks to make sure data is as we expect it to be. + */ + REQUIRE(drivername != NULL); + REQUIRE(methods != NULL); + REQUIRE(methods->create != NULL); + REQUIRE(methods->destroy != NULL); + REQUIRE(methods->findzone != NULL); + REQUIRE(mctx != NULL); + REQUIRE(dlzimp != NULL && *dlzimp == NULL); + + /* + * initialize the dlz_implementations list, this is guaranteed + * to only really happen once. + */ + RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS); + + /* lock the dlz_implementations list so we can modify it. */ + RWLOCK(&dlz_implock, isc_rwlocktype_write); + + /* + * check that another already registered driver isn't using + * the same name + */ + dlz_imp = dlz_impfind(drivername); + if (dlz_imp != NULL) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_DEBUG(2), + "DLZ Driver '%s' already registered", drivername); + RWUNLOCK(&dlz_implock, isc_rwlocktype_write); + return (ISC_R_EXISTS); + } + + /* + * Allocate memory for a dlz_implementation object. Error if + * we cannot. + */ + dlz_imp = isc_mem_get(mctx, sizeof(dns_dlzimplementation_t)); + + /* Make sure memory region is set to all 0's */ + memset(dlz_imp, 0, sizeof(dns_dlzimplementation_t)); + + /* Store the data passed into this method */ + dlz_imp->name = drivername; + dlz_imp->methods = methods; + dlz_imp->mctx = NULL; + dlz_imp->driverarg = driverarg; + + /* attach the new dlz_implementation object to a memory context */ + isc_mem_attach(mctx, &dlz_imp->mctx); + + /* + * prepare the dlz_implementation object to be put in a list, + * and append it to the list + */ + ISC_LINK_INIT(dlz_imp, link); + ISC_LIST_APPEND(dlz_implementations, dlz_imp, link); + + /* Unlock the dlz_implementations list. */ + RWUNLOCK(&dlz_implock, isc_rwlocktype_write); + + /* Pass back the dlz_implementation that we created. */ + *dlzimp = dlz_imp; + + return (ISC_R_SUCCESS); +} + +/*% + * Tokenize the string "s" into whitespace-separated words, + * return the number of words in '*argcp' and an array + * of pointers to the words in '*argvp'. The caller + * must free the array using isc_mem_put(). The string + * is modified in-place. + */ +isc_result_t +dns_dlzstrtoargv(isc_mem_t *mctx, char *s, unsigned int *argcp, char ***argvp) { + return (isc_commandline_strtoargv(mctx, s, argcp, argvp, 0)); +} + +/*% + * Unregisters a DLZ driver. This basically just removes the dlz + * driver from the list of available drivers in the dlz_implementations list. + */ +void +dns_dlzunregister(dns_dlzimplementation_t **dlzimp) { + dns_dlzimplementation_t *dlz_imp; + + /* Write debugging message to log */ + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DLZ, + ISC_LOG_DEBUG(2), "Unregistering DLZ driver."); + + /* + * Performs checks to make sure data is as we expect it to be. + */ + REQUIRE(dlzimp != NULL && *dlzimp != NULL); + + /* + * initialize the dlz_implementations list, this is guaranteed + * to only really happen once. + */ + RUNTIME_CHECK(isc_once_do(&once, dlz_initialize) == ISC_R_SUCCESS); + + dlz_imp = *dlzimp; + + /* lock the dlz_implementations list so we can modify it. */ + RWLOCK(&dlz_implock, isc_rwlocktype_write); + + /* remove the dlz_implementation object from the list */ + ISC_LIST_UNLINK(dlz_implementations, dlz_imp, link); + + /* + * Return the memory back to the available memory pool and + * remove it from the memory context. + */ + isc_mem_putanddetach(&dlz_imp->mctx, dlz_imp, sizeof(*dlz_imp)); + + /* Unlock the dlz_implementations list. */ + RWUNLOCK(&dlz_implock, isc_rwlocktype_write); +} + +/* + * Create a writeable DLZ zone. This can be called by DLZ drivers + * during configure() to create a zone that can be updated. The zone + * type is set to dns_zone_dlz, which is equivalent to a master zone + * + * This function uses a callback setup in dns_dlzconfigure() to call + * into the server zone code to setup the remaining pieces of server + * specific functionality on the zone + */ +isc_result_t +dns_dlz_writeablezone(dns_view_t *view, dns_dlzdb_t *dlzdb, + const char *zone_name) { + dns_zone_t *zone = NULL; + dns_zone_t *dupzone = NULL; + isc_result_t result; + isc_buffer_t buffer; + dns_fixedname_t fixorigin; + dns_name_t *origin; + + REQUIRE(DNS_DLZ_VALID(dlzdb)); + + REQUIRE(dlzdb->configure_callback != NULL); + + isc_buffer_constinit(&buffer, zone_name, strlen(zone_name)); + isc_buffer_add(&buffer, strlen(zone_name)); + dns_fixedname_init(&fixorigin); + result = dns_name_fromtext(dns_fixedname_name(&fixorigin), &buffer, + dns_rootname, 0, NULL); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + origin = dns_fixedname_name(&fixorigin); + + if (!dlzdb->search) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_WARNING, + "DLZ %s has 'search no;', but attempted to " + "register writeable zone %s.", + dlzdb->dlzname, zone_name); + result = ISC_R_SUCCESS; + goto cleanup; + } + + /* See if the zone already exists */ + result = dns_view_findzone(view, origin, &dupzone); + if (result == ISC_R_SUCCESS) { + dns_zone_detach(&dupzone); + result = ISC_R_EXISTS; + goto cleanup; + } + INSIST(dupzone == NULL); + + /* Create it */ + result = dns_zone_create(&zone, view->mctx); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + result = dns_zone_setorigin(zone, origin); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + dns_zone_setview(zone, view); + + dns_zone_setadded(zone, true); + + if (dlzdb->ssutable == NULL) { + result = dns_ssutable_createdlz(dlzdb->mctx, &dlzdb->ssutable, + dlzdb); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + } + dns_zone_setssutable(zone, dlzdb->ssutable); + + result = dlzdb->configure_callback(view, dlzdb, zone); + if (result != ISC_R_SUCCESS) { + goto cleanup; + } + + result = dns_view_addzone(view, zone); + +cleanup: + if (zone != NULL) { + dns_zone_detach(&zone); + } + + return (result); +} + +/*% + * Configure a DLZ driver. This is optional, and if supplied gives + * the backend an opportunity to configure parameters related to DLZ. + */ +isc_result_t +dns_dlzconfigure(dns_view_t *view, dns_dlzdb_t *dlzdb, + dlzconfigure_callback_t callback) { + dns_dlzimplementation_t *impl; + isc_result_t result; + + REQUIRE(DNS_DLZ_VALID(dlzdb)); + REQUIRE(dlzdb->implementation != NULL); + + impl = dlzdb->implementation; + + if (impl->methods->configure == NULL) { + return (ISC_R_SUCCESS); + } + + dlzdb->configure_callback = callback; + + result = impl->methods->configure(impl->driverarg, dlzdb->dbdata, view, + dlzdb); + return (result); +} + +bool +dns_dlz_ssumatch(dns_dlzdb_t *dlzdatabase, const dns_name_t *signer, + const dns_name_t *name, const isc_netaddr_t *tcpaddr, + dns_rdatatype_t type, const dst_key_t *key) { + dns_dlzimplementation_t *impl; + bool r; + + REQUIRE(dlzdatabase != NULL); + REQUIRE(dlzdatabase->implementation != NULL); + REQUIRE(dlzdatabase->implementation->methods != NULL); + impl = dlzdatabase->implementation; + + if (impl->methods->ssumatch == NULL) { + isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, + DNS_LOGMODULE_DLZ, ISC_LOG_INFO, + "No ssumatch method for DLZ database"); + return (false); + } + + r = impl->methods->ssumatch(signer, name, tcpaddr, type, key, + impl->driverarg, dlzdatabase->dbdata); + return (r); +} |