Home | History | Annotate | Line # | Download | only in ns
hooks.c revision 1.1.1.8
      1 /*	$NetBSD: hooks.c,v 1.1.1.8 2024/02/21 21:54:47 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  *
      6  * SPDX-License-Identifier: MPL-2.0
      7  *
      8  * This Source Code Form is subject to the terms of the Mozilla Public
      9  * License, v. 2.0. If a copy of the MPL was not distributed with this
     10  * file, you can obtain one at https://mozilla.org/MPL/2.0/.
     11  *
     12  * See the COPYRIGHT file distributed with this work for additional
     13  * information regarding copyright ownership.
     14  */
     15 
     16 /*! \file */
     17 
     18 #include <errno.h>
     19 #include <stdio.h>
     20 #include <string.h>
     21 #include <uv.h>
     22 
     23 #include <isc/errno.h>
     24 #include <isc/list.h>
     25 #include <isc/log.h>
     26 #include <isc/mem.h>
     27 #include <isc/mutex.h>
     28 #include <isc/print.h>
     29 #include <isc/result.h>
     30 #include <isc/types.h>
     31 #include <isc/util.h>
     32 
     33 #include <dns/view.h>
     34 
     35 #include <ns/hooks.h>
     36 #include <ns/log.h>
     37 #include <ns/query.h>
     38 
     39 #define CHECK(op)                              \
     40 	do {                                   \
     41 		result = (op);                 \
     42 		if (result != ISC_R_SUCCESS) { \
     43 			goto cleanup;          \
     44 		}                              \
     45 	} while (0)
     46 
     47 struct ns_plugin {
     48 	isc_mem_t *mctx;
     49 	uv_lib_t handle;
     50 	void *inst;
     51 	char *modpath;
     52 	ns_plugin_check_t *check_func;
     53 	ns_plugin_register_t *register_func;
     54 	ns_plugin_destroy_t *destroy_func;
     55 	LINK(ns_plugin_t) link;
     56 };
     57 
     58 static ns_hooklist_t default_hooktable[NS_HOOKPOINTS_COUNT];
     59 ns_hooktable_t *ns__hook_table = &default_hooktable;
     60 
     61 isc_result_t
     62 ns_plugin_expandpath(const char *src, char *dst, size_t dstsize) {
     63 	int result;
     64 
     65 	/*
     66 	 * On Unix systems, differentiate between paths and filenames.
     67 	 */
     68 	if (strchr(src, '/') != NULL) {
     69 		/*
     70 		 * 'src' is an absolute or relative path.  Copy it verbatim.
     71 		 */
     72 		result = snprintf(dst, dstsize, "%s", src);
     73 	} else {
     74 		/*
     75 		 * 'src' is a filename.  Prepend default plugin directory path.
     76 		 */
     77 		result = snprintf(dst, dstsize, "%s/%s", NAMED_PLUGINDIR, src);
     78 	}
     79 
     80 	if (result < 0) {
     81 		return (isc_errno_toresult(errno));
     82 	} else if ((size_t)result >= dstsize) {
     83 		return (ISC_R_NOSPACE);
     84 	} else {
     85 		return (ISC_R_SUCCESS);
     86 	}
     87 }
     88 
     89 static isc_result_t
     90 load_symbol(uv_lib_t *handle, const char *modpath, const char *symbol_name,
     91 	    void **symbolp) {
     92 	void *symbol = NULL;
     93 	int r;
     94 
     95 	REQUIRE(handle != NULL);
     96 	REQUIRE(symbolp != NULL && *symbolp == NULL);
     97 
     98 	r = uv_dlsym(handle, symbol_name, &symbol);
     99 	if (r != 0) {
    100 		const char *errmsg = uv_dlerror(handle);
    101 		if (errmsg == NULL) {
    102 			errmsg = "returned function pointer is NULL";
    103 		}
    104 		isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
    105 			      NS_LOGMODULE_HOOKS, ISC_LOG_ERROR,
    106 			      "failed to look up symbol %s in "
    107 			      "plugin '%s': %s",
    108 			      symbol_name, modpath, errmsg);
    109 		return (ISC_R_FAILURE);
    110 	}
    111 
    112 	*symbolp = symbol;
    113 
    114 	return (ISC_R_SUCCESS);
    115 }
    116 
    117 static void
    118 unload_plugin(ns_plugin_t **pluginp);
    119 
    120 static isc_result_t
    121 load_plugin(isc_mem_t *mctx, const char *modpath, ns_plugin_t **pluginp) {
    122 	isc_result_t result;
    123 	ns_plugin_t *plugin = NULL;
    124 	ns_plugin_version_t *version_func = NULL;
    125 	int version;
    126 	int r;
    127 
    128 	REQUIRE(pluginp != NULL && *pluginp == NULL);
    129 
    130 	plugin = isc_mem_get(mctx, sizeof(*plugin));
    131 	memset(plugin, 0, sizeof(*plugin));
    132 	isc_mem_attach(mctx, &plugin->mctx);
    133 
    134 	plugin->modpath = isc_mem_strdup(plugin->mctx, modpath);
    135 
    136 	ISC_LINK_INIT(plugin, link);
    137 
    138 	r = uv_dlopen(modpath, &plugin->handle);
    139 	if (r != 0) {
    140 		const char *errmsg = uv_dlerror(&plugin->handle);
    141 		if (errmsg == NULL) {
    142 			errmsg = "unknown error";
    143 		}
    144 		isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
    145 			      NS_LOGMODULE_HOOKS, ISC_LOG_ERROR,
    146 			      "failed to dlopen() plugin '%s': %s", modpath,
    147 			      errmsg);
    148 		CHECK(ISC_R_FAILURE);
    149 	}
    150 
    151 	CHECK(load_symbol(&plugin->handle, modpath, "plugin_version",
    152 			  (void **)&version_func));
    153 
    154 	version = version_func();
    155 	if (version < (NS_PLUGIN_VERSION - NS_PLUGIN_AGE) ||
    156 	    version > NS_PLUGIN_VERSION)
    157 	{
    158 		isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
    159 			      NS_LOGMODULE_HOOKS, ISC_LOG_ERROR,
    160 			      "plugin API version mismatch: %d/%d", version,
    161 			      NS_PLUGIN_VERSION);
    162 		CHECK(ISC_R_FAILURE);
    163 	}
    164 
    165 	CHECK(load_symbol(&plugin->handle, modpath, "plugin_check",
    166 			  (void **)&plugin->check_func));
    167 	CHECK(load_symbol(&plugin->handle, modpath, "plugin_register",
    168 			  (void **)&plugin->register_func));
    169 	CHECK(load_symbol(&plugin->handle, modpath, "plugin_destroy",
    170 			  (void **)&plugin->destroy_func));
    171 
    172 	*pluginp = plugin;
    173 
    174 	return (ISC_R_SUCCESS);
    175 
    176 cleanup:
    177 	isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_HOOKS,
    178 		      ISC_LOG_ERROR,
    179 		      "failed to dynamically load plugin '%s': %s", modpath,
    180 		      isc_result_totext(result));
    181 
    182 	unload_plugin(&plugin);
    183 
    184 	return (result);
    185 }
    186 
    187 static void
    188 unload_plugin(ns_plugin_t **pluginp) {
    189 	ns_plugin_t *plugin = NULL;
    190 
    191 	REQUIRE(pluginp != NULL && *pluginp != NULL);
    192 
    193 	plugin = *pluginp;
    194 	*pluginp = NULL;
    195 
    196 	isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_HOOKS,
    197 		      ISC_LOG_DEBUG(1), "unloading plugin '%s'",
    198 		      plugin->modpath);
    199 
    200 	if (plugin->inst != NULL) {
    201 		plugin->destroy_func(&plugin->inst);
    202 	}
    203 
    204 	uv_dlclose(&plugin->handle);
    205 	isc_mem_free(plugin->mctx, plugin->modpath);
    206 	isc_mem_putanddetach(&plugin->mctx, plugin, sizeof(*plugin));
    207 }
    208 
    209 isc_result_t
    210 ns_plugin_register(const char *modpath, const char *parameters, const void *cfg,
    211 		   const char *cfg_file, unsigned long cfg_line,
    212 		   isc_mem_t *mctx, isc_log_t *lctx, void *actx,
    213 		   dns_view_t *view) {
    214 	isc_result_t result;
    215 	ns_plugin_t *plugin = NULL;
    216 
    217 	REQUIRE(mctx != NULL);
    218 	REQUIRE(lctx != NULL);
    219 	REQUIRE(view != NULL);
    220 
    221 	isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_HOOKS,
    222 		      ISC_LOG_INFO, "loading plugin '%s'", modpath);
    223 
    224 	CHECK(load_plugin(mctx, modpath, &plugin));
    225 
    226 	isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_HOOKS,
    227 		      ISC_LOG_INFO, "registering plugin '%s'", modpath);
    228 
    229 	CHECK(plugin->register_func(parameters, cfg, cfg_file, cfg_line, mctx,
    230 				    lctx, actx, view->hooktable,
    231 				    &plugin->inst));
    232 
    233 	ISC_LIST_APPEND(*(ns_plugins_t *)view->plugins, plugin, link);
    234 
    235 cleanup:
    236 	if (result != ISC_R_SUCCESS && plugin != NULL) {
    237 		unload_plugin(&plugin);
    238 	}
    239 
    240 	return (result);
    241 }
    242 
    243 isc_result_t
    244 ns_plugin_check(const char *modpath, const char *parameters, const void *cfg,
    245 		const char *cfg_file, unsigned long cfg_line, isc_mem_t *mctx,
    246 		isc_log_t *lctx, void *actx) {
    247 	isc_result_t result;
    248 	ns_plugin_t *plugin = NULL;
    249 
    250 	CHECK(load_plugin(mctx, modpath, &plugin));
    251 
    252 	result = plugin->check_func(parameters, cfg, cfg_file, cfg_line, mctx,
    253 				    lctx, actx);
    254 
    255 cleanup:
    256 	if (plugin != NULL) {
    257 		unload_plugin(&plugin);
    258 	}
    259 
    260 	return (result);
    261 }
    262 
    263 void
    264 ns_hooktable_init(ns_hooktable_t *hooktable) {
    265 	int i;
    266 
    267 	for (i = 0; i < NS_HOOKPOINTS_COUNT; i++) {
    268 		ISC_LIST_INIT((*hooktable)[i]);
    269 	}
    270 }
    271 
    272 isc_result_t
    273 ns_hooktable_create(isc_mem_t *mctx, ns_hooktable_t **tablep) {
    274 	ns_hooktable_t *hooktable = NULL;
    275 
    276 	REQUIRE(tablep != NULL && *tablep == NULL);
    277 
    278 	hooktable = isc_mem_get(mctx, sizeof(*hooktable));
    279 
    280 	ns_hooktable_init(hooktable);
    281 
    282 	*tablep = hooktable;
    283 
    284 	return (ISC_R_SUCCESS);
    285 }
    286 
    287 void
    288 ns_hooktable_free(isc_mem_t *mctx, void **tablep) {
    289 	ns_hooktable_t *table = NULL;
    290 	ns_hook_t *hook = NULL, *next = NULL;
    291 	int i = 0;
    292 
    293 	REQUIRE(tablep != NULL && *tablep != NULL);
    294 
    295 	table = *tablep;
    296 	*tablep = NULL;
    297 
    298 	for (i = 0; i < NS_HOOKPOINTS_COUNT; i++) {
    299 		for (hook = ISC_LIST_HEAD((*table)[i]); hook != NULL;
    300 		     hook = next)
    301 		{
    302 			next = ISC_LIST_NEXT(hook, link);
    303 			ISC_LIST_UNLINK((*table)[i], hook, link);
    304 			if (hook->mctx != NULL) {
    305 				isc_mem_putanddetach(&hook->mctx, hook,
    306 						     sizeof(*hook));
    307 			}
    308 		}
    309 	}
    310 
    311 	isc_mem_put(mctx, table, sizeof(*table));
    312 }
    313 
    314 void
    315 ns_hook_add(ns_hooktable_t *hooktable, isc_mem_t *mctx,
    316 	    ns_hookpoint_t hookpoint, const ns_hook_t *hook) {
    317 	ns_hook_t *copy = NULL;
    318 
    319 	REQUIRE(hooktable != NULL);
    320 	REQUIRE(mctx != NULL);
    321 	REQUIRE(hookpoint < NS_HOOKPOINTS_COUNT);
    322 	REQUIRE(hook != NULL);
    323 
    324 	copy = isc_mem_get(mctx, sizeof(*copy));
    325 	memset(copy, 0, sizeof(*copy));
    326 
    327 	copy->action = hook->action;
    328 	copy->action_data = hook->action_data;
    329 	isc_mem_attach(mctx, &copy->mctx);
    330 
    331 	ISC_LINK_INIT(copy, link);
    332 	ISC_LIST_APPEND((*hooktable)[hookpoint], copy, link);
    333 }
    334 
    335 void
    336 ns_plugins_create(isc_mem_t *mctx, ns_plugins_t **listp) {
    337 	ns_plugins_t *plugins = NULL;
    338 
    339 	REQUIRE(listp != NULL && *listp == NULL);
    340 
    341 	plugins = isc_mem_get(mctx, sizeof(*plugins));
    342 	memset(plugins, 0, sizeof(*plugins));
    343 	ISC_LIST_INIT(*plugins);
    344 
    345 	*listp = plugins;
    346 }
    347 
    348 void
    349 ns_plugins_free(isc_mem_t *mctx, void **listp) {
    350 	ns_plugins_t *list = NULL;
    351 	ns_plugin_t *plugin = NULL, *next = NULL;
    352 
    353 	REQUIRE(listp != NULL && *listp != NULL);
    354 
    355 	list = *listp;
    356 	*listp = NULL;
    357 
    358 	for (plugin = ISC_LIST_HEAD(*list); plugin != NULL; plugin = next) {
    359 		next = ISC_LIST_NEXT(plugin, link);
    360 		ISC_LIST_UNLINK(*list, plugin, link);
    361 		unload_plugin(&plugin);
    362 	}
    363 
    364 	isc_mem_put(mctx, list, sizeof(*list));
    365 }
    366