]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
set up hooks.c to enable setting hook points and loading modules
authorEvan Hunt <each@isc.org>
Fri, 3 Aug 2018 21:16:41 +0000 (14:16 -0700)
committerEvan Hunt <each@isc.org>
Thu, 6 Dec 2018 18:29:05 +0000 (10:29 -0800)
- 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

17 files changed:
bin/named/server.c
configure
configure.ac
lib/dns/dyndb.c
lib/dns/include/dns/dyndb.h
lib/ns/Makefile.in
lib/ns/hooks.c [new file with mode: 0644]
lib/ns/include/ns/Makefile.in
lib/ns/include/ns/hooks.h [moved from lib/ns/hooks.h with 61% similarity]
lib/ns/include/ns/log.h
lib/ns/log.c
lib/ns/query.c
lib/ns/tests/listenlist_test.c
lib/ns/tests/notify_test.c
lib/ns/tests/nstest.c
lib/ns/tests/query_test.c
util/copyrights

index 923e25a63bf3d3a3cbef8763bd3bdc2bf4e293bc..9e6c75a15f2c5dbc2ac8022e93cc7789dd135902 100644 (file)
@@ -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);
index 85f07cd4e1f7d601d22b366b4a082d2ab6ebf7e7..5031db85a3e4ffbc17bc879e7e4ad74326eb3ce0 100755 (executable)
--- 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
index df3683ba836a002e44234956d160f0260e17345a..d831bae619e82b6f00e7932f98c8f43a28ac3620 100644 (file)
@@ -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
index 0faf393878fa6086c8a8784c629a0cde6811a8cc..932244a8dbc94ebee999ab7064c70514f352f971 100644 (file)
@@ -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);
 
index d5fedb5fda4d51ba599523633c456f669450575a..d5faf920695a6011cd7bdabe239858496ef4f06b 100644 (file)
@@ -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:
index 51d21377c67bcba8e70ba80428389071e37f5650..2e6c0183da7f3bb97ff1ec5aa3610a76c6a1ce68 100644 (file)
@@ -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 (file)
index 0000000..4b97c5b
--- /dev/null
@@ -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 <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 **)&register_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 **)&register_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);
+}
index 9bfa9f15982c28ebd9dac220a6e32f5d483ffd7a..2009d9a73a87e27be55785a7d41ed6344b99a03a 100644 (file)
@@ -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 =
similarity index 61%
rename from lib/ns/hooks.h
rename to lib/ns/include/ns/hooks.h
index 5c3aa14409fda910fcf2709e20703aff7f684386..0475227750909e64c3f3e1ba1e65635ac98e1a49 100644 (file)
 #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 */
index aab57aca091cf97858e8c6bee4549d06503457b7..24689a652ce7dafec5e719f9b23f645df40d0cfa 100644 (file)
@@ -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);
index f167674b32e65ddbdea283e8c70874277013b73d..3e83d98401fbeb8c90563f1997ad026e936468a1 100644 (file)
@@ -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 }
 };
 
index 64a38e45621a57fc1781c6b0b8dd42340ae17b53..54af7eac7c16802a55ba8cf19dff328fad3e956c 100644 (file)
 
 #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
@@ -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);
index 40415c1d7c6dad344d7e4fe7ad4c8f368a05a4a0..24b5b970f0be7b9931af83c9867fcdfc08339de2 100644 (file)
@@ -20,8 +20,6 @@
 
 #include <isc/util.h>
 
-#ifdef NS_HOOKS_ENABLE
-
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
@@ -126,18 +124,6 @@ main(void) {
 
        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>
index e1e2654ca3882d7140a8c797b72fe3070c8dcb39..b6884ea7df55a906f485f6229733c50e08d4b053 100644 (file)
@@ -20,8 +20,6 @@
 
 #include <isc/util.h>
 
-#ifdef NS_HOOKS_ENABLE
-
 #include <stdio.h>
 #include <string.h>
 #include <unistd.h>
@@ -154,18 +152,6 @@ main(void) {
 
        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>
index 85c641bcd4828f8b7517799129d79db1544501d5..f0a589ed15675f85e177f5fa630e35e61867fe5e 100644 (file)
@@ -13,8 +13,6 @@
 
 #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;
@@ -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
index 21c68e4c7638331ee3a657dbc765d7b5be00ae90..b47b775fdaee42302621c893e767d3436a3da6e4 100644 (file)
@@ -19,8 +19,6 @@
 
 #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
@@ -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 <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>
index 0622c2c9de6b401c5b17b7d81bbb2b4fc787ed48..3845fe4f59bcf03aa97469824de6701333f62036 100644 (file)
 ./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