2
0
mirror of https://gitlab.isc.org/isc-projects/bind9 synced 2025-08-24 11:08:45 +00:00
bind/lib/ns/hooks.c
Evan Hunt 81f58e2ea2 enable modules to store data in qctx
- added a 'hookdata' array to qctx to store pointers to up to
  16 blobs of data which are allocated by modules as needed.
  each module is assigned an ID number as it's loaded, and this
  is the index into the hook data array. this is to be used for
  holding persistent state between calls to a hook module for a
  specific query.
- instead of using qctx->filter_aaaa, we now use qctx->hookdata.
  (this was the last piece of filter-aaaa specific code outside the
  module.)
- added hook points for qctx initialization and destruction. we get
  a filter-aaaa data pointer from the mempool when initializing and
  store it in the qctx->hookdata table; return to to the mempool
  when destroying the qctx.
- link the view to the qctx so that detaching the client doesn't cause
  hooks to fail
- added a qctx_destroy() function which must be called after qctx_init;
  this calls the QCTX_DESTROY hook and detaches the view
- general cleanup and comments
2018-12-06 10:29:11 -08:00

464 lines
10 KiB
C

/*
* Copyright (C) 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/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
/*! \file */
#include <config.h>
#include <string.h>
#if HAVE_DLFCN_H
#include <dlfcn.h>
#elif _WIN32
#include <windows.h>
#endif
#include <isc/mem.h>
#include <isc/mutex.h>
#include <isc/result.h>
#include <isc/once.h>
#include <isc/util.h>
#include <ns/hooks.h>
#include <ns/log.h>
#define CHECK(op) \
do { result = (op); \
if (result != ISC_R_SUCCESS) goto cleanup; \
} while (0)
typedef struct ns_hook_module ns_hook_module_t;
struct ns_hook_module {
isc_mem_t *mctx;
void *handle;
char *filename;
ns_hook_register_t *register_func;
ns_hook_destroy_t *destroy_func;
void *inst;
LINK(ns_hook_module_t) link;
};
static ns_hooklist_t hooktab[NS_QUERY_HOOKS_COUNT];
LIBNS_EXTERNAL_DATA ns_hooktable_t *ns__hook_table = &hooktab;
/*
* List of hook modules.
*
* These are stored here so they can be cleaned up on shutdown.
* (The order in which they are stored is not important.)
*/
static LIST(ns_hook_module_t) hook_modules;
static isc_once_t once = ISC_ONCE_INIT;
static void
init_modules(void) {
INIT_LIST(hook_modules);
}
#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(ns_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_HOOKS, ISC_LOG_ERROR,
"failed to look upsymbol %s in "
"hook 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, ns_hook_module_t **hmodp) {
isc_result_t result;
void *handle = NULL;
ns_hook_module_t *hmod = NULL;
ns_hook_register_t *register_func = NULL;
ns_hook_destroy_t *destroy_func = NULL;
ns_hook_version_t *version_func = NULL;
int version, flags;
REQUIRE(hmodp != NULL && *hmodp == NULL);
flags = RTLD_NOW|RTLD_LOCAL;
#ifdef RTLD_DEEPBIND
flags |= RTLD_DEEPBIND;
#endif
handle = dlopen(filename, flags);
if (handle == NULL) {
CHECK(ISC_R_FAILURE);
}
/* Clear dlerror */
dlerror();
CHECK(load_symbol(handle, filename, "hook_version",
(void **)&version_func));
version = version_func(NULL);
if (version < (NS_HOOK_VERSION - NS_HOOK_AGE) ||
version > NS_HOOK_VERSION)
{
isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_HOOKS, ISC_LOG_ERROR,
"driver API version mismatch: %d/%d",
version, NS_HOOK_VERSION);
CHECK(ISC_R_FAILURE);
}
CHECK(load_symbol(handle, filename, "hook_register",
(void **)&register_func));
CHECK(load_symbol(handle, filename, "hook_destroy",
(void **)&destroy_func));
hmod = isc_mem_get(mctx, sizeof(*hmod));
hmod->mctx = NULL;
isc_mem_attach(mctx, &hmod->mctx);
hmod->handle = handle;
hmod->register_func = register_func;
hmod->destroy_func = destroy_func;
hmod->inst = NULL;
ISC_LINK_INIT(hmod, link);
*hmodp = hmod;
hmod = NULL;
cleanup:
if (result != ISC_R_SUCCESS) {
isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_HOOKS, ISC_LOG_ERROR,
"failed to dynamically load "
"module '%s': %s (%s)", filename,
dlerror(), isc_result_totext(result));
if (hmod != NULL) {
isc_mem_putanddetach(&hmod->mctx, hmod,
sizeof(*hmod));
}
if (handle != NULL) {
dlclose(handle);
}
}
return (result);
}
static void
unload_library(ns_hook_module_t **hmodp) {
ns_hook_module_t *hmod;
REQUIRE(hmodp != NULL && *hmodp != NULL);
hmod = *hmodp;
*hmodp = NULL;
if (hmod->handle != NULL) {
dlclose(hmod->handle);
}
if (hmod->filename != NULL) {
isc_mem_free(hmod->mctx, hmod->filename);
}
isc_mem_putanddetach(&hmod->mctx, hmod, sizeof(*hmod));
}
#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(ns_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_HOOKS, ISC_LOG_ERROR,
"failed to look up symbol %s in "
"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, ns_hook_module_t **hmodp) {
isc_result_t result;
HMODULE handle;
ns_hook_module_t *hmod = NULL;
ns_hook_register_t *register_func = NULL;
ns_hook_destroy_t *destroy_func = NULL;
ns_hook_version_t *version_func = NULL;
int version;
REQUIRE(hmodp != NULL && *hmodp == NULL);
handle = LoadLibraryA(filename);
if (handle == NULL) {
CHECK(ISC_R_FAILURE);
}
CHECK(load_symbol(handle, filename, "hook_version",
(void **)&version_func));
version = version_func(NULL);
if (version < (NS_HOOK_VERSION - NS_HOOK_AGE) ||
version > NS_HOOK_VERSION)
{
isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_HOOKS, ISC_LOG_ERROR,
"driver API version mismatch: %d/%d",
version, NS_HOOK_VERSION);
CHECK(ISC_R_FAILURE);
}
CHECK(load_symbol(handle, filename, "hook_register",
(void **)&register_func));
CHECK(load_symbol(handle, filename, "hook_destroy",
(void **)&destroy_func));
hmod = isc_mem_get(mctx, sizeof(*hmod));
hmod->mctx = NULL;
isc_mem_attach(mctx, &hmod->mctx);
hmod->handle = handle;
hmod->register_func = register_func;
hmod->destroy_func = destroy_func;
hmod->inst = NULL;
ISC_LINK_INIT(hmod, link);
*hmodp = hmod;
hmod = NULL;
cleanup:
if (result != ISC_R_SUCCESS) {
isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_HOOKS, ISC_LOG_ERROR,
"failed to dynamically load "
"hook module '%s': %d (%s)", filename,
GetLastError(), isc_result_totext(result));
if (hmod != NULL) {
isc_mem_putanddetach(&hmod->mctx, hmod,
sizeof(*hmod));
}
if (handle != NULL) {
FreeLibrary(handle);
}
}
return (result);
}
static void
unload_library(ns_hook_module_t **hmodp) {
ns_hook_module_t *hmod;
REQUIRE(hmodp != NULL && *hmodp != NULL);
hmod = *hmodp;
*hmodp = NULL;
if (hmod->handle != NULL) {
FreeLibrary(hmod->handle);
}
if (hmod->filename != NULL) {
isc_mem_free(hmod->mctx, hmod->filename);
}
isc_mem_putanddetach(&hmod->mctx, hmod, sizeof(*hmod));
}
#else /* HAVE_DLFCN_H || _WIN32 */
static isc_result_t
load_library(isc_mem_t *mctx, const char *filename, ns_hook_module_t **hmodp) {
UNUSED(mctx);
UNUSED(filename);
UNUSED(hmodp);
isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_HOOKS, ISC_LOG_ERROR,
"hook module support is not hmodlemented");
return (ISC_R_NOTIMPLEMENTED);
}
static void
unload_library(ns_hook_module_t **hmodp) {
UNUSED(hmodp);
}
#endif /* HAVE_DLFCN_H */
isc_result_t
ns_hookmodule_load(const char *libname, const unsigned int modid,
const char *parameters,
const char *file, unsigned long line,
const void *cfg, void *actx,
ns_hookctx_t *hctx, ns_hooktable_t *hooktable)
{
isc_result_t result;
ns_hook_module_t *module = NULL;
REQUIRE(NS_HOOKCTX_VALID(hctx));
isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_HOOKS, ISC_LOG_INFO,
"loading module '%s'", libname);
CHECK(load_library(hctx->mctx, libname, &module));
CHECK(module->register_func(modid, parameters, file, line,
cfg, actx, hctx, hooktable,
&module->inst));
APPEND(hook_modules, module, link);
result = ISC_R_SUCCESS;
cleanup:
if (result != ISC_R_SUCCESS && module != NULL) {
unload_library(&module);
}
return (result);
}
void
ns_hookmodule_cleanup(void) {
ns_hook_module_t *hmod, *prev;
RUNTIME_CHECK(isc_once_do(&once, init_modules) == ISC_R_SUCCESS);
hmod = ISC_LIST_TAIL(hook_modules);
while (hmod != NULL) {
prev = PREV(hmod, link);
UNLINK(hook_modules, hmod, link);
isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
NS_LOGMODULE_HOOKS, ISC_LOG_INFO,
"unloading module '%s'", hmod->filename);
hmod->destroy_func(&hmod->inst);
ENSURE(hmod->inst == NULL);
unload_library(&hmod);
hmod = prev;
}
}
isc_result_t
ns_hook_createctx(isc_mem_t *mctx, ns_hookctx_t **hctxp) {
ns_hookctx_t *hctx;
REQUIRE(hctxp != NULL && *hctxp == NULL);
hctx = isc_mem_get(mctx, sizeof(*hctx));
memset(hctx, 0, sizeof(*hctx));
hctx->lctx = ns_lctx;
isc_mem_attach(mctx, &hctx->mctx);
hctx->magic = NS_HOOKCTX_MAGIC;
*hctxp = hctx;
return (ISC_R_SUCCESS);
}
void
ns_hook_destroyctx(ns_hookctx_t **hctxp) {
ns_hookctx_t *hctx;
REQUIRE(hctxp != NULL && NS_HOOKCTX_VALID(*hctxp));
hctx = *hctxp;
*hctxp = NULL;
hctx->magic = 0;
hctx->lctx = NULL;
isc_mem_putanddetach(&hctx->mctx, hctx, sizeof(*hctx));
}
void
ns_hooktable_init(ns_hooktable_t *hooktable) {
int i;
RUNTIME_CHECK(isc_once_do(&once, init_modules) == ISC_R_SUCCESS);
for (i = 0; i < NS_QUERY_HOOKS_COUNT; i++) {
ISC_LIST_INIT((*hooktable)[i]);
}
}
isc_result_t
ns_hooktable_create(isc_mem_t *mctx, ns_hooktable_t **tablep) {
ns_hooktable_t *hooktable;
REQUIRE(tablep != NULL && *tablep == NULL);
hooktable = isc_mem_get(mctx, sizeof(ns_hooktable_t));
ns_hooktable_init(hooktable);
*tablep = hooktable;
return (ISC_R_SUCCESS);
}
void
ns_hooktable_free(isc_mem_t *mctx, void **tablep) {
REQUIRE(tablep != NULL && *tablep != NULL);
isc_mem_put(mctx, *tablep, sizeof(ns_hooktable_t));
*tablep = NULL;
}
void
ns_hook_add(ns_hooktable_t *hooktable, ns_hookpoint_t hookpoint,
ns_hook_t *hook)
{
REQUIRE(hookpoint < NS_QUERY_HOOKS_COUNT);
REQUIRE(hook != NULL);
if (hooktable == NULL) {
hooktable = ns__hook_table;
}
ISC_LINK_INIT(hook, link);
ISC_LIST_APPEND((*hooktable)[hookpoint], hook, link);
}