From: Evan Hunt Date: Fri, 3 Aug 2018 21:16:41 +0000 (-0700) Subject: set up hooks.c to enable setting hook points and loading modules X-Git-Tag: v9.13.5~7^2~25 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=70cc3f803e40e60c9127edf458f6ada11ac0d737;p=thirdparty%2Fbind9.git set up hooks.c to enable setting hook points and loading modules - move hooks.h to public include directory - ns_hooktable_init() initializes a hook table. if NULL is passed in, it initializes the global hook table - ns_hooktable_save() saves a pointer to the current global hook table. - ns_hooktable_reset() replaces the global hook table with different one - ns_hook_add() adds hooks at specified hook points in a hook table (or the global hook table if the specified table is NULL) - load and unload functions support dlopen() of hook modules (this is adapted from dyndb and not yet functional) - began adding new hook points to query.c --- diff --git a/bin/named/server.c b/bin/named/server.c index 923e25a63bf..9e6c75a15f2 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -8030,6 +8030,7 @@ load_configuration(const char *filename, named_server_t *server, * Shut down all dyndb instances. */ dns_dyndb_cleanup(false); + ns_hookmodule_cleanup(); /* * Parse the global default pseudo-config file. @@ -9511,6 +9512,7 @@ shutdown_server(isc_task_t *task, isc_event_t *event) { } dns_dyndb_cleanup(true); + ns_hookmodule_cleanup(); while ((nsc = ISC_LIST_HEAD(server->cachelist)) != NULL) { ISC_LIST_UNLINK(server->cachelist, nsc, link); diff --git a/configure b/configure index 85f07cd4e1f..5031db85a3e 100755 --- a/configure +++ b/configure @@ -11916,7 +11916,7 @@ fi XTARGETS= case "$enable_developer" in yes) - STD_CDEFINES="$STD_CDEFINES -DISC_MEM_DEFAULTFILL=1 -DISC_LIST_CHECKINIT=1 -DNS_HOOKS_ENABLE=1" + STD_CDEFINES="$STD_CDEFINES -DISC_MEM_DEFAULTFILL=1 -DISC_LIST_CHECKINIT=1" test "${enable_fixed_rrset+set}" = set || enable_fixed_rrset=yes test "${enable_querytrace+set}" = set || enable_querytrace=yes test "${with_cmocka+set}" = set || with_cmocka=yes diff --git a/configure.ac b/configure.ac index df3683ba836..d831bae619e 100644 --- a/configure.ac +++ b/configure.ac @@ -76,7 +76,7 @@ AC_ARG_ENABLE(developer, XTARGETS= case "$enable_developer" in yes) - STD_CDEFINES="$STD_CDEFINES -DISC_MEM_DEFAULTFILL=1 -DISC_LIST_CHECKINIT=1 -DNS_HOOKS_ENABLE=1" + STD_CDEFINES="$STD_CDEFINES -DISC_MEM_DEFAULTFILL=1 -DISC_LIST_CHECKINIT=1" test "${enable_fixed_rrset+set}" = set || enable_fixed_rrset=yes test "${enable_querytrace+set}" = set || enable_querytrace=yes test "${with_cmocka+set}" = set || with_cmocka=yes diff --git a/lib/dns/dyndb.c b/lib/dns/dyndb.c index 0faf393878f..932244a8dbc 100644 --- a/lib/dns/dyndb.c +++ b/lib/dns/dyndb.c @@ -190,7 +190,8 @@ cleanup: "driver '%s': %s (%s)", instname, filename, dlerror(), isc_result_totext(result)); if (imp != NULL) - isc_mem_putanddetach(&imp->mctx, imp, sizeof(dyndb_implementation_t)); + isc_mem_putanddetach(&imp->mctx, imp, + sizeof(dyndb_implementation_t)); if (result != ISC_R_SUCCESS && handle != NULL) dlclose(handle); @@ -305,7 +306,8 @@ cleanup: "driver '%s': %d (%s)", instname, filename, GetLastError(), isc_result_totext(result)); if (imp != NULL) - isc_mem_putanddetach(&imp->mctx, imp, sizeof(dyndb_implementation_t)); + isc_mem_putanddetach(&imp->mctx, imp, + sizeof(dyndb_implementation_t)); if (result != ISC_R_SUCCESS && handle != NULL) FreeLibrary(handle); diff --git a/lib/dns/include/dns/dyndb.h b/lib/dns/include/dns/dyndb.h index d5fedb5fda4..d5faf920695 100644 --- a/lib/dns/include/dns/dyndb.h +++ b/lib/dns/include/dns/dyndb.h @@ -40,7 +40,7 @@ struct dns_dyndbctx { dns_zonemgr_t *zmgr; isc_task_t *task; isc_timermgr_t *timermgr; - bool *refvar; + bool *refvar; }; #define DNS_DYNDBCTX_MAGIC ISC_MAGIC('D', 'd', 'b', 'c') @@ -71,7 +71,7 @@ typedef isc_result_t dns_dyndb_register_t(isc_mem_t *mctx, * 'parameters' contains the driver configuration text. 'dctx' is the * initialization context set up in dns_dyndb_createctx(). * - * '*instp' must be set to the driver instance handle if the functino + * '*instp' will be set to the driver instance handle if the function * is successful. * * Returns: diff --git a/lib/ns/Makefile.in b/lib/ns/Makefile.in index 51d21377c67..2e6c0183da7 100644 --- a/lib/ns/Makefile.in +++ b/lib/ns/Makefile.in @@ -43,12 +43,12 @@ DNSDEPLIBS = ../../lib/dns/libdns.@A@ LIBS = @LIBS@ # Alphabetically -OBJS = client.@O@ interfacemgr.@O@ lib.@O@ \ +OBJS = client.@O@ hooks.@O@ interfacemgr.@O@ lib.@O@ \ listenlist.@O@ log.@O@ notify.@O@ query.@O@ \ server.@O@ sortlist.@O@ stats.@O@ update.@O@ \ version.@O@ xfrout.@O@ -SRCS = client.c interfacemgr.c lib.c listenlist.c \ +SRCS = client.c hooks.c interfacemgr.c lib.c listenlist.c \ log.c notify.c query.c server.c sortlist.c stats.c \ update.c version.c xfrout.c diff --git a/lib/ns/hooks.c b/lib/ns/hooks.c new file mode 100644 index 00000000000..4b97c5b3d5f --- /dev/null +++ b/lib/ns/hooks.c @@ -0,0 +1,439 @@ +/* + * 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 + +#include + +#if HAVE_DLFCN_H +#include +#elif _WIN32 +#include +#endif + +#include +#include +#include +#include +#include + +#include +#include + +#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; + char *name; + 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); + + isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_HOOKS, ISC_LOG_INFO, + "loading module '%s'", + filename); + + 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_init", + (void **)®ister_func)); + CHECK(load_symbol(handle, filename, "hook_destroy", + (void **)&destroy_func)); + + hmod = isc_mem_get(mctx, sizeof(*hmod)); + if (hmod == NULL) { + CHECK(ISC_R_NOMEMORY); + } + + 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(ns_hook_module_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(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); + + isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_HOOKS, ISC_LOG_INFO, + "loading module '%s'", filename); + + 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_init", + (void **)®ister_func)); + CHECK(load_symbol(handle, filename, "hook_destroy", + (void **)&destroy_func)); + + hmod = isc_mem_get(mctx, sizeof(*hmod)); + if (hmod == NULL) { + CHECK(ISC_R_NOMEMORY); + } + + 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 char *parameters, + const char *file, unsigned long line, isc_mem_t *mctx) +{ + isc_result_t result; + ns_hook_module_t *module = NULL; + + RUNTIME_CHECK(isc_once_do(&once, init_modules) == ISC_R_SUCCESS); + + isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL, + NS_LOGMODULE_HOOKS, ISC_LOG_INFO, + "loading module '%s'", libname); + + CHECK(load_library(mctx, libname, &module)); + CHECK(module->register_func(mctx, parameters, file, line, + &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->name); + hmod->destroy_func(&hmod->inst); + ENSURE(hmod->inst == NULL); + unload_library(&hmod); + hmod = prev; + } +} + +void +ns_hooktable_init(ns_hooktable_t *hooktable) { + int i; + + RUNTIME_CHECK(isc_once_do(&once, init_modules) == ISC_R_SUCCESS); + + if (hooktable == NULL) { + hooktable = ns__hook_table; + } + + for (i = 0; i < NS_QUERY_HOOKS_COUNT; i++) { + ISC_LIST_INIT((*hooktable)[i]); + } +} + +ns_hooktable_t * +ns_hooktable_save() { + return (ns__hook_table); +} + +void +ns_hooktable_reset(ns_hooktable_t *hooktable) { + if (hooktable != NULL) { + ns__hook_table = hooktable; + } else { + ns__hook_table = &hooktab; + } +} + +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); +} diff --git a/lib/ns/include/ns/Makefile.in b/lib/ns/include/ns/Makefile.in index 9bfa9f15982..2009d9a73a8 100644 --- a/lib/ns/include/ns/Makefile.in +++ b/lib/ns/include/ns/Makefile.in @@ -13,7 +13,7 @@ top_srcdir = @top_srcdir@ VERSION=@BIND9_VERSION@ -HEADERS = client.h interfacemgr.h lib.h listenlist.h log.h \ +HEADERS = client.h hooks.h interfacemgr.h lib.h listenlist.h log.h \ notify.h query.h server.h sortlist.h stats.h \ types.h update.h version.h xfrout.h SUBDIRS = diff --git a/lib/ns/hooks.h b/lib/ns/include/ns/hooks.h similarity index 61% rename from lib/ns/hooks.h rename to lib/ns/include/ns/hooks.h index 5c3aa14409f..04752277509 100644 --- a/lib/ns/hooks.h +++ b/lib/ns/include/ns/hooks.h @@ -12,12 +12,11 @@ #ifndef NS_HOOKS_H #define NS_HOOKS_H 1 -#ifdef NS_HOOKS_ENABLE - /*! \file */ #include +#include #include /* @@ -158,40 +157,171 @@ * called this time and foo_bar() will return ISC_R_SUCCESS. */ -enum { +typedef enum { NS_QUERY_SETUP_QCTX_INITIALIZED, + NS_QUERY_START_BEGIN, NS_QUERY_LOOKUP_BEGIN, + NS_QUERY_RESUME_BEGIN, + NS_QUERY_PREP_RESPONSE_BEGIN, + NS_QUERY_RESPOND_ANY_BEGIN, + NS_QUERY_RESPOND_ANY_POST_LOOKUP, + NS_QUERY_RESPOND_ANY_FOUND, + NS_QUERY_RESPOND_ANY_NOT_FOUND, + NS_QUERY_RESPOND_BEGIN, + NS_QUERY_GOT_ANSWER_BEGIN, + NS_QUERY_NOTFOUND_BEGIN, + NS_QUERY_PREP_DELEGATION_BEGIN, + NS_QUERY_ZONE_DELEGATION_BEGIN, + NS_QUERY_DELEGATION_BEGIN, + NS_QUERY_NODATA_BEGIN, + NS_QUERY_NXDOMAIN_BEGIN, + NS_QUERY_CNAME_BEGIN, + NS_QUERY_DNAME_BEGIN, + NS_QUERY_ADDITIONAL_BEGIN, NS_QUERY_DONE_BEGIN, + NS_QUERY_DONE_SEND, NS_QUERY_HOOKS_COUNT /* MUST BE LAST */ -}; +} ns_hookpoint_t; typedef bool (*ns_hook_cb_t)(void *hook_data, void *callback_data, isc_result_t *resultp); +/* + * API version + * + * When the API changes, increment NS_HOOK_VERSION. If the + * change is backward-compatible (e.g., adding a new function call + * but not changing or removing an old one), increment NS_HOOK_AGE + * as well; if not, set NS_HOOK_AGE to 0. + */ +#ifndef NS_HOOK_VERSION +#define NS_HOOK_VERSION 1 +#define NS_HOOK_AGE 0 +#endif + +typedef isc_result_t ns_hook_register_t(isc_mem_t *mctx, + const char *parameters, + const char *file, + unsigned long line, + void **instp); +/*% + * Called when registering a new module. + * + * 'parameters' contains the module configuration text. + * + * '*instp' will be set to the module instance handle if the function + * is successful. + * + * Returns: + *\li #ISC_R_SUCCESS + *\li #ISC_R_NOMEMORY + *\li Other errors are possible + */ + +typedef void ns_hook_destroy_t(void **instp); +/*% + * Destroy a module instance. + * + * '*instp' must be set to NULL by the function before it returns. + */ + +typedef int ns_hook_version_t(unsigned int *flags); +/*% + * Return the API version number a hook module was compiled with. + * + * If the returned version number is no greater than than + * NS_HOOK_VERSION, and no less than NS_HOOK_VERSION - NS_HOOK_AGE, + * then the module is API-compatible with named. + * + * 'flags' is currently unused and may be NULL, but could be used in + * the future to pass back driver capabilities or other information. + */ + typedef struct ns_hook { ns_hook_cb_t callback; void *callback_data; + ISC_LINK(struct ns_hook) link; } ns_hook_t; +/* + * ns__hook_table is a globally visible pointer to the active hook + * table. It's initialized to point to 'hooktab', which is the default + * global hook table. + */ +typedef ISC_LIST(ns_hook_t) ns_hooklist_t; +typedef ns_hooklist_t ns_hooktable_t[NS_QUERY_HOOKS_COUNT]; +LIBNS_EXTERNAL_DATA extern ns_hooktable_t *ns__hook_table; + +/* + * Run a hook. Calls the function or functions registered at hookpoint 'id'. + * If one of them returns true, we interrupt processing and return the + * result that was returned by the hook function. If none of them return + * true, we continue processing. + */ #define _NS_PROCESS_HOOK(table, id, data, ...) \ - if (table != NULL) { \ - ns_hook_cb_t _callback = table[id].callback; \ - void *_callback_data = table[id].callback_data; \ + if (table != NULL) { \ + ns_hook_t *_hook = ISC_LIST_HEAD((*table)[id]); \ isc_result_t _result; \ \ - if (_callback != NULL && \ - _callback(data, _callback_data, &_result)) { \ - return __VA_ARGS__; \ + while (_hook != NULL) { \ + ns_hook_cb_t _callback = _hook->callback; \ + void *_callback_data = _hook->callback_data; \ + if (_callback != NULL && \ + _callback(data, _callback_data, &_result)) \ + { \ + return __VA_ARGS__; \ + } else { \ + _hook = ISC_LIST_NEXT(_hook, link); \ + } \ } \ } #define NS_PROCESS_HOOK(table, id, data) \ _NS_PROCESS_HOOK(table, id, data, _result) - #define NS_PROCESS_HOOK_VOID(table, id, data) \ _NS_PROCESS_HOOK(table, id, data) -LIBNS_EXTERNAL_DATA extern ns_hook_t *ns__hook_table; +isc_result_t +ns_hookmodule_load(const char *libname, const char *parameters, + const char *file, unsigned long line, isc_mem_t *mctx); +void +ns_hookmodule_cleanup(void); + +void +ns_hook_add(ns_hooktable_t *hooktable, ns_hookpoint_t hookpoint, + ns_hook_t *hook); +/*% + * Append hook function 'hook' to the list of hooks at 'hookpoint' in + * 'hooktable'. If 'hooktable' is NULL, the global hook table + * ns__hook_table is used. + * + * Requires: + *\li 'hook' is not NULL + * + *\li 'hookpoint' is less than NS_QUERY_HOOKS_COUNT + * + */ + +ns_hooktable_t * +ns_hooktable_save(void); +/*% + * Returns a pointer to the current global hook table so it can + * be restored after replacing it. + */ + +void +ns_hooktable_reset(ns_hooktable_t *hooktable); +/*% + * Set the global hooks table pointer to 'hooktable'. + * + * If 'hooktable' is NULL, restores the default global hook table. + */ + +void +ns_hooktable_init(ns_hooktable_t *hooktable); +/*% + * Initialize a hook table. If 'hooktable' is NULL, initialize + * the global hooktable ns__hook_table. + */ -#endif /* NS_HOOKS_ENABLE */ #endif /* NS_HOOKS_H */ diff --git a/lib/ns/include/ns/log.h b/lib/ns/include/ns/log.h index aab57aca091..24689a652ce 100644 --- a/lib/ns/include/ns/log.h +++ b/lib/ns/include/ns/log.h @@ -42,6 +42,7 @@ LIBNS_EXTERNAL_DATA extern isc_logmodule_t ns_modules[]; #define NS_LOGMODULE_XFER_IN (&ns_modules[4]) #define NS_LOGMODULE_XFER_OUT (&ns_modules[5]) #define NS_LOGMODULE_NOTIFY (&ns_modules[6]) +#define NS_LOGMODULE_HOOKS (&ns_modules[7]) void ns_log_init(isc_log_t *lctx); diff --git a/lib/ns/log.c b/lib/ns/log.c index f167674b32e..3e83d98401f 100644 --- a/lib/ns/log.c +++ b/lib/ns/log.c @@ -50,6 +50,7 @@ LIBNS_EXTERNAL_DATA isc_logmodule_t ns_modules[] = { { "ns/xfer-in", 0 }, { "ns/xfer-out", 0 }, { "ns/notify", 0 }, + { "ns/hooks", 0 }, { NULL, 0 } }; diff --git a/lib/ns/query.c b/lib/ns/query.c index 64a38e45621..54af7eac7c1 100644 --- a/lib/ns/query.c +++ b/lib/ns/query.c @@ -62,14 +62,13 @@ #include #include +#include #include #include #include #include #include -#include "hooks.h" - #if 0 /* * It has been recommended that DNS64 be changed to return excluded @@ -240,22 +239,11 @@ static void log_noexistnodata(void *val, int level, const char *fmt, ...) ISC_FORMAT_PRINTF(3, 4); -#ifdef NS_HOOKS_ENABLE - -LIBNS_EXTERNAL_DATA ns_hook_t *ns__hook_table = NULL; - #define PROCESS_HOOK(...) \ NS_PROCESS_HOOK(ns__hook_table, __VA_ARGS__) #define PROCESS_HOOK_VOID(...) \ NS_PROCESS_HOOK_VOID(ns__hook_table, __VA_ARGS__) -#else - -#define PROCESS_HOOK(...) do {} while (0) -#define PROCESS_HOOK_VOID(...) do {} while (0) - -#endif - /* * The functions defined below implement the query logic that previously lived * in the single very complex function query_find(). The query_ctx_t structure @@ -433,8 +421,7 @@ static void query_addbestns(query_ctx_t *qctx); static void -query_addwildcardproof(query_ctx_t *qctx, bool ispositive, - bool nodata); +query_addwildcardproof(query_ctx_t *qctx, bool ispositive, bool nodata); static void query_addauth(query_ctx_t *qctx); @@ -1989,6 +1976,7 @@ query_addadditional(void *arg, const dns_name_t *name, dns_rdatatype_t qtype) { (!WANTDNSSEC(client) || sigrdataset == NULL || !dns_rdataset_isassociated(sigrdataset))))) goto addname; + if (additionaltype == dns_rdatasetadditional_fromcache && (DNS_TRUST_PENDING(rdataset->trust) || @@ -5253,6 +5241,8 @@ ns__query_start(query_ctx_t *qctx) { qctx->need_wildcardproof = false; qctx->rpz = false; + PROCESS_HOOK(NS_QUERY_START_BEGIN, qctx); + /* * If we require a server cookie then send back BADCOOKIE * before we have done too much work. @@ -5881,6 +5871,8 @@ query_resume(query_ctx_t *qctx) { char tbuf[DNS_RDATATYPE_FORMATSIZE]; #endif + PROCESS_HOOK(NS_QUERY_RESUME_BEGIN, qctx); + qctx->want_restart = false; qctx->rpz_st = qctx->client->query.rpz_st; @@ -6703,6 +6695,8 @@ query_gotanswer(query_ctx_t *qctx, isc_result_t result) { CCTRACE(ISC_LOG_DEBUG(3), "query_gotanswer"); + PROCESS_HOOK(NS_QUERY_GOT_ANSWER_BEGIN, qctx); + if (query_checkrrl(qctx, result) != ISC_R_SUCCESS) { return (query_done(qctx)); } @@ -6893,6 +6887,8 @@ query_respond_any(query_ctx_t *qctx) { have_a = !qctx->authoritative; have_sig = false; + PROCESS_HOOK(NS_QUERY_RESPOND_ANY_BEGIN, qctx); + result = dns_db_allrdatasets(qctx->db, qctx->node, qctx->version, 0, &rdsiter); if (result != ISC_R_SUCCESS) { @@ -7044,6 +7040,8 @@ query_respond_any(query_ctx_t *qctx) { result = dns_rdatasetiter_next(rdsiter); } + PROCESS_HOOK(NS_QUERY_RESPOND_ANY_POST_LOOKUP, qctx); + /* * Filter AAAAs if there is an A and there is no signature * or we are supposed to break DNSSEC. @@ -7273,6 +7271,8 @@ query_respond(query_ctx_t *qctx) { dns_rdataset_t **sigrdatasetp = NULL; isc_result_t result; + PROCESS_HOOK(NS_QUERY_RESPOND_BEGIN, qctx); + /* * If we have a zero ttl from the cache, refetch. */ @@ -7761,6 +7761,8 @@ static isc_result_t query_notfound(query_ctx_t *qctx) { isc_result_t result; + PROCESS_HOOK(NS_QUERY_NOTFOUND_BEGIN, qctx); + INSIST(!qctx->is_zone); if (qctx->db != NULL) @@ -7837,6 +7839,8 @@ query_prepare_delegation_response(query_ctx_t *qctx) { dns_rdataset_t **sigrdatasetp = NULL; bool detach = false; + PROCESS_HOOK(NS_QUERY_PREP_DELEGATION_BEGIN, qctx); + /* * qctx->fname could be released in query_addrrset(), so save a copy of * it here in case we need it. @@ -7886,6 +7890,8 @@ static isc_result_t query_zone_delegation(query_ctx_t *qctx) { isc_result_t result; + PROCESS_HOOK(NS_QUERY_ZONE_DELEGATION_BEGIN, qctx); + /* * If the query type is DS, look to see if we are * authoritative for the child zone @@ -7977,6 +7983,8 @@ static isc_result_t query_delegation(query_ctx_t *qctx) { isc_result_t result; + PROCESS_HOOK(NS_QUERY_DELEGATION_BEGIN, qctx); + qctx->authoritative = false; if (qctx->is_zone) { @@ -8223,6 +8231,8 @@ query_addds(query_ctx_t *qctx) { */ static isc_result_t query_nodata(query_ctx_t *qctx, isc_result_t result) { + PROCESS_HOOK(NS_QUERY_NODATA_BEGIN, qctx); + #ifdef dns64_bis_return_excluded_addresses if (qctx->dns64) #else @@ -8534,6 +8544,8 @@ query_nxdomain(query_ctx_t *qctx, bool empty_wild) { uint32_t ttl; isc_result_t result; + PROCESS_HOOK(NS_QUERY_NXDOMAIN_BEGIN, qctx); + INSIST(qctx->is_zone || REDIRECT(qctx->client)); if (!empty_wild) { @@ -9437,6 +9449,8 @@ query_cname(query_ctx_t *qctx) { dns_rdata_t rdata = DNS_RDATA_INIT; dns_rdata_cname_t cname; + PROCESS_HOOK(NS_QUERY_CNAME_BEGIN, qctx); + /* * If we have a zero ttl from the cache refetch it. */ @@ -9565,6 +9579,8 @@ query_dname(query_ctx_t *qctx) { isc_result_t result; unsigned int nlabels; + PROCESS_HOOK(NS_QUERY_DNAME_BEGIN, qctx); + /* * Compare the current qname to the found name. We need * to know how many labels and bits are in common because @@ -9773,6 +9789,8 @@ query_addcname(query_ctx_t *qctx, dns_trust_t trust, dns_ttl_t ttl) { */ static isc_result_t query_prepresponse(query_ctx_t *qctx) { + PROCESS_HOOK(NS_QUERY_PREP_RESPONSE_BEGIN, qctx); + if (WANTDNSSEC(qctx->client) && (qctx->fname->attributes & DNS_NAMEATTR_WILDCARD) != 0) { @@ -9798,11 +9816,14 @@ query_prepresponse(query_ctx_t *qctx) { if (result == ISC_R_SUCCESS && qctx->client->view->v4_aaaa != dns_aaaa_ok && is_v4_client(qctx->client)) + { qctx->client->filter_aaaa = qctx->client->view->v4_aaaa; - else if (result == ISC_R_SUCCESS && - qctx->client->view->v6_aaaa != dns_aaaa_ok && - is_v6_client(qctx->client)) + } else if (result == ISC_R_SUCCESS && + qctx->client->view->v6_aaaa != dns_aaaa_ok && + is_v6_client(qctx->client)) + { qctx->client->filter_aaaa = qctx->client->view->v6_aaaa; + } } @@ -10778,6 +10799,8 @@ query_done(query_ctx_t *qctx) { qctx->result = ISC_R_FAILURE; } + PROCESS_HOOK(NS_QUERY_DONE_SEND, qctx); + query_send(qctx->client); ns_client_detach(&qctx->client); diff --git a/lib/ns/tests/listenlist_test.c b/lib/ns/tests/listenlist_test.c index 40415c1d7c6..24b5b970f0b 100644 --- a/lib/ns/tests/listenlist_test.c +++ b/lib/ns/tests/listenlist_test.c @@ -20,8 +20,6 @@ #include -#ifdef NS_HOOKS_ENABLE - #include #include #include @@ -126,18 +124,6 @@ main(void) { return (cmocka_run_group_tests(tests, NULL, NULL)); } -#else - -#include - -int -main(void) { - printf("1..0 # Skipped: libns hooks not enabled\n"); - return (0); -} - -#endif /* NS_HOOKS_ENABLE */ - #else /* HAVE_CMOCKA */ #include diff --git a/lib/ns/tests/notify_test.c b/lib/ns/tests/notify_test.c index e1e2654ca38..b6884ea7df5 100644 --- a/lib/ns/tests/notify_test.c +++ b/lib/ns/tests/notify_test.c @@ -20,8 +20,6 @@ #include -#ifdef NS_HOOKS_ENABLE - #include #include #include @@ -154,18 +152,6 @@ main(void) { return (cmocka_run_group_tests(tests, NULL, NULL)); } -#else - -#include - -int -main(void) { - printf("1..0 # Skipped: libns hooks not enabled\n"); - return (0); -} - -#endif /* NS_HOOKS_ENABLE */ - #else /* HAVE_CMOCKA */ #include diff --git a/lib/ns/tests/nstest.c b/lib/ns/tests/nstest.c index 85c641bcd48..f0a589ed156 100644 --- a/lib/ns/tests/nstest.c +++ b/lib/ns/tests/nstest.c @@ -13,8 +13,6 @@ #include -#ifdef NS_HOOKS_ENABLE - #include #include #include @@ -47,11 +45,10 @@ #include #include +#include #include #include -#include "../hooks.h" - #include "nstest.h" isc_mem_t *mctx = NULL; @@ -287,8 +284,6 @@ ns_test_begin(FILE *logfile, bool start_managers) { CHECK(ISC_R_FAILURE); } - ns__hook_table = NULL; - return (ISC_R_SUCCESS); cleanup: @@ -677,12 +672,10 @@ extract_qctx(void *hook_data, void *callback_data, isc_result_t *resultp) { */ static isc_result_t create_qctx_for_client(ns_client_t *client, query_ctx_t **qctxp) { - ns_hook_t *saved_hook_table; - ns_hook_t query_hooks[NS_QUERY_HOOKS_COUNT + 1] = { - [NS_QUERY_SETUP_QCTX_INITIALIZED] = { - .callback = extract_qctx, - .callback_data = qctxp, - }, + ns_hooktable_t *saved_hook_table, query_hooks; + ns_hook_t hook = { + .callback = extract_qctx, + .callback_data = qctxp, }; REQUIRE(client != NULL); @@ -696,10 +689,15 @@ create_qctx_for_client(ns_client_t *client, query_ctx_t **qctxp) { * further processing. Make sure we do not overwrite any previously * set hooks. */ - saved_hook_table = ns__hook_table; - ns__hook_table = query_hooks; + + ns_hooktable_init(&query_hooks); + ns_hook_add(&query_hooks, NS_QUERY_SETUP_QCTX_INITIALIZED, &hook); + + saved_hook_table = ns_hooktable_save(); + + ns_hooktable_reset(&query_hooks); ns_query_start(client); - ns__hook_table = saved_hook_table; + ns_hooktable_reset(saved_hook_table); if (*qctxp == NULL) { return (ISC_R_NOMEMORY); @@ -927,4 +925,3 @@ ns_test_getdata(const char *file, unsigned char *buf, isc_stdio_close(f); return (result); } -#endif diff --git a/lib/ns/tests/query_test.c b/lib/ns/tests/query_test.c index 21c68e4c763..b47b775fdae 100644 --- a/lib/ns/tests/query_test.c +++ b/lib/ns/tests/query_test.c @@ -19,8 +19,6 @@ #include -#ifdef NS_HOOKS_ENABLE - #include #include #include @@ -31,12 +29,12 @@ #include #include + #include +#include #include #include -#include "../hooks.h" - #include "nstest.h" static int @@ -95,13 +93,14 @@ run_sfcache_test(const ns__query_sfcache_test_params_t *test) { /* * Interrupt execution if query_done() is called. */ - ns_hook_t query_hooks[NS_QUERY_HOOKS_COUNT + 1] = { - [NS_QUERY_DONE_BEGIN] = { - .callback = ns_test_hook_catch_call, - .callback_data = NULL, - }, + ns_hook_t hook = { + .callback = ns_test_hook_catch_call, }; - ns__hook_table = query_hooks; + ns_hooktable_t query_hooks; + + ns_hooktable_init(&query_hooks); + ns_hook_add(&query_hooks, NS_QUERY_DONE_BEGIN, &hook); + ns_hooktable_reset(&query_hooks); /* * Construct a query context for a ./NS query with given flags. @@ -295,17 +294,16 @@ run_start_test(const ns__query_start_test_params_t *test) { /* * Interrupt execution if query_lookup() or query_done() is called. */ - ns_hook_t query_hooks[NS_QUERY_HOOKS_COUNT + 1] = { - [NS_QUERY_LOOKUP_BEGIN] = { - .callback = ns_test_hook_catch_call, - .callback_data = NULL, - }, - [NS_QUERY_DONE_BEGIN] = { - .callback = ns_test_hook_catch_call, - .callback_data = NULL, - }, + ns_hook_t hook = { + .callback = ns_test_hook_catch_call, }; - ns__hook_table = query_hooks; + ns_hooktable_t query_hooks; + + ns_hooktable_init(&query_hooks); + ns_hook_add(&query_hooks, NS_QUERY_LOOKUP_BEGIN, &hook); + ns_hook_add(&query_hooks, NS_QUERY_DONE_BEGIN, &hook); + + ns_hooktable_reset(&query_hooks); /* * Construct a query context using the supplied parameters. @@ -604,18 +602,6 @@ main(void) { return (cmocka_run_group_tests(tests, NULL, NULL)); } -#else - -#include - -int -main(void) { - printf("1..0 # Skipped: libns hooks not enabled\n"); - return (0); -} - -#endif /* NS_HOOKS_ENABLE */ - #else /* HAVE_CMOCKA */ #include diff --git a/util/copyrights b/util/copyrights index 0622c2c9de6..3845fe4f59b 100644 --- a/util/copyrights +++ b/util/copyrights @@ -2480,8 +2480,9 @@ ./lib/ns/Kyuafile X 2017,2018 ./lib/ns/api X 2017,2018 ./lib/ns/client.c C 2017,2018 -./lib/ns/hooks.h C 2017,2018 +./lib/ns/hooks.c C 2018 ./lib/ns/include/ns/client.h C 2017,2018 +./lib/ns/include/ns/hooks.h C 2017,2018 ./lib/ns/include/ns/interfacemgr.h C 2017,2018 ./lib/ns/include/ns/lib.h C 2017,2018 ./lib/ns/include/ns/listenlist.h C 2017,2018