]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
add zone-specific plugin instance
authorColin Vidal <colin@isc.org>
Tue, 27 May 2025 09:46:21 +0000 (11:46 +0200)
committerColin Vidal <colin@isc.org>
Tue, 9 Sep 2025 07:42:34 +0000 (09:42 +0200)
The zone object now has its own hooktable and plugins, which are
initialized during zone initialization.

bin/named/include/named/server.h
bin/named/include/named/zoneconf.h
bin/named/server.c
bin/named/zoneconf.c
lib/dns/include/dns/zone.h
lib/dns/zone.c
lib/ns/hooks.c
lib/ns/include/ns/hooks.h
lib/ns/query.c

index 7be62eecc7015534325c9232e2c8768067ada1df..7d0713b78dabbdb4bf96fc87392392143c1b35fb 100644 (file)
@@ -31,6 +31,8 @@
 #include <dns/stats.h>
 #include <dns/types.h>
 
+#include <isccfg/cfg.h>
+
 #include <ns/interfacemgr.h>
 #include <ns/server.h>
 #include <ns/stats.h>
@@ -407,3 +409,13 @@ named_server_togglememprof(isc_lex_t *lex);
  */
 const char *
 named_server_getmemprof(void);
+
+/*%
+ * Helper callback function to register a plugin, called via
+ * cfg_pluginlist_foreach(). Note: if registration of any
+ * plugin fails, subsequent ones will not be attempted.
+ */
+isc_result_t
+named_register_one_plugin(const cfg_obj_t *config, const cfg_obj_t *obj,
+                         const char *plugin_path, const char *parameters,
+                         void *callback_data);
index ed0735ec516cc66eeb8c55b524e2caf7dd146499..ff5a326832e8613c4b69a76e15bfcc10025b6880 100644 (file)
@@ -81,3 +81,16 @@ named_zone_templateopts(const cfg_obj_t *config, const cfg_obj_t *zoptions);
  * the template options and return them. If no such template is found,
  * return NULL.
  */
+
+isc_result_t
+named_zone_loadplugins(dns_zone_t *zone, const cfg_obj_t *config,
+                      const cfg_obj_t *zoptions);
+/*%<
+ * Load plugins that should run for this specific zone. Take care of cleaning
+ * up any pre-existing plugins first, if the zone is re-used.
+ *
+ * Require:
+ * \li 'zone' to be a valid zone
+ * \li 'config' to be a valid named.conf configuration tree
+ * \li 'zoptions' to be a valid zone configuration tree
+ */
index 5629508a486ca28e4eb202e9a4dfe331a6e15299..0a4f60beea11261303e9e296cd5c7e85bae3a863 100644 (file)
@@ -3712,18 +3712,13 @@ create_mapped_acl(void) {
        return result;
 }
 
-/*%
- * A callback for the cfg_pluginlist_foreach() call in configure_view() below.
- * If registering any plugin fails, registering subsequent ones is not
- * attempted.
- */
-static isc_result_t
-register_one_plugin(const cfg_obj_t *config, const cfg_obj_t *obj,
-                   const char *plugin_path, const char *parameters,
-                   void *callback_data) {
-       dns_view_t *view = callback_data;
+isc_result_t
+named_register_one_plugin(const cfg_obj_t *config, const cfg_obj_t *obj,
+                         const char *plugin_path, const char *parameters,
+                         void *callback_data) {
        char full_path[PATH_MAX];
        isc_result_t result;
+       ns_hook_data_t *hookdata = callback_data;
 
        result = ns_plugin_expandpath(plugin_path, full_path,
                                      sizeof(full_path));
@@ -3738,7 +3733,7 @@ register_one_plugin(const cfg_obj_t *config, const cfg_obj_t *obj,
 
        result = ns_plugin_register(full_path, parameters, config,
                                    cfg_obj_file(obj), cfg_obj_line(obj),
-                                   isc_g_mctx, named_g_aclconfctx, view);
+                                   isc_g_mctx, named_g_aclconfctx, hookdata);
        if (result != ISC_R_SUCCESS) {
                isc_log_write(NAMED_LOGCATEGORY_GENERAL, NAMED_LOGMODULE_SERVER,
                              ISC_LOG_ERROR,
@@ -5422,16 +5417,20 @@ configure_view(dns_view_t *view, dns_viewlist_t *viewlist, cfg_obj_t *config,
        }
 
        if (plugin_list != NULL) {
+               ns_hook_data_t hookdata = {};
+
                INSIST(view->hooktable == NULL);
-               CHECK(ns_hooktable_create(view->mctx,
-                                         (ns_hooktable_t **)&view->hooktable));
+               ns_hooktable_create(view->mctx, &hookdata.hooktable);
+               view->hooktable = hookdata.hooktable;
                view->hooktable_free = ns_hooktable_free;
 
-               ns_plugins_create(view->mctx, (ns_plugins_t **)&view->plugins);
+               ns_plugins_create(view->mctx, &hookdata.plugins);
+               view->plugins = hookdata.plugins;
                view->plugins_free = ns_plugins_free;
 
                CHECK(cfg_pluginlist_foreach(config, plugin_list,
-                                            register_one_plugin, view));
+                                            named_register_one_plugin,
+                                            &hookdata));
        }
 
        /*
@@ -6661,6 +6660,8 @@ configure_zone(const cfg_obj_t *config, const cfg_obj_t *zconfig,
                dns_zone_rekey(zone, fullsign, false);
        }
 
+       result = named_zone_loadplugins(zone, config, zoptions);
+
 cleanup:
        if (zone != NULL) {
                dns_zone_detach(&zone);
index 2ebbb94843e58ff8a75f5246db9158180ccce19d..ab7dad50d89d283cb540b4477fe69b3f8dfaae47 100644 (file)
@@ -43,6 +43,7 @@
 #include <dns/zone.h>
 
 #include <ns/client.h>
+#include <ns/hooks.h>
 
 #include <named/config.h>
 #include <named/globals.h>
@@ -2097,3 +2098,42 @@ named_zone_templateopts(const cfg_obj_t *config, const cfg_obj_t *zoptions) {
 
        return NULL;
 }
+
+isc_result_t
+named_zone_loadplugins(dns_zone_t *zone, const cfg_obj_t *config,
+                      const cfg_obj_t *zoptions) {
+       isc_result_t result = ISC_R_SUCCESS;
+       const cfg_obj_t *pluginlist = NULL;
+
+       /*
+        * If the zone previously had any loaded plugins, unload them:
+        * they might not be needed anymore, or they might hold state
+        * that's now obsolete.
+        */
+       dns_zone_unloadplugins(zone);
+
+       /*
+        * Load zone-specific plugin instances.
+        */
+       if (zoptions != NULL) {
+               (void)cfg_map_get(zoptions, "plugin", &pluginlist);
+       }
+
+       if (pluginlist != NULL) {
+               ns_hook_data_t hookdata = {};
+               isc_mem_t *zmctx = dns_zone_getmctx(zone);
+
+               ns_hooktable_create(zmctx, &hookdata.hooktable);
+               dns_zone_sethooktable(zone, hookdata.hooktable,
+                                     ns_hooktable_free);
+
+               ns_plugins_create(zmctx, &hookdata.plugins);
+               dns_zone_setplugins(zone, hookdata.plugins, ns_plugins_free);
+
+               result = cfg_pluginlist_foreach(config, pluginlist,
+                                               named_register_one_plugin,
+                                               &hookdata);
+       }
+
+       return result;
+}
index a7ce45596493b8d41cec2c308fa715f9756f7368..88127095483a12fa92b4d19e60e401081383efeb 100644 (file)
@@ -2754,6 +2754,49 @@ dns_zone_getkeystores(dns_zone_t *zone);
  * initialized.
  */
 
+void *
+dns_zone_gethooktable(dns_zone_t *zone);
+/**<
+ * Returns the zone hooktable
+ *
+ * Requires:
+ * \li 'zone' to be a valid zone.
+ */
+
+void
+dns_zone_sethooktable(dns_zone_t *zone, void *hooktable,
+                     void (*hooktable_free)(isc_mem_t *, void **));
+/**<
+ * Initialize zone hooktable and free callback
+ *
+ * Requires:
+ * \li 'zone' to be a valid zone.
+ * \li  'hooktable' to be initialized.
+ * \li  'hooktable_free' to be valid.
+ */
+
+void
+dns_zone_setplugins(dns_zone_t *zone, void *plugins,
+                   void (*plugins_free)(isc_mem_t *, void **));
+/**<
+ * Initialize zone plugins owning list and free callback
+ *
+ * Requires:
+ * \li 'zone' to be a valid zone.
+ * \li  'plugins' to be initialized.
+ * \li  'plugins_free' to be valid.
+ */
+
+void
+dns_zone_unloadplugins(dns_zone_t *zone);
+/**<
+ * Unload all plugins attached to this zone, and free the hooktable as well as
+ * the plugins list.
+ *
+ * Requires:
+ * \li 'zone' to be a valid zone.
+ */
+
 #if DNS_ZONE_TRACE
 #define dns_zone_ref(ptr)   dns_zone__ref(ptr, __func__, __FILE__, __LINE__)
 #define dns_zone_unref(ptr) dns_zone__unref(ptr, __func__, __FILE__, __LINE__)
index b7350b9c2a654d5d45b3ba74a869c48f4434e22c..dd16e4ce6e2052016e8238ee69ed6028b5fd0ef6 100644 (file)
@@ -530,6 +530,14 @@ struct dns_zone {
         */
        dns_skr_t *skr;
        dns_skrbundle_t *skrbundle;
+
+       /*
+        * Plugin-related data structures
+        */
+       void *plugins;
+       void (*plugins_free)(isc_mem_t *, void **);
+       void *hooktable;
+       void (*hooktable_free)(isc_mem_t *, void **);
 };
 
 #define zonediff_init(z, d)                \
@@ -1376,6 +1384,7 @@ zone_free(dns_zone_t *zone) {
        if (zone->gluecachestats != NULL) {
                isc_stats_detach(&zone->gluecachestats);
        }
+       dns_zone_unloadplugins(zone);
 
        /* last stuff */
        ZONEDB_DESTROYLOCK(&zone->dblock);
@@ -24864,3 +24873,48 @@ dns_zone_setrad(dns_zone_t *zone, dns_name_t *name) {
        }
        rcu_read_unlock();
 }
+
+void *
+dns_zone_gethooktable(dns_zone_t *zone) {
+       REQUIRE(DNS_ZONE_VALID(zone));
+       return zone->hooktable;
+}
+
+void
+dns_zone_sethooktable(dns_zone_t *zone, void *hooktable,
+                     void (*hooktable_free)(isc_mem_t *, void **)) {
+       REQUIRE(DNS_ZONE_VALID(zone));
+       REQUIRE(zone->hooktable == NULL);
+       REQUIRE(zone->hooktable_free == NULL);
+
+       zone->hooktable = hooktable;
+       zone->hooktable_free = hooktable_free;
+}
+
+void
+dns_zone_setplugins(dns_zone_t *zone, void *plugins,
+                   void (*plugins_free)(isc_mem_t *, void **)) {
+       REQUIRE(DNS_ZONE_VALID(zone));
+       REQUIRE(zone->plugins == NULL);
+       REQUIRE(zone->plugins_free == NULL);
+
+       zone->plugins = plugins;
+       zone->plugins_free = plugins_free;
+}
+
+void
+dns_zone_unloadplugins(dns_zone_t *zone) {
+       if (zone->hooktable != NULL) {
+               INSIST(zone->hooktable_free);
+               zone->hooktable_free(zone->mctx, &zone->hooktable);
+               INSIST(zone->hooktable == NULL);
+               zone->hooktable_free = NULL;
+       }
+
+       if (zone->plugins != NULL) {
+               INSIST(zone->plugins_free);
+               zone->plugins_free(zone->mctx, &zone->plugins);
+               INSIST(zone->plugins == NULL);
+               zone->plugins_free = NULL;
+       }
+}
index 4fd2aae6bbecd01a1ae4fd95da0f993ca27344b7..9c7957e81890d0f7c8a9886158299e78da60a7a2 100644 (file)
@@ -224,12 +224,12 @@ unload_plugin(ns_plugin_t **pluginp) {
 isc_result_t
 ns_plugin_register(const char *modpath, const char *parameters, const void *cfg,
                   const char *cfg_file, unsigned long cfg_line,
-                  isc_mem_t *mctx, void *actx, dns_view_t *view) {
+                  isc_mem_t *mctx, void *actx, ns_hook_data_t *hookdata) {
        isc_result_t result;
        ns_plugin_t *plugin = NULL;
 
        REQUIRE(mctx != NULL);
-       REQUIRE(view != NULL);
+       REQUIRE(hookdata != NULL);
 
        isc_log_write(NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_HOOKS, ISC_LOG_INFO,
                      "loading plugin '%s'", modpath);
@@ -240,9 +240,9 @@ ns_plugin_register(const char *modpath, const char *parameters, const void *cfg,
                      "registering plugin '%s'", modpath);
 
        CHECK(plugin->register_func(parameters, cfg, cfg_file, cfg_line, mctx,
-                                   actx, view->hooktable, &plugin->inst));
+                                   actx, hookdata->hooktable, &plugin->inst));
 
-       ISC_LIST_APPEND(*(ns_plugins_t *)view->plugins, plugin, link);
+       ISC_LIST_APPEND(*hookdata->plugins, plugin, link);
 
 cleanup:
        if (result != ISC_R_SUCCESS && plugin != NULL) {
@@ -281,7 +281,7 @@ ns_hooktable_init(ns_hooktable_t *hooktable) {
        }
 }
 
-isc_result_t
+void
 ns_hooktable_create(isc_mem_t *mctx, ns_hooktable_t **tablep) {
        ns_hooktable_t *hooktable = NULL;
 
@@ -292,8 +292,6 @@ ns_hooktable_create(isc_mem_t *mctx, ns_hooktable_t **tablep) {
        ns_hooktable_init(hooktable);
 
        *tablep = hooktable;
-
-       return ISC_R_SUCCESS;
 }
 
 void
index c5addf2c829ff3423bb6ad07d660b48aa6492524..ee371dfdc10add320e8211420ab026e57b4ba09a 100644 (file)
@@ -447,6 +447,15 @@ typedef struct ns_hook_resume {
        void        *arg;        /* argument to pass to the callback */
 } ns_hook_resume_t;
 
+/*
+ * Wrapper struct holding hook/plugins owning data structures used and owned by
+ * zones and views having registered plugins.
+ */
+typedef struct ns_hook_data {
+       ns_hooktable_t *hooktable;
+       ns_plugins_t   *plugins;
+} ns_hook_data_t;
+
 /*
  * Plugin API version
  *
@@ -535,7 +544,7 @@ ns_plugin_expandpath(const char *src, char *dst, size_t dstsize);
 isc_result_t
 ns_plugin_register(const char *modpath, const char *parameters, const void *cfg,
                   const char *cfg_file, unsigned long cfg_line,
-                  isc_mem_t *mctx, void *actx, dns_view_t *view);
+                  isc_mem_t *mctx, void *actx, ns_hook_data_t *hookdata);
 /*%<
  * Load the plugin module specified from the file 'modpath', and
  * register an instance using 'parameters'.
@@ -603,7 +612,7 @@ ns_hooktable_init(ns_hooktable_t *hooktable);
  * Initialize a hook table.
  */
 
-isc_result_t
+void
 ns_hooktable_create(isc_mem_t *mctx, ns_hooktable_t **tablep);
 /*%<
  * Allocate and initialize a hook table.
index 99f742a29291cff7d828130a6274110efd1071e8..ede3245be1e0bc13d2888c27dfff4400918e6473 100644 (file)
@@ -259,18 +259,80 @@ acquire_recursionquota(ns_client_t *client);
 static void
 release_recursionquota(ns_client_t *client);
 
-/*
- * Return the hooktable in use with 'qctx', or if there isn't one
- * set, return the default hooktable.
- */
+static ns_hookresult_t
+ns__query_callhook(uint8_t id, query_ctx_t *qctx, isc_result_t *result,
+                  ns_hooktable_t *hooktab) {
+       isc_result_t hookresult = *result;
+       ns_hook_t *hook;
+
+       if (hooktab == NULL) {
+               return NS_HOOK_CONTINUE;
+       }
+
+       hook = ISC_LIST_HEAD((*hooktab)[id]);
+       while (hook != NULL) {
+               ns_hook_action_t func = hook->action;
+               void *data = hook->action_data;
+
+               INSIST(func != NULL);
+
+               switch (func(qctx, data, &hookresult)) {
+               case NS_HOOK_CONTINUE:
+                       hook = ISC_LIST_NEXT(hook, link);
+                       break;
+               case NS_HOOK_RETURN:
+                       *result = hookresult;
+                       return NS_HOOK_RETURN;
+               default:
+                       UNREACHABLE();
+               }
+       }
+
+       return NS_HOOK_CONTINUE;
+}
+
+static void
+ns__query_callhook_noreturn(uint8_t id, query_ctx_t *qctx,
+                           ns_hooktable_t *hooktab) {
+       ns_hook_t *hook;
+       isc_result_t dummyres;
+
+       if (hooktab == NULL) {
+               return;
+       }
+
+       hook = ISC_LIST_HEAD((*hooktab)[id]);
+       while (hook != NULL) {
+               ns_hook_action_t func = hook->action;
+               void *data = hook->action_data;
+
+               INSIST(func != NULL);
+
+               func(qctx, data, &dummyres);
+               hook = ISC_LIST_NEXT(hook, link);
+       }
+}
+
 static ns_hooktable_t *
-get_hooktab(query_ctx_t *qctx) {
-       if (qctx == NULL || qctx->view == NULL || qctx->view->hooktable == NULL)
-       {
-               return ns__hook_table;
+ns__zone_hooktab(query_ctx_t *qctx) {
+       ns_hooktable_t *hooktab = NULL;
+
+       if (qctx && qctx->zone) {
+               hooktab = dns_zone_gethooktable(qctx->zone);
+       }
+
+       return hooktab;
+}
+
+static ns_hooktable_t *
+ns__view_hooktab(query_ctx_t *qctx) {
+       ns_hooktable_t *hooktab = NULL;
+
+       if (qctx && qctx->view) {
+               hooktab = qctx->view->hooktable;
        }
 
-       return qctx->view->hooktable;
+       return hooktab;
 }
 
 /*
@@ -283,28 +345,22 @@ get_hooktab(query_ctx_t *qctx) {
  * is a macro instead of a static function; it needs to be able to use
  * 'goto cleanup' regardless of the return value.)
  */
-#define CALL_HOOK(_id, _qctx)                                       \
-       do {                                                        \
-               isc_result_t _res = result;                         \
-               ns_hooktable_t *_tab = get_hooktab(_qctx);          \
-               ns_hook_t *_hook;                                   \
-               _hook = ISC_LIST_HEAD((*_tab)[_id]);                \
-               while (_hook != NULL) {                             \
-                       ns_hook_action_t _func = _hook->action;     \
-                       void *_data = _hook->action_data;           \
-                       INSIST(_func != NULL);                      \
-                       switch (_func(_qctx, _data, &_res)) {       \
-                       case NS_HOOK_CONTINUE:                      \
-                               _hook = ISC_LIST_NEXT(_hook, link); \
-                               break;                              \
-                       case NS_HOOK_RETURN:                        \
-                               result = _res;                      \
-                               goto cleanup;                       \
-                       default:                                    \
-                               UNREACHABLE();                      \
-                       }                                           \
-               }                                                   \
-       } while (false)
+#define CALL_HOOK(_id, _qctx)                                              \
+       if (ns__query_callhook(_id, _qctx, &result,                        \
+                              ns__zone_hooktab(_qctx)) == NS_HOOK_RETURN) \
+       {                                                                  \
+               goto cleanup;                                              \
+       }                                                                  \
+       if (ns__query_callhook(_id, _qctx, &result,                        \
+                              ns__view_hooktab(_qctx)) == NS_HOOK_RETURN) \
+       {                                                                  \
+               goto cleanup;                                              \
+       }                                                                  \
+       if (ns__query_callhook(_id, _qctx, &result, ns__hook_table) ==     \
+           NS_HOOK_RETURN)                                                \
+       {                                                                  \
+               goto cleanup;                                              \
+       }
 
 /*
  * Call the specified hook function in every configured module that
@@ -315,20 +371,10 @@ get_hooktab(query_ctx_t *qctx) {
  * (This could be implemented as a static void function, but is left as a
  * macro for symmetry with CALL_HOOK above.)
  */
-#define CALL_HOOK_NORETURN(_id, _qctx)                          \
-       do {                                                    \
-               isc_result_t _res;                              \
-               ns_hooktable_t *_tab = get_hooktab(_qctx);      \
-               ns_hook_t *_hook;                               \
-               _hook = ISC_LIST_HEAD((*_tab)[_id]);            \
-               while (_hook != NULL) {                         \
-                       ns_hook_action_t _func = _hook->action; \
-                       void *_data = _hook->action_data;       \
-                       INSIST(_func != NULL);                  \
-                       _func(_qctx, _data, &_res);             \
-                       _hook = ISC_LIST_NEXT(_hook, link);     \
-               }                                               \
-       } while (false)
+#define CALL_HOOK_NORETURN(_id, _qctx)                                    \
+       ns__query_callhook_noreturn(_id, _qctx, ns__zone_hooktab(_qctx)); \
+       ns__query_callhook_noreturn(_id, _qctx, ns__view_hooktab(_qctx)); \
+       ns__query_callhook_noreturn(_id, _qctx, ns__hook_table);
 
 /*
  * The functions defined below implement the query logic that previously lived