#include <dns/stats.h>
#include <dns/types.h>
+#include <isccfg/cfg.h>
+
#include <ns/interfacemgr.h>
#include <ns/server.h>
#include <ns/stats.h>
*/
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);
* 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
+ */
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));
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,
}
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));
}
/*
dns_zone_rekey(zone, fullsign, false);
}
+ result = named_zone_loadplugins(zone, config, zoptions);
+
cleanup:
if (zone != NULL) {
dns_zone_detach(&zone);
#include <dns/zone.h>
#include <ns/client.h>
+#include <ns/hooks.h>
#include <named/config.h>
#include <named/globals.h>
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;
+}
* 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__)
*/
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) \
if (zone->gluecachestats != NULL) {
isc_stats_detach(&zone->gluecachestats);
}
+ dns_zone_unloadplugins(zone);
/* last stuff */
ZONEDB_DESTROYLOCK(&zone->dblock);
}
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;
+ }
+}
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);
"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) {
}
}
-isc_result_t
+void
ns_hooktable_create(isc_mem_t *mctx, ns_hooktable_t **tablep) {
ns_hooktable_t *hooktable = NULL;
ns_hooktable_init(hooktable);
*tablep = hooktable;
-
- return ISC_R_SUCCESS;
}
void
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
*
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'.
* 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.
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;
}
/*
* 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
* (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