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