* Shut down all dyndb instances.
*/
dns_dyndb_cleanup(false);
+ ns_hookmodule_cleanup();
/*
* Parse the global default pseudo-config file.
}
dns_dyndb_cleanup(true);
+ ns_hookmodule_cleanup();
while ((nsc = ISC_LIST_HEAD(server->cachelist)) != NULL) {
ISC_LIST_UNLINK(server->cachelist, nsc, link);
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
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
"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);
"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);
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')
* '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:
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
--- /dev/null
+/*
+ * 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;
+ 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);
+}
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 =
#ifndef NS_HOOKS_H
#define NS_HOOKS_H 1
-#ifdef NS_HOOKS_ENABLE
-
/*! \file */
#include <stdbool.h>
+#include <isc/list.h>
#include <isc/result.h>
/*
* 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 */
#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);
{ "ns/xfer-in", 0 },
{ "ns/xfer-out", 0 },
{ "ns/notify", 0 },
+ { "ns/hooks", 0 },
{ NULL, 0 }
};
#include <ns/client.h>
#include <ns/interfacemgr.h>
+#include <ns/hooks.h>
#include <ns/log.h>
#include <ns/server.h>
#include <ns/sortlist.h>
#include <ns/stats.h>
#include <ns/xfrout.h>
-#include "hooks.h"
-
#if 0
/*
* It has been recommended that DNS64 be changed to return excluded
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
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);
(!WANTDNSSEC(client) || sigrdataset == NULL ||
!dns_rdataset_isassociated(sigrdataset)))))
goto addname;
+
if (additionaltype ==
dns_rdatasetadditional_fromcache &&
(DNS_TRUST_PENDING(rdataset->trust) ||
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.
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;
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));
}
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) {
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.
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.
*/
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)
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.
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
query_delegation(query_ctx_t *qctx) {
isc_result_t result;
+ PROCESS_HOOK(NS_QUERY_DELEGATION_BEGIN, qctx);
+
qctx->authoritative = false;
if (qctx->is_zone) {
*/
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
uint32_t ttl;
isc_result_t result;
+ PROCESS_HOOK(NS_QUERY_NXDOMAIN_BEGIN, qctx);
+
INSIST(qctx->is_zone || REDIRECT(qctx->client));
if (!empty_wild) {
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.
*/
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
*/
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)
{
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;
+ }
}
qctx->result = ISC_R_FAILURE;
}
+ PROCESS_HOOK(NS_QUERY_DONE_SEND, qctx);
+
query_send(qctx->client);
ns_client_detach(&qctx->client);
#include <isc/util.h>
-#ifdef NS_HOOKS_ENABLE
-
#include <stdio.h>
#include <string.h>
#include <unistd.h>
return (cmocka_run_group_tests(tests, NULL, NULL));
}
-#else
-
-#include <stdio.h>
-
-int
-main(void) {
- printf("1..0 # Skipped: libns hooks not enabled\n");
- return (0);
-}
-
-#endif /* NS_HOOKS_ENABLE */
-
#else /* HAVE_CMOCKA */
#include <stdio.h>
#include <isc/util.h>
-#ifdef NS_HOOKS_ENABLE
-
#include <stdio.h>
#include <string.h>
#include <unistd.h>
return (cmocka_run_group_tests(tests, NULL, NULL));
}
-#else
-
-#include <stdio.h>
-
-int
-main(void) {
- printf("1..0 # Skipped: libns hooks not enabled\n");
- return (0);
-}
-
-#endif /* NS_HOOKS_ENABLE */
-
#else /* HAVE_CMOCKA */
#include <stdio.h>
#include <config.h>
-#ifdef NS_HOOKS_ENABLE
-
#include <inttypes.h>
#include <stdbool.h>
#include <stdlib.h>
#include <dns/zone.h>
#include <ns/client.h>
+#include <ns/hooks.h>
#include <ns/interfacemgr.h>
#include <ns/server.h>
-#include "../hooks.h"
-
#include "nstest.h"
isc_mem_t *mctx = NULL;
CHECK(ISC_R_FAILURE);
}
- ns__hook_table = NULL;
-
return (ISC_R_SUCCESS);
cleanup:
*/
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);
* 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);
isc_stdio_close(f);
return (result);
}
-#endif
#include <isc/util.h>
-#ifdef NS_HOOKS_ENABLE
-
#include <inttypes.h>
#include <stdbool.h>
#include <stdlib.h>
#include <dns/badcache.h>
#include <dns/view.h>
+
#include <ns/client.h>
+#include <ns/hooks.h>
#include <ns/query.h>
#include <isc/util.h>
-#include "../hooks.h"
-
#include "nstest.h"
static int
/*
* 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.
/*
* 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.
return (cmocka_run_group_tests(tests, NULL, NULL));
}
-#else
-
-#include <stdio.h>
-
-int
-main(void) {
- printf("1..0 # Skipped: libns hooks not enabled\n");
- return (0);
-}
-
-#endif /* NS_HOOKS_ENABLE */
-
#else /* HAVE_CMOCKA */
#include <stdio.h>
./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