mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-08-24 11:08:45 +00:00
- 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
464 lines
10 KiB
C
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 **)®ister_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 **)®ister_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);
|
|
}
|