Home | History | Annotate | Line # | Download | only in ns
hooks.c revision 1.1
      1 /*	$NetBSD: hooks.c,v 1.1 2019/01/09 16:48:22 christos Exp $	*/
      2 
      3 /*
      4  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
      5  *
      6  * This Source Code Form is subject to the terms of the Mozilla Public
      7  * License, v. 2.0. If a copy of the MPL was not distributed with this
      8  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
      9  *
     10  * See the COPYRIGHT file distributed with this work for additional
     11  * information regarding copyright ownership.
     12  */
     13 
     14 /*! \file */
     15 
     16 #include <config.h>
     17 
     18 #include <string.h>
     19 
     20 #if HAVE_DLFCN_H
     21 #include <dlfcn.h>
     22 #elif _WIN32
     23 #include <windows.h>
     24 #endif
     25 
     26 #include <isc/list.h>
     27 #include <isc/log.h>
     28 #include <isc/mem.h>
     29 #include <isc/mutex.h>
     30 #include <isc/result.h>
     31 #include <isc/platform.h>
     32 #include <isc/util.h>
     33 #include <isc/types.h>
     34 
     35 #include <dns/view.h>
     36 
     37 #include <ns/hooks.h>
     38 #include <ns/log.h>
     39 #include <ns/query.h>
     40 
     41 #define CHECK(op)						\
     42 	do {							\
     43 		result = (op);					\
     44 		if (result != ISC_R_SUCCESS) {			\
     45 			goto cleanup;				\
     46 		}						\
     47 	} while (0)
     48 
     49 struct ns_plugin {
     50        isc_mem_t		*mctx;
     51        void			*handle;
     52        void			*inst;
     53        char			*modpath;
     54        ns_plugin_check_t	*check_func;
     55        ns_plugin_register_t	*register_func;
     56        ns_plugin_destroy_t	*destroy_func;
     57        LINK(ns_plugin_t)	link;
     58 };
     59 
     60 static ns_hooklist_t default_hooktable[NS_HOOKPOINTS_COUNT];
     61 LIBNS_EXTERNAL_DATA ns_hooktable_t *ns__hook_table = &default_hooktable;
     62 
     63 #if HAVE_DLFCN_H && HAVE_DLOPEN
     64 static isc_result_t
     65 load_symbol(void *handle, const char *modpath,
     66 	    const char *symbol_name, void **symbolp)
     67 {
     68 	void *symbol = NULL;
     69 
     70 	REQUIRE(handle != NULL);
     71 	REQUIRE(symbolp != NULL && *symbolp == NULL);
     72 
     73 	/*
     74 	 * Clear any pre-existing error conditions before running dlsym().
     75 	 * (In this case, we expect dlsym() to return non-NULL values
     76 	 * and will always return an error if it returns NULL, but
     77 	 * this ensures that we'll report the correct error condition
     78 	 * if there is one.)
     79 	 */
     80 	dlerror();
     81 	symbol = dlsym(handle, symbol_name);
     82 	if (symbol == NULL) {
     83 		const char *errmsg = dlerror();
     84 		if (errmsg == NULL) {
     85 			errmsg = "returned function pointer is NULL";
     86 		}
     87 		isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
     88 			      NS_LOGMODULE_HOOKS, ISC_LOG_ERROR,
     89 			      "failed to look up symbol %s in "
     90 			      "plugin '%s': %s",
     91 			      symbol_name, modpath, errmsg);
     92 		return (ISC_R_FAILURE);
     93 	}
     94 
     95 	*symbolp = symbol;
     96 
     97 	return (ISC_R_SUCCESS);
     98 }
     99 
    100 static isc_result_t
    101 load_plugin(isc_mem_t *mctx, const char *modpath, ns_plugin_t **pluginp) {
    102 	isc_result_t result;
    103 	void *handle = NULL;
    104 	ns_plugin_t *plugin = NULL;
    105 	ns_plugin_check_t *check_func = NULL;
    106 	ns_plugin_register_t *register_func = NULL;
    107 	ns_plugin_destroy_t *destroy_func = NULL;
    108 	ns_plugin_version_t *version_func = NULL;
    109 	int version, flags;
    110 
    111 	REQUIRE(pluginp != NULL && *pluginp == NULL);
    112 
    113 	flags = RTLD_LAZY | RTLD_LOCAL;
    114 #ifdef RTLD_DEEPBIND
    115 	flags |= RTLD_DEEPBIND;
    116 #endif
    117 
    118 	handle = dlopen(modpath, flags);
    119 	if (handle == NULL) {
    120 		const char *errmsg = dlerror();
    121 		if (errmsg == NULL) {
    122 			errmsg = "unknown error";
    123 		}
    124 		isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
    125 			      NS_LOGMODULE_HOOKS, ISC_LOG_ERROR,
    126 			      "failed to dlopen() plugin '%s': %s",
    127 			      modpath, errmsg);
    128 		return (ISC_R_FAILURE);
    129 	}
    130 
    131 	CHECK(load_symbol(handle, modpath, "plugin_version",
    132 			  (void **)&version_func));
    133 
    134 	version = version_func();
    135 	if (version < (NS_PLUGIN_VERSION - NS_PLUGIN_AGE) ||
    136 	    version > NS_PLUGIN_VERSION)
    137 	{
    138 		isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
    139 			      NS_LOGMODULE_HOOKS, ISC_LOG_ERROR,
    140 			      "plugin API version mismatch: %d/%d",
    141 			      version, NS_PLUGIN_VERSION);
    142 		CHECK(ISC_R_FAILURE);
    143 	}
    144 
    145 	CHECK(load_symbol(handle, modpath, "plugin_check",
    146 			  (void **)&check_func));
    147 	CHECK(load_symbol(handle, modpath, "plugin_register",
    148 			  (void **)&register_func));
    149 	CHECK(load_symbol(handle, modpath, "plugin_destroy",
    150 			  (void **)&destroy_func));
    151 
    152 	plugin = isc_mem_get(mctx, sizeof(*plugin));
    153 	memset(plugin, 0, sizeof(*plugin));
    154 	isc_mem_attach(mctx, &plugin->mctx);
    155 	plugin->handle = handle;
    156 	plugin->modpath = isc_mem_strdup(plugin->mctx, modpath);
    157 	plugin->check_func = check_func;
    158 	plugin->register_func = register_func;
    159 	plugin->destroy_func = destroy_func;
    160 
    161 	ISC_LINK_INIT(plugin, link);
    162 
    163 	*pluginp = plugin;
    164 	plugin = NULL;
    165 
    166 cleanup:
    167 	if (result != ISC_R_SUCCESS) {
    168 		isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
    169 			      NS_LOGMODULE_HOOKS, ISC_LOG_ERROR,
    170 			      "failed to dynamically load "
    171 			      "plugin '%s': %s", modpath,
    172 			      isc_result_totext(result));
    173 
    174 		if (plugin != NULL) {
    175 			isc_mem_putanddetach(&plugin->mctx, plugin,
    176 					     sizeof(*plugin));
    177 		}
    178 
    179 		if (handle != NULL) {
    180 			(void) dlclose(handle);
    181 		}
    182 	}
    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,
    197 		      NS_LOGMODULE_HOOKS, ISC_LOG_INFO,
    198 		      "unloading plugin '%s'", plugin->modpath);
    199 
    200 	if (plugin->inst != NULL) {
    201 		plugin->destroy_func(&plugin->inst);
    202 	}
    203 	if (plugin->handle != NULL) {
    204 		(void) dlclose(plugin->handle);
    205 	}
    206 	if (plugin->modpath != NULL) {
    207 		isc_mem_free(plugin->mctx, plugin->modpath);
    208 	}
    209 
    210 	isc_mem_putanddetach(&plugin->mctx, plugin, sizeof(*plugin));
    211 }
    212 #elif _WIN32
    213 static isc_result_t
    214 load_symbol(HMODULE handle, const char *modpath,
    215 	    const char *symbol_name, void **symbolp)
    216 {
    217 	void *symbol = NULL;
    218 
    219 	REQUIRE(handle != NULL);
    220 	REQUIRE(symbolp != NULL && *symbolp == NULL);
    221 
    222 	symbol = GetProcAddress(handle, symbol_name);
    223 	if (symbol == NULL) {
    224 		int errstatus = GetLastError();
    225 		isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
    226 			      NS_LOGMODULE_HOOKS, ISC_LOG_ERROR,
    227 			      "failed to look up symbol %s in "
    228 			      "plugin '%s': %d",
    229 			      symbol_name, modpath, errstatus);
    230 		return (ISC_R_FAILURE);
    231 	}
    232 
    233 	*symbolp = symbol;
    234 
    235 	return (ISC_R_SUCCESS);
    236 }
    237 
    238 static isc_result_t
    239 load_plugin(isc_mem_t *mctx, const char *modpath, ns_plugin_t **pluginp) {
    240 	isc_result_t result;
    241 	HMODULE handle;
    242 	ns_plugin_t *plugin = NULL;
    243 	ns_plugin_register_t *register_func = NULL;
    244 	ns_plugin_destroy_t *destroy_func = NULL;
    245 	ns_plugin_version_t *version_func = NULL;
    246 	int version;
    247 
    248 	REQUIRE(pluginp != NULL && *pluginp == NULL);
    249 
    250 	handle = LoadLibraryA(modpath);
    251 	if (handle == NULL) {
    252 		CHECK(ISC_R_FAILURE);
    253 	}
    254 
    255 	CHECK(load_symbol(handle, modpath, "plugin_version",
    256 			  (void **)&version_func));
    257 
    258 	version = version_func(NULL);
    259 	if (version < (NS_PLUGIN_VERSION - NS_PLUGIN_AGE) ||
    260 	    version > NS_PLUGIN_VERSION)
    261 	{
    262 		isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
    263 			      NS_LOGMODULE_HOOKS, ISC_LOG_ERROR,
    264 			      "plugin API version mismatch: %d/%d",
    265 			      version, NS_PLUGIN_VERSION);
    266 		CHECK(ISC_R_FAILURE);
    267 	}
    268 
    269 	CHECK(load_symbol(handle, modpath, "plugin_register",
    270 			  (void **)&register_func));
    271 	CHECK(load_symbol(handle, modpath, "plugin_destroy",
    272 			  (void **)&destroy_func));
    273 
    274 	plugin = isc_mem_get(mctx, sizeof(*plugin));
    275 	memset(plugin, 0, sizeof(*plugin));
    276 	isc_mem_attach(mctx, &plugin->mctx);
    277 	plugin->handle = handle;
    278 	plugin->modpath = isc_mem_strdup(plugin->mctx, modpath);
    279 	plugin->register_func = register_func;
    280 	plugin->destroy_func = destroy_func;
    281 
    282 	ISC_LINK_INIT(plugin, link);
    283 
    284 	*pluginp = plugin;
    285 	plugin = NULL;
    286 
    287 cleanup:
    288 	if (result != ISC_R_SUCCESS) {
    289 		isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
    290 			      NS_LOGMODULE_HOOKS, ISC_LOG_ERROR,
    291 			      "failed to dynamically load "
    292 			      "plugin '%s': %d (%s)", modpath,
    293 			      GetLastError(), isc_result_totext(result));
    294 
    295 		if (plugin != NULL) {
    296 			isc_mem_putanddetach(&plugin->mctx, plugin,
    297 					     sizeof(*plugin));
    298 		}
    299 
    300 		if (handle != NULL) {
    301 			FreeLibrary(handle);
    302 		}
    303 	}
    304 
    305 	return (result);
    306 }
    307 
    308 static void
    309 unload_plugin(ns_plugin_t **pluginp) {
    310 	ns_plugin_t *plugin = NULL;
    311 
    312 	REQUIRE(pluginp != NULL && *pluginp != NULL);
    313 
    314 	plugin = *pluginp;
    315 	*pluginp = NULL;
    316 
    317 	isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
    318 		      NS_LOGMODULE_HOOKS, ISC_LOG_INFO,
    319 		      "unloading plugin '%s'", plugin->modpath);
    320 
    321 	if (plugin->inst != NULL) {
    322 		plugin->destroy_func(&plugin->inst);
    323 	}
    324 	if (plugin->handle != NULL) {
    325 		FreeLibrary(plugin->handle);
    326 	}
    327 
    328 	if (plugin->modpath != NULL) {
    329 		isc_mem_free(plugin->mctx, plugin->modpath);
    330 	}
    331 
    332 	isc_mem_putanddetach(&plugin->mctx, plugin, sizeof(*plugin));
    333 }
    334 #else	/* HAVE_DLFCN_H || _WIN32 */
    335 static isc_result_t
    336 load_plugin(isc_mem_t *mctx, const char *modpath, ns_plugin_t **pluginp) {
    337 	UNUSED(mctx);
    338 	UNUSED(modpath);
    339 	UNUSED(pluginp);
    340 
    341 	isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
    342 		      NS_LOGMODULE_HOOKS, ISC_LOG_ERROR,
    343 		      "plugin support is not implemented");
    344 
    345 	return (ISC_R_NOTIMPLEMENTED);
    346 }
    347 
    348 static void
    349 unload_plugin(ns_plugin_t **pluginp) {
    350 	UNUSED(pluginp);
    351 }
    352 #endif	/* HAVE_DLFCN_H */
    353 
    354 isc_result_t
    355 ns_plugin_register(const char *modpath, const char *parameters,
    356 		   const void *cfg, const char *cfg_file,
    357 		   unsigned long cfg_line,
    358 		   isc_mem_t *mctx, isc_log_t *lctx, void *actx,
    359 		   dns_view_t *view)
    360 {
    361 	isc_result_t result;
    362 	ns_plugin_t *plugin = NULL;
    363 
    364 	REQUIRE(mctx != NULL);
    365 	REQUIRE(lctx != NULL);
    366 	REQUIRE(view != NULL);
    367 
    368 	isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
    369 		      NS_LOGMODULE_HOOKS, ISC_LOG_INFO,
    370 		      "loading plugin '%s'", modpath);
    371 
    372 	CHECK(load_plugin(mctx, modpath, &plugin));
    373 
    374 	isc_log_write(ns_lctx, NS_LOGCATEGORY_GENERAL,
    375 		      NS_LOGMODULE_HOOKS, ISC_LOG_INFO,
    376 		      "registering plugin '%s'", modpath);
    377 
    378 	CHECK(plugin->register_func(parameters, cfg, cfg_file, cfg_line,
    379 				    mctx, lctx, actx, view->hooktable,
    380 				    &plugin->inst));
    381 
    382 	ISC_LIST_APPEND(*(ns_plugins_t *)view->plugins, plugin, link);
    383 
    384 cleanup:
    385 	if (result != ISC_R_SUCCESS && plugin != NULL) {
    386 		unload_plugin(&plugin);
    387 	}
    388 
    389 	return (result);
    390 }
    391 
    392 isc_result_t
    393 ns_plugin_check(const char *modpath, const char *parameters,
    394 		const void *cfg, const char *cfg_file, unsigned long cfg_line,
    395 		isc_mem_t *mctx, isc_log_t *lctx, void *actx)
    396 {
    397 	isc_result_t result;
    398 	ns_plugin_t *plugin = NULL;
    399 
    400 	CHECK(load_plugin(mctx, modpath, &plugin));
    401 
    402 	result = plugin->check_func(parameters, cfg, cfg_file, cfg_line,
    403 				  mctx, lctx, actx);
    404 
    405 cleanup:
    406 	if (plugin != NULL) {
    407 		unload_plugin(&plugin);
    408 	}
    409 
    410 	return (result);
    411 }
    412 
    413 void
    414 ns_hooktable_init(ns_hooktable_t *hooktable) {
    415 	int i;
    416 
    417 	for (i = 0; i < NS_HOOKPOINTS_COUNT; i++) {
    418 		ISC_LIST_INIT((*hooktable)[i]);
    419 	}
    420 }
    421 
    422 isc_result_t
    423 ns_hooktable_create(isc_mem_t *mctx, ns_hooktable_t **tablep) {
    424 	ns_hooktable_t *hooktable = NULL;
    425 
    426 	REQUIRE(tablep != NULL && *tablep == NULL);
    427 
    428 	hooktable = isc_mem_get(mctx, sizeof(*hooktable));
    429 
    430 	ns_hooktable_init(hooktable);
    431 
    432 	*tablep = hooktable;
    433 
    434 	return (ISC_R_SUCCESS);
    435 }
    436 
    437 void
    438 ns_hooktable_free(isc_mem_t *mctx, void **tablep) {
    439 	ns_hooktable_t *table = NULL;
    440 	ns_hook_t *hook = NULL, *next = NULL;
    441 	int i = 0;
    442 
    443 	REQUIRE(tablep != NULL && *tablep != NULL);
    444 
    445 	table = *tablep;
    446 	*tablep = NULL;
    447 
    448 	for (i = 0; i < NS_HOOKPOINTS_COUNT; i++) {
    449 		for (hook = ISC_LIST_HEAD((*table)[i]);
    450 		     hook != NULL;
    451 		     hook = next)
    452 		{
    453 			next = ISC_LIST_NEXT(hook, link);
    454 			ISC_LIST_UNLINK((*table)[i], hook, link);
    455 			if (hook->mctx != NULL) {
    456 				isc_mem_putanddetach(&hook->mctx,
    457 						     hook, sizeof(*hook));
    458 			}
    459 		}
    460 	}
    461 
    462 	isc_mem_put(mctx, table, sizeof(*table));
    463 }
    464 
    465 void
    466 ns_hook_add(ns_hooktable_t *hooktable, isc_mem_t *mctx,
    467 	    ns_hookpoint_t hookpoint, const ns_hook_t *hook)
    468 {
    469 	ns_hook_t *copy = NULL;
    470 
    471 	REQUIRE(hooktable != NULL);
    472 	REQUIRE(mctx != NULL);
    473 	REQUIRE(hookpoint < NS_HOOKPOINTS_COUNT);
    474 	REQUIRE(hook != NULL);
    475 
    476 	copy = isc_mem_get(mctx, sizeof(*copy));
    477 	memset(copy, 0, sizeof(*copy));
    478 
    479 	copy->action = hook->action;
    480 	copy->action_data = hook->action_data;
    481 	isc_mem_attach(mctx, &copy->mctx);
    482 
    483 	ISC_LINK_INIT(copy, link);
    484 	ISC_LIST_APPEND((*hooktable)[hookpoint], copy, link);
    485 }
    486 
    487 void
    488 ns_plugins_create(isc_mem_t *mctx, ns_plugins_t **listp) {
    489 	ns_plugins_t *plugins = NULL;
    490 
    491 	REQUIRE(listp != NULL && *listp == NULL);
    492 
    493 	plugins = isc_mem_get(mctx, sizeof(*plugins));
    494 	memset(plugins, 0, sizeof(*plugins));
    495 	ISC_LIST_INIT(*plugins);
    496 
    497 	*listp = plugins;
    498 }
    499 
    500 void
    501 ns_plugins_free(isc_mem_t *mctx, void **listp) {
    502 	ns_plugins_t *list = NULL;
    503 	ns_plugin_t *plugin = NULL, *next = NULL;
    504 
    505 	REQUIRE(listp != NULL && *listp != NULL);
    506 
    507 	list = *listp;
    508 	*listp = NULL;
    509 
    510 	for (plugin = ISC_LIST_HEAD(*list);
    511 	     plugin != NULL;
    512 	     plugin = next)
    513 	{
    514 		next = ISC_LIST_NEXT(plugin, link);
    515 		ISC_LIST_UNLINK(*list, plugin, link);
    516 		unload_plugin(&plugin);
    517 	}
    518 
    519 	isc_mem_put(mctx, list, sizeof(*list));
    520 }
    521