xref: /netbsd-src/external/mpl/bind/dist/lib/ns/hooks.c (revision bcda20f65a8566e103791ec395f7f499ef322704)
1*bcda20f6Schristos /*	$NetBSD: hooks.c,v 1.11 2025/01/26 16:25:45 christos Exp $	*/
266331fe0Schristos 
366331fe0Schristos /*
466331fe0Schristos  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
566331fe0Schristos  *
68596601aSchristos  * SPDX-License-Identifier: MPL-2.0
78596601aSchristos  *
866331fe0Schristos  * This Source Code Form is subject to the terms of the Mozilla Public
966331fe0Schristos  * License, v. 2.0. If a copy of the MPL was not distributed with this
10fce770bdSchristos  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
1166331fe0Schristos  *
1266331fe0Schristos  * See the COPYRIGHT file distributed with this work for additional
1366331fe0Schristos  * information regarding copyright ownership.
1466331fe0Schristos  */
1566331fe0Schristos 
1666331fe0Schristos /*! \file */
1766331fe0Schristos 
18684ad4e5Schristos #include <errno.h>
19684ad4e5Schristos #include <stdio.h>
2066331fe0Schristos #include <string.h>
2166331fe0Schristos 
22684ad4e5Schristos #include <isc/errno.h>
2366331fe0Schristos #include <isc/list.h>
2466331fe0Schristos #include <isc/log.h>
2566331fe0Schristos #include <isc/mem.h>
2666331fe0Schristos #include <isc/mutex.h>
2766331fe0Schristos #include <isc/result.h>
2866331fe0Schristos #include <isc/types.h>
295606745fSchristos #include <isc/util.h>
30*bcda20f6Schristos #include <isc/uv.h>
3166331fe0Schristos 
3266331fe0Schristos #include <dns/view.h>
3366331fe0Schristos 
3466331fe0Schristos #include <ns/hooks.h>
3566331fe0Schristos #include <ns/log.h>
3666331fe0Schristos #include <ns/query.h>
3766331fe0Schristos 
3866331fe0Schristos #define CHECK(op)                              \
3966331fe0Schristos 	do {                                   \
4066331fe0Schristos 		result = (op);                 \
4166331fe0Schristos 		if (result != ISC_R_SUCCESS) { \
4266331fe0Schristos 			goto cleanup;          \
4366331fe0Schristos 		}                              \
4453cc4e50Srillig 	} while (0)
4566331fe0Schristos 
4666331fe0Schristos struct ns_plugin {
4766331fe0Schristos 	isc_mem_t *mctx;
48bb5aa156Schristos 	uv_lib_t handle;
4966331fe0Schristos 	void *inst;
5066331fe0Schristos 	char *modpath;
5166331fe0Schristos 	ns_plugin_check_t *check_func;
5266331fe0Schristos 	ns_plugin_register_t *register_func;
5366331fe0Schristos 	ns_plugin_destroy_t *destroy_func;
5466331fe0Schristos 	LINK(ns_plugin_t) link;
5566331fe0Schristos };
5666331fe0Schristos 
5766331fe0Schristos static ns_hooklist_t default_hooktable[NS_HOOKPOINTS_COUNT];
58bb5aa156Schristos ns_hooktable_t *ns__hook_table = &default_hooktable;
5966331fe0Schristos 
60684ad4e5Schristos isc_result_t
61684ad4e5Schristos ns_plugin_expandpath(const char *src, char *dst, size_t dstsize) {
62684ad4e5Schristos 	int result;
63684ad4e5Schristos 
64684ad4e5Schristos 	/*
65684ad4e5Schristos 	 * On Unix systems, differentiate between paths and filenames.
66684ad4e5Schristos 	 */
67684ad4e5Schristos 	if (strchr(src, '/') != NULL) {
68684ad4e5Schristos 		/*
69684ad4e5Schristos 		 * 'src' is an absolute or relative path.  Copy it verbatim.
70684ad4e5Schristos 		 */
71684ad4e5Schristos 		result = snprintf(dst, dstsize, "%s", src);
72684ad4e5Schristos 	} else {
73684ad4e5Schristos 		/*
74684ad4e5Schristos 		 * 'src' is a filename.  Prepend default plugin directory path.
75684ad4e5Schristos 		 */
76684ad4e5Schristos 		result = snprintf(dst, dstsize, "%s/%s", NAMED_PLUGINDIR, src);
77684ad4e5Schristos 	}
78684ad4e5Schristos 
79684ad4e5Schristos 	if (result < 0) {
80*bcda20f6Schristos 		return isc_errno_toresult(errno);
81684ad4e5Schristos 	} else if ((size_t)result >= dstsize) {
82*bcda20f6Schristos 		return ISC_R_NOSPACE;
83684ad4e5Schristos 	} else {
84*bcda20f6Schristos 		return ISC_R_SUCCESS;
85684ad4e5Schristos 	}
86684ad4e5Schristos }
87684ad4e5Schristos 
8866331fe0Schristos static isc_result_t
89bb5aa156Schristos load_symbol(uv_lib_t *handle, const char *modpath, const char *symbol_name,
905606745fSchristos 	    void **symbolp) {
9166331fe0Schristos 	void *symbol = NULL;
92bb5aa156Schristos 	int r;
9366331fe0Schristos 
9466331fe0Schristos 	REQUIRE(handle != NULL);
9566331fe0Schristos 	REQUIRE(symbolp != NULL && *symbolp == NULL);
9666331fe0Schristos 
97bb5aa156Schristos 	r = uv_dlsym(handle, symbol_name, &symbol);
98bb5aa156Schristos 	if (r != 0) {
99bb5aa156Schristos 		const char *errmsg = uv_dlerror(handle);
10066331fe0Schristos 		if (errmsg == NULL) {
10166331fe0Schristos 			errmsg = "returned function pointer is NULL";
10266331fe0Schristos 		}
10366331fe0Schristos 		isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
10466331fe0Schristos 			      NS_LOGMODULE_HOOKS, ISC_LOG_ERROR,
10566331fe0Schristos 			      "failed to look up symbol %s in "
10666331fe0Schristos 			      "plugin '%s': %s",
10766331fe0Schristos 			      symbol_name, modpath, errmsg);
108*bcda20f6Schristos 		return ISC_R_FAILURE;
10966331fe0Schristos 	}
11066331fe0Schristos 
11166331fe0Schristos 	*symbolp = symbol;
11266331fe0Schristos 
113*bcda20f6Schristos 	return ISC_R_SUCCESS;
11466331fe0Schristos }
11566331fe0Schristos 
116bb5aa156Schristos static void
117bb5aa156Schristos unload_plugin(ns_plugin_t **pluginp);
118bb5aa156Schristos 
11966331fe0Schristos static isc_result_t
12066331fe0Schristos load_plugin(isc_mem_t *mctx, const char *modpath, ns_plugin_t **pluginp) {
12166331fe0Schristos 	isc_result_t result;
12266331fe0Schristos 	ns_plugin_t *plugin = NULL;
12366331fe0Schristos 	ns_plugin_version_t *version_func = NULL;
124bb5aa156Schristos 	int version;
125bb5aa156Schristos 	int r;
12666331fe0Schristos 
12766331fe0Schristos 	REQUIRE(pluginp != NULL && *pluginp == NULL);
12866331fe0Schristos 
129bb5aa156Schristos 	plugin = isc_mem_get(mctx, sizeof(*plugin));
130*bcda20f6Schristos 	*plugin = (ns_plugin_t){
131*bcda20f6Schristos 		.modpath = isc_mem_strdup(mctx, modpath),
132*bcda20f6Schristos 	};
13366331fe0Schristos 
134*bcda20f6Schristos 	isc_mem_attach(mctx, &plugin->mctx);
135bb5aa156Schristos 
136bb5aa156Schristos 	ISC_LINK_INIT(plugin, link);
137bb5aa156Schristos 
138bb5aa156Schristos 	r = uv_dlopen(modpath, &plugin->handle);
139bb5aa156Schristos 	if (r != 0) {
140bb5aa156Schristos 		const char *errmsg = uv_dlerror(&plugin->handle);
14166331fe0Schristos 		if (errmsg == NULL) {
14266331fe0Schristos 			errmsg = "unknown error";
14366331fe0Schristos 		}
14466331fe0Schristos 		isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
14566331fe0Schristos 			      NS_LOGMODULE_HOOKS, ISC_LOG_ERROR,
1465606745fSchristos 			      "failed to dlopen() plugin '%s': %s", modpath,
1475606745fSchristos 			      errmsg);
148bb5aa156Schristos 		CHECK(ISC_R_FAILURE);
14966331fe0Schristos 	}
15066331fe0Schristos 
151bb5aa156Schristos 	CHECK(load_symbol(&plugin->handle, modpath, "plugin_version",
15266331fe0Schristos 			  (void **)&version_func));
15366331fe0Schristos 
15466331fe0Schristos 	version = version_func();
15566331fe0Schristos 	if (version < (NS_PLUGIN_VERSION - NS_PLUGIN_AGE) ||
15666331fe0Schristos 	    version > NS_PLUGIN_VERSION)
15766331fe0Schristos 	{
15866331fe0Schristos 		isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
15966331fe0Schristos 			      NS_LOGMODULE_HOOKS, ISC_LOG_ERROR,
1605606745fSchristos 			      "plugin API version mismatch: %d/%d", version,
1615606745fSchristos 			      NS_PLUGIN_VERSION);
16266331fe0Schristos 		CHECK(ISC_R_FAILURE);
16366331fe0Schristos 	}
16466331fe0Schristos 
165bb5aa156Schristos 	CHECK(load_symbol(&plugin->handle, modpath, "plugin_check",
166bb5aa156Schristos 			  (void **)&plugin->check_func));
167bb5aa156Schristos 	CHECK(load_symbol(&plugin->handle, modpath, "plugin_register",
168bb5aa156Schristos 			  (void **)&plugin->register_func));
169bb5aa156Schristos 	CHECK(load_symbol(&plugin->handle, modpath, "plugin_destroy",
170bb5aa156Schristos 			  (void **)&plugin->destroy_func));
17166331fe0Schristos 
17266331fe0Schristos 	*pluginp = plugin;
17366331fe0Schristos 
174*bcda20f6Schristos 	return ISC_R_SUCCESS;
17566331fe0Schristos 
17666331fe0Schristos cleanup:
177bb5aa156Schristos 	isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_HOOKS,
178bb5aa156Schristos 		      ISC_LOG_ERROR,
179bb5aa156Schristos 		      "failed to dynamically load plugin '%s': %s", modpath,
1805606745fSchristos 		      isc_result_totext(result));
18166331fe0Schristos 
182bb5aa156Schristos 	unload_plugin(&plugin);
18366331fe0Schristos 
184*bcda20f6Schristos 	return result;
18566331fe0Schristos }
18666331fe0Schristos 
18766331fe0Schristos static void
18866331fe0Schristos unload_plugin(ns_plugin_t **pluginp) {
18966331fe0Schristos 	ns_plugin_t *plugin = NULL;
19066331fe0Schristos 
19166331fe0Schristos 	REQUIRE(pluginp != NULL && *pluginp != NULL);
19266331fe0Schristos 
19366331fe0Schristos 	plugin = *pluginp;
19466331fe0Schristos 	*pluginp = NULL;
19566331fe0Schristos 
1965606745fSchristos 	isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_HOOKS,
1975606745fSchristos 		      ISC_LOG_DEBUG(1), "unloading plugin '%s'",
1985606745fSchristos 		      plugin->modpath);
19966331fe0Schristos 
20066331fe0Schristos 	if (plugin->inst != NULL) {
20166331fe0Schristos 		plugin->destroy_func(&plugin->inst);
20266331fe0Schristos 	}
20366331fe0Schristos 
204bb5aa156Schristos 	uv_dlclose(&plugin->handle);
20566331fe0Schristos 	isc_mem_free(plugin->mctx, plugin->modpath);
20666331fe0Schristos 	isc_mem_putanddetach(&plugin->mctx, plugin, sizeof(*plugin));
20766331fe0Schristos }
20866331fe0Schristos 
20966331fe0Schristos isc_result_t
2105606745fSchristos ns_plugin_register(const char *modpath, const char *parameters, const void *cfg,
2115606745fSchristos 		   const char *cfg_file, unsigned long cfg_line,
21266331fe0Schristos 		   isc_mem_t *mctx, isc_log_t *lctx, void *actx,
2135606745fSchristos 		   dns_view_t *view) {
21466331fe0Schristos 	isc_result_t result;
21566331fe0Schristos 	ns_plugin_t *plugin = NULL;
21666331fe0Schristos 
21766331fe0Schristos 	REQUIRE(mctx != NULL);
21866331fe0Schristos 	REQUIRE(lctx != NULL);
21966331fe0Schristos 	REQUIRE(view != NULL);
22066331fe0Schristos 
2215606745fSchristos 	isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_HOOKS,
2225606745fSchristos 		      ISC_LOG_INFO, "loading plugin '%s'", modpath);
22366331fe0Schristos 
22466331fe0Schristos 	CHECK(load_plugin(mctx, modpath, &plugin));
22566331fe0Schristos 
2265606745fSchristos 	isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_HOOKS,
2275606745fSchristos 		      ISC_LOG_INFO, "registering plugin '%s'", modpath);
22866331fe0Schristos 
2295606745fSchristos 	CHECK(plugin->register_func(parameters, cfg, cfg_file, cfg_line, mctx,
2305606745fSchristos 				    lctx, actx, view->hooktable,
23166331fe0Schristos 				    &plugin->inst));
23266331fe0Schristos 
23366331fe0Schristos 	ISC_LIST_APPEND(*(ns_plugins_t *)view->plugins, plugin, link);
23466331fe0Schristos 
23566331fe0Schristos cleanup:
23666331fe0Schristos 	if (result != ISC_R_SUCCESS && plugin != NULL) {
23766331fe0Schristos 		unload_plugin(&plugin);
23866331fe0Schristos 	}
23966331fe0Schristos 
240*bcda20f6Schristos 	return result;
24166331fe0Schristos }
24266331fe0Schristos 
24366331fe0Schristos isc_result_t
2445606745fSchristos ns_plugin_check(const char *modpath, const char *parameters, const void *cfg,
2455606745fSchristos 		const char *cfg_file, unsigned long cfg_line, isc_mem_t *mctx,
2465606745fSchristos 		isc_log_t *lctx, void *actx) {
24766331fe0Schristos 	isc_result_t result;
24866331fe0Schristos 	ns_plugin_t *plugin = NULL;
24966331fe0Schristos 
25066331fe0Schristos 	CHECK(load_plugin(mctx, modpath, &plugin));
25166331fe0Schristos 
2525606745fSchristos 	result = plugin->check_func(parameters, cfg, cfg_file, cfg_line, mctx,
2535606745fSchristos 				    lctx, actx);
25466331fe0Schristos 
25566331fe0Schristos cleanup:
25666331fe0Schristos 	if (plugin != NULL) {
25766331fe0Schristos 		unload_plugin(&plugin);
25866331fe0Schristos 	}
25966331fe0Schristos 
260*bcda20f6Schristos 	return result;
26166331fe0Schristos }
26266331fe0Schristos 
26366331fe0Schristos void
26466331fe0Schristos ns_hooktable_init(ns_hooktable_t *hooktable) {
26566331fe0Schristos 	int i;
26666331fe0Schristos 
26766331fe0Schristos 	for (i = 0; i < NS_HOOKPOINTS_COUNT; i++) {
26866331fe0Schristos 		ISC_LIST_INIT((*hooktable)[i]);
26966331fe0Schristos 	}
27066331fe0Schristos }
27166331fe0Schristos 
27266331fe0Schristos isc_result_t
27366331fe0Schristos ns_hooktable_create(isc_mem_t *mctx, ns_hooktable_t **tablep) {
27466331fe0Schristos 	ns_hooktable_t *hooktable = NULL;
27566331fe0Schristos 
27666331fe0Schristos 	REQUIRE(tablep != NULL && *tablep == NULL);
27766331fe0Schristos 
27866331fe0Schristos 	hooktable = isc_mem_get(mctx, sizeof(*hooktable));
27966331fe0Schristos 
28066331fe0Schristos 	ns_hooktable_init(hooktable);
28166331fe0Schristos 
28266331fe0Schristos 	*tablep = hooktable;
28366331fe0Schristos 
284*bcda20f6Schristos 	return ISC_R_SUCCESS;
28566331fe0Schristos }
28666331fe0Schristos 
28766331fe0Schristos void
28866331fe0Schristos ns_hooktable_free(isc_mem_t *mctx, void **tablep) {
28966331fe0Schristos 	ns_hooktable_t *table = NULL;
29066331fe0Schristos 	ns_hook_t *hook = NULL, *next = NULL;
29166331fe0Schristos 	int i = 0;
29266331fe0Schristos 
29366331fe0Schristos 	REQUIRE(tablep != NULL && *tablep != NULL);
29466331fe0Schristos 
29566331fe0Schristos 	table = *tablep;
29666331fe0Schristos 	*tablep = NULL;
29766331fe0Schristos 
29866331fe0Schristos 	for (i = 0; i < NS_HOOKPOINTS_COUNT; i++) {
2995606745fSchristos 		for (hook = ISC_LIST_HEAD((*table)[i]); hook != NULL;
300903adeddSchristos 		     hook = next)
301903adeddSchristos 		{
30266331fe0Schristos 			next = ISC_LIST_NEXT(hook, link);
30366331fe0Schristos 			ISC_LIST_UNLINK((*table)[i], hook, link);
30466331fe0Schristos 			if (hook->mctx != NULL) {
3055606745fSchristos 				isc_mem_putanddetach(&hook->mctx, hook,
3065606745fSchristos 						     sizeof(*hook));
30766331fe0Schristos 			}
30866331fe0Schristos 		}
30966331fe0Schristos 	}
31066331fe0Schristos 
31166331fe0Schristos 	isc_mem_put(mctx, table, sizeof(*table));
31266331fe0Schristos }
31366331fe0Schristos 
31466331fe0Schristos void
31566331fe0Schristos ns_hook_add(ns_hooktable_t *hooktable, isc_mem_t *mctx,
3165606745fSchristos 	    ns_hookpoint_t hookpoint, const ns_hook_t *hook) {
31766331fe0Schristos 	ns_hook_t *copy = NULL;
31866331fe0Schristos 
31966331fe0Schristos 	REQUIRE(hooktable != NULL);
32066331fe0Schristos 	REQUIRE(mctx != NULL);
32166331fe0Schristos 	REQUIRE(hookpoint < NS_HOOKPOINTS_COUNT);
32266331fe0Schristos 	REQUIRE(hook != NULL);
32366331fe0Schristos 
32466331fe0Schristos 	copy = isc_mem_get(mctx, sizeof(*copy));
325*bcda20f6Schristos 	*copy = (ns_hook_t){
326*bcda20f6Schristos 		.action = hook->action,
327*bcda20f6Schristos 		.action_data = hook->action_data,
328*bcda20f6Schristos 	};
32966331fe0Schristos 	isc_mem_attach(mctx, &copy->mctx);
33066331fe0Schristos 
33166331fe0Schristos 	ISC_LINK_INIT(copy, link);
33266331fe0Schristos 	ISC_LIST_APPEND((*hooktable)[hookpoint], copy, link);
33366331fe0Schristos }
33466331fe0Schristos 
33566331fe0Schristos void
33666331fe0Schristos ns_plugins_create(isc_mem_t *mctx, ns_plugins_t **listp) {
33766331fe0Schristos 	ns_plugins_t *plugins = NULL;
33866331fe0Schristos 
33966331fe0Schristos 	REQUIRE(listp != NULL && *listp == NULL);
34066331fe0Schristos 
34166331fe0Schristos 	plugins = isc_mem_get(mctx, sizeof(*plugins));
342*bcda20f6Schristos 	*plugins = (ns_plugins_t){ 0 };
34366331fe0Schristos 	ISC_LIST_INIT(*plugins);
34466331fe0Schristos 
34566331fe0Schristos 	*listp = plugins;
34666331fe0Schristos }
34766331fe0Schristos 
34866331fe0Schristos void
34966331fe0Schristos ns_plugins_free(isc_mem_t *mctx, void **listp) {
35066331fe0Schristos 	ns_plugins_t *list = NULL;
35166331fe0Schristos 	ns_plugin_t *plugin = NULL, *next = NULL;
35266331fe0Schristos 
35366331fe0Schristos 	REQUIRE(listp != NULL && *listp != NULL);
35466331fe0Schristos 
35566331fe0Schristos 	list = *listp;
35666331fe0Schristos 	*listp = NULL;
35766331fe0Schristos 
3585606745fSchristos 	for (plugin = ISC_LIST_HEAD(*list); plugin != NULL; plugin = next) {
35966331fe0Schristos 		next = ISC_LIST_NEXT(plugin, link);
36066331fe0Schristos 		ISC_LIST_UNLINK(*list, plugin, link);
36166331fe0Schristos 		unload_plugin(&plugin);
36266331fe0Schristos 	}
36366331fe0Schristos 
36466331fe0Schristos 	isc_mem_put(mctx, list, sizeof(*list));
36566331fe0Schristos }
366