summaryrefslogtreecommitdiffstats
path: root/lib/dns/dyndb.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--lib/dns/dyndb.c465
1 files changed, 465 insertions, 0 deletions
diff --git a/lib/dns/dyndb.c b/lib/dns/dyndb.c
new file mode 100644
index 0000000..1caf90b
--- /dev/null
+++ b/lib/dns/dyndb.c
@@ -0,0 +1,465 @@
+/*
+ * 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.
+ */
+
+#if HAVE_DLFCN_H
+#include <dlfcn.h>
+#elif _WIN32
+#include <windows.h>
+#endif /* if HAVE_DLFCN_H */
+
+#include <string.h>
+
+#include <isc/buffer.h>
+#include <isc/mem.h>
+#include <isc/mutex.h>
+#include <isc/once.h>
+#include <isc/region.h>
+#include <isc/result.h>
+#include <isc/task.h>
+#include <isc/types.h>
+#include <isc/util.h>
+
+#include <dns/dyndb.h>
+#include <dns/log.h>
+#include <dns/types.h>
+#include <dns/view.h>
+#include <dns/zone.h>
+
+#define CHECK(op) \
+ do { \
+ result = (op); \
+ if (result != ISC_R_SUCCESS) \
+ goto cleanup; \
+ } while (0)
+
+typedef struct dyndb_implementation dyndb_implementation_t;
+struct dyndb_implementation {
+ isc_mem_t *mctx;
+ void *handle;
+ dns_dyndb_register_t *register_func;
+ dns_dyndb_destroy_t *destroy_func;
+ char *name;
+ void *inst;
+ LINK(dyndb_implementation_t) link;
+};
+
+/*
+ * List of dyndb implementations. Locked by dyndb_lock.
+ *
+ * These are stored here so they can be cleaned up on shutdown.
+ * (The order in which they are stored is not important.)
+ */
+static LIST(dyndb_implementation_t) dyndb_implementations;
+
+/* Locks dyndb_implementations. */
+static isc_mutex_t dyndb_lock;
+static isc_once_t once = ISC_ONCE_INIT;
+
+static void
+dyndb_initialize(void) {
+ isc_mutex_init(&dyndb_lock);
+ INIT_LIST(dyndb_implementations);
+}
+
+static dyndb_implementation_t *
+impfind(const char *name) {
+ dyndb_implementation_t *imp;
+
+ for (imp = ISC_LIST_HEAD(dyndb_implementations); imp != NULL;
+ imp = ISC_LIST_NEXT(imp, link))
+ {
+ if (strcasecmp(name, imp->name) == 0) {
+ return (imp);
+ }
+ }
+ return (NULL);
+}
+
+#if HAVE_DLFCN_H && HAVE_DLOPEN
+static isc_result_t
+load_symbol(void *handle, const char *filename, const char *symbol_name,
+ void **symbolp) {
+ const char *errmsg;
+ void *symbol;
+
+ REQUIRE(handle != NULL);
+ REQUIRE(symbolp != NULL && *symbolp == NULL);
+
+ symbol = dlsym(handle, symbol_name);
+ if (symbol == NULL) {
+ errmsg = dlerror();
+ if (errmsg == NULL) {
+ errmsg = "returned function pointer is NULL";
+ }
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
+ "failed to lookup symbol %s in "
+ "dyndb module '%s': %s",
+ symbol_name, filename, errmsg);
+ return (ISC_R_FAILURE);
+ }
+ dlerror();
+
+ *symbolp = symbol;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+load_library(isc_mem_t *mctx, const char *filename, const char *instname,
+ dyndb_implementation_t **impp) {
+ isc_result_t result;
+ void *handle = NULL;
+ dyndb_implementation_t *imp = NULL;
+ dns_dyndb_register_t *register_func = NULL;
+ dns_dyndb_destroy_t *destroy_func = NULL;
+ dns_dyndb_version_t *version_func = NULL;
+ int version;
+
+ REQUIRE(impp != NULL && *impp == NULL);
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DYNDB,
+ ISC_LOG_INFO, "loading DynDB instance '%s' driver '%s'",
+ instname, filename);
+
+ handle = dlopen(filename, RTLD_NOW | RTLD_LOCAL);
+ if (handle == NULL) {
+ CHECK(ISC_R_FAILURE);
+ }
+
+ /* Clear dlerror */
+ dlerror();
+
+ CHECK(load_symbol(handle, filename, "dyndb_version",
+ (void **)&version_func));
+
+ version = version_func(NULL);
+ if (version < (DNS_DYNDB_VERSION - DNS_DYNDB_AGE) ||
+ version > DNS_DYNDB_VERSION)
+ {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
+ "driver API version mismatch: %d/%d", version,
+ DNS_DYNDB_VERSION);
+ CHECK(ISC_R_FAILURE);
+ }
+
+ CHECK(load_symbol(handle, filename, "dyndb_init",
+ (void **)&register_func));
+ CHECK(load_symbol(handle, filename, "dyndb_destroy",
+ (void **)&destroy_func));
+
+ imp = isc_mem_get(mctx, sizeof(dyndb_implementation_t));
+
+ imp->mctx = NULL;
+ isc_mem_attach(mctx, &imp->mctx);
+ imp->handle = handle;
+ imp->register_func = register_func;
+ imp->destroy_func = destroy_func;
+ imp->name = isc_mem_strdup(mctx, instname);
+
+ imp->inst = NULL;
+ INIT_LINK(imp, link);
+
+ *impp = imp;
+ imp = NULL;
+
+cleanup:
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
+ "failed to dynamically load instance '%s' "
+ "driver '%s': %s (%s)",
+ instname, filename, dlerror(),
+ isc_result_totext(result));
+ }
+ if (imp != NULL) {
+ isc_mem_putanddetach(&imp->mctx, imp,
+ sizeof(dyndb_implementation_t));
+ }
+ if (result != ISC_R_SUCCESS && handle != NULL) {
+ dlclose(handle);
+ }
+
+ return (result);
+}
+
+static void
+unload_library(dyndb_implementation_t **impp) {
+ dyndb_implementation_t *imp;
+
+ REQUIRE(impp != NULL && *impp != NULL);
+
+ imp = *impp;
+ *impp = NULL;
+
+ isc_mem_free(imp->mctx, imp->name);
+ isc_mem_putanddetach(&imp->mctx, imp, sizeof(dyndb_implementation_t));
+}
+#elif _WIN32
+static isc_result_t
+load_symbol(HMODULE handle, const char *filename, const char *symbol_name,
+ void **symbolp) {
+ void *symbol;
+
+ REQUIRE(handle != NULL);
+ REQUIRE(symbolp != NULL && *symbolp == NULL);
+
+ symbol = GetProcAddress(handle, symbol_name);
+ if (symbol == NULL) {
+ int errstatus = GetLastError();
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
+ "failed to lookup symbol %s in "
+ "dyndb module '%s': %d",
+ symbol_name, filename, errstatus);
+ return (ISC_R_FAILURE);
+ }
+
+ *symbolp = symbol;
+
+ return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+load_library(isc_mem_t *mctx, const char *filename, const char *instname,
+ dyndb_implementation_t **impp) {
+ isc_result_t result;
+ HMODULE handle;
+ dyndb_implementation_t *imp = NULL;
+ dns_dyndb_register_t *register_func = NULL;
+ dns_dyndb_destroy_t *destroy_func = NULL;
+ dns_dyndb_version_t *version_func = NULL;
+ int version;
+
+ REQUIRE(impp != NULL && *impp == NULL);
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DYNDB,
+ ISC_LOG_INFO, "loading DynDB instance '%s' driver '%s'",
+ instname, filename);
+
+ handle = LoadLibraryA(filename);
+ if (handle == NULL) {
+ CHECK(ISC_R_FAILURE);
+ }
+
+ CHECK(load_symbol(handle, filename, "dyndb_version",
+ (void **)&version_func));
+
+ version = version_func(NULL);
+ if (version < (DNS_DYNDB_VERSION - DNS_DYNDB_AGE) ||
+ version > DNS_DYNDB_VERSION)
+ {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
+ "driver API version mismatch: %d/%d", version,
+ DNS_DYNDB_VERSION);
+ CHECK(ISC_R_FAILURE);
+ }
+
+ CHECK(load_symbol(handle, filename, "dyndb_init",
+ (void **)&register_func));
+ CHECK(load_symbol(handle, filename, "dyndb_destroy",
+ (void **)&destroy_func));
+
+ imp = isc_mem_get(mctx, sizeof(dyndb_implementation_t));
+
+ imp->mctx = NULL;
+ isc_mem_attach(mctx, &imp->mctx);
+ imp->handle = handle;
+ imp->register_func = register_func;
+ imp->destroy_func = destroy_func;
+ imp->name = isc_mem_strdup(mctx, instname);
+
+ imp->inst = NULL;
+ INIT_LINK(imp, link);
+
+ *impp = imp;
+ imp = NULL;
+
+cleanup:
+ if (result != ISC_R_SUCCESS) {
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DYNDB, ISC_LOG_ERROR,
+ "failed to dynamically load instance '%s' "
+ "driver '%s': %d (%s)",
+ instname, filename, GetLastError(),
+ isc_result_totext(result));
+ }
+ if (imp != NULL) {
+ isc_mem_putanddetach(&imp->mctx, imp,
+ sizeof(dyndb_implementation_t));
+ }
+ if (result != ISC_R_SUCCESS && handle != NULL) {
+ FreeLibrary(handle);
+ }
+
+ return (result);
+}
+
+static void
+unload_library(dyndb_implementation_t **impp) {
+ dyndb_implementation_t *imp;
+
+ REQUIRE(impp != NULL && *impp != NULL);
+
+ imp = *impp;
+ *impp = NULL;
+
+ isc_mem_free(imp->mctx, imp->name);
+ isc_mem_putanddetach(&imp->mctx, imp, sizeof(dyndb_implementation_t));
+}
+#else /* HAVE_DLFCN_H || _WIN32 */
+static isc_result_t
+load_library(isc_mem_t *mctx, const char *filename, const char *instname,
+ dyndb_implementation_t **impp) {
+ UNUSED(mctx);
+ UNUSED(filename);
+ UNUSED(instname);
+ UNUSED(impp);
+
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE, DNS_LOGMODULE_DYNDB,
+ ISC_LOG_ERROR,
+ "dynamic database support is not implemented");
+
+ return (ISC_R_NOTIMPLEMENTED);
+}
+
+static void
+unload_library(dyndb_implementation_t **impp) {
+ UNUSED(impp);
+}
+#endif /* HAVE_DLFCN_H */
+
+isc_result_t
+dns_dyndb_load(const char *libname, const char *name, const char *parameters,
+ const char *file, unsigned long line, isc_mem_t *mctx,
+ const dns_dyndbctx_t *dctx) {
+ isc_result_t result;
+ dyndb_implementation_t *implementation = NULL;
+
+ REQUIRE(DNS_DYNDBCTX_VALID(dctx));
+ REQUIRE(name != NULL);
+
+ RUNTIME_CHECK(isc_once_do(&once, dyndb_initialize) == ISC_R_SUCCESS);
+
+ LOCK(&dyndb_lock);
+
+ /* duplicate instance names are not allowed */
+ if (impfind(name) != NULL) {
+ CHECK(ISC_R_EXISTS);
+ }
+
+ CHECK(load_library(mctx, libname, name, &implementation));
+ CHECK(implementation->register_func(mctx, name, parameters, file, line,
+ dctx, &implementation->inst));
+
+ APPEND(dyndb_implementations, implementation, link);
+ result = ISC_R_SUCCESS;
+
+cleanup:
+ if (result != ISC_R_SUCCESS) {
+ if (implementation != NULL) {
+ unload_library(&implementation);
+ }
+ }
+
+ UNLOCK(&dyndb_lock);
+ return (result);
+}
+
+void
+dns_dyndb_cleanup(bool exiting) {
+ dyndb_implementation_t *elem;
+ dyndb_implementation_t *prev;
+
+ RUNTIME_CHECK(isc_once_do(&once, dyndb_initialize) == ISC_R_SUCCESS);
+
+ LOCK(&dyndb_lock);
+ elem = TAIL(dyndb_implementations);
+ while (elem != NULL) {
+ prev = PREV(elem, link);
+ UNLINK(dyndb_implementations, elem, link);
+ isc_log_write(dns_lctx, DNS_LOGCATEGORY_DATABASE,
+ DNS_LOGMODULE_DYNDB, ISC_LOG_INFO,
+ "unloading DynDB instance '%s'", elem->name);
+ elem->destroy_func(&elem->inst);
+ ENSURE(elem->inst == NULL);
+ unload_library(&elem);
+ elem = prev;
+ }
+ UNLOCK(&dyndb_lock);
+
+ if (exiting) {
+ isc_mutex_destroy(&dyndb_lock);
+ }
+}
+
+isc_result_t
+dns_dyndb_createctx(isc_mem_t *mctx, const void *hashinit, isc_log_t *lctx,
+ dns_view_t *view, dns_zonemgr_t *zmgr, isc_task_t *task,
+ isc_timermgr_t *tmgr, dns_dyndbctx_t **dctxp) {
+ dns_dyndbctx_t *dctx;
+
+ REQUIRE(dctxp != NULL && *dctxp == NULL);
+
+ dctx = isc_mem_get(mctx, sizeof(*dctx));
+
+ memset(dctx, 0, sizeof(*dctx));
+ if (view != NULL) {
+ dns_view_attach(view, &dctx->view);
+ }
+ if (zmgr != NULL) {
+ dns_zonemgr_attach(zmgr, &dctx->zmgr);
+ }
+ if (task != NULL) {
+ isc_task_attach(task, &dctx->task);
+ }
+ dctx->timermgr = tmgr;
+ dctx->hashinit = hashinit;
+ dctx->lctx = lctx;
+ dctx->memdebug = &isc_mem_debugging;
+
+ isc_mem_attach(mctx, &dctx->mctx);
+ dctx->magic = DNS_DYNDBCTX_MAGIC;
+
+ *dctxp = dctx;
+
+ return (ISC_R_SUCCESS);
+}
+
+void
+dns_dyndb_destroyctx(dns_dyndbctx_t **dctxp) {
+ dns_dyndbctx_t *dctx;
+
+ REQUIRE(dctxp != NULL && DNS_DYNDBCTX_VALID(*dctxp));
+
+ dctx = *dctxp;
+ *dctxp = NULL;
+
+ dctx->magic = 0;
+
+ if (dctx->view != NULL) {
+ dns_view_detach(&dctx->view);
+ }
+ if (dctx->zmgr != NULL) {
+ dns_zonemgr_detach(&dctx->zmgr);
+ }
+ if (dctx->task != NULL) {
+ isc_task_detach(&dctx->task);
+ }
+ dctx->timermgr = NULL;
+ dctx->lctx = NULL;
+
+ isc_mem_putanddetach(&dctx->mctx, dctx, sizeof(*dctx));
+}