mirror of
https://github.com/isc-projects/bind9.git
synced 2026-06-11 00:19:59 -04:00
In named_config_parsefile(), when checking the validity of named.conf, the checking of plugin correctness was deliberately postponed until the plugin is loaded and registered. However, when the plugin was registered, the checking was never actually done: the plugin_register() implementation was called, but plugin_check() was not. This made it necessary to duplicate the correctness checking in both functions, so that both named-checkconf and named could catch errors. That should not be required. ns_plugin_register() now calls the check function before the register function, and aborts if either one fails. ns_plugin_check() calls only the check function. ns_plugin_check() is used by named-checkconf, and ns_plugin_register() is used by named. (Note: this design has a side effect that a call to ns_plugin_register() will result in the plugin parameters being parsed twice at registration time.) ns_plugin_check() now takes an additional argument for the hook source: zone or view.
374 lines
8.8 KiB
C
374 lines
8.8 KiB
C
/*
|
|
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
|
|
*
|
|
* SPDX-License-Identifier: MPL-2.0
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
|
|
*
|
|
* See the COPYRIGHT file distributed with this work for additional
|
|
* information regarding copyright ownership.
|
|
*/
|
|
|
|
/*! \file */
|
|
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include <isc/errno.h>
|
|
#include <isc/file.h>
|
|
#include <isc/list.h>
|
|
#include <isc/log.h>
|
|
#include <isc/mem.h>
|
|
#include <isc/mutex.h>
|
|
#include <isc/result.h>
|
|
#include <isc/types.h>
|
|
#include <isc/util.h>
|
|
#include <isc/uv.h>
|
|
|
|
#include <dns/view.h>
|
|
|
|
#include <ns/hooks.h>
|
|
#include <ns/query.h>
|
|
|
|
#define CHECK(op) \
|
|
do { \
|
|
result = (op); \
|
|
if (result != ISC_R_SUCCESS) { \
|
|
goto cleanup; \
|
|
} \
|
|
} while (0)
|
|
|
|
struct ns_plugin {
|
|
isc_mem_t *mctx;
|
|
uv_lib_t handle;
|
|
void *inst;
|
|
char *modpath;
|
|
ns_plugin_check_t *check_func;
|
|
ns_plugin_register_t *register_func;
|
|
ns_plugin_destroy_t *destroy_func;
|
|
ISC_LINK(ns_plugin_t) link;
|
|
};
|
|
|
|
static ns_hooklist_t default_hooktable[NS_HOOKPOINTS_COUNT];
|
|
ns_hooktable_t *ns__hook_table = &default_hooktable;
|
|
|
|
static isc_result_t
|
|
plugin_expandpath(const char *src, char *dst, size_t dstsize, bool appendext) {
|
|
int result;
|
|
const char *ext = appendext ? NAMED_PLUGINEXT : "";
|
|
|
|
/*
|
|
* On Unix systems, differentiate between paths and filenames.
|
|
*/
|
|
if (strchr(src, '/') != NULL) {
|
|
/*
|
|
* 'src' is an absolute or relative path. Copy it verbatim.
|
|
*/
|
|
result = snprintf(dst, dstsize, "%s%s", src, ext);
|
|
} else {
|
|
/*
|
|
* 'src' is a filename. Prepend default plugin directory path.
|
|
*/
|
|
result = snprintf(dst, dstsize, "%s/%s%s", NAMED_PLUGINDIR, src,
|
|
ext);
|
|
}
|
|
|
|
if (result < 0) {
|
|
return isc_errno_toresult(errno);
|
|
} else if ((size_t)result >= dstsize) {
|
|
return ISC_R_NOSPACE;
|
|
} else {
|
|
return ISC_R_SUCCESS;
|
|
}
|
|
}
|
|
|
|
isc_result_t
|
|
ns_plugin_expandpath(const char *src, char *dst, size_t dstsize) {
|
|
isc_result_t result;
|
|
|
|
result = plugin_expandpath(src, dst, dstsize, false);
|
|
if (result != ISC_R_SUCCESS) {
|
|
return result;
|
|
}
|
|
|
|
if (isc_file_exists(dst) == false) {
|
|
result = plugin_expandpath(src, dst, dstsize, true);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static isc_result_t
|
|
load_symbol(uv_lib_t *handle, const char *modpath, const char *symbol_name,
|
|
void **symbolp) {
|
|
void *symbol = NULL;
|
|
int r;
|
|
|
|
REQUIRE(handle != NULL);
|
|
REQUIRE(symbolp != NULL && *symbolp == NULL);
|
|
|
|
r = uv_dlsym(handle, symbol_name, &symbol);
|
|
if (r != 0) {
|
|
const char *errmsg = uv_dlerror(handle);
|
|
if (errmsg == NULL) {
|
|
errmsg = "returned function pointer is NULL";
|
|
}
|
|
isc_log_write(NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_HOOKS,
|
|
ISC_LOG_ERROR,
|
|
"failed to look up symbol %s in "
|
|
"plugin '%s': %s",
|
|
symbol_name, modpath, errmsg);
|
|
return ISC_R_FAILURE;
|
|
}
|
|
|
|
*symbolp = symbol;
|
|
|
|
return ISC_R_SUCCESS;
|
|
}
|
|
|
|
static void
|
|
unload_plugin(ns_plugin_t **pluginp);
|
|
|
|
static isc_result_t
|
|
load_plugin(isc_mem_t *mctx, const char *modpath, ns_plugin_t **pluginp) {
|
|
isc_result_t result;
|
|
ns_plugin_t *plugin = NULL;
|
|
ns_plugin_version_t *version_func = NULL;
|
|
int version;
|
|
int r;
|
|
|
|
REQUIRE(pluginp != NULL && *pluginp == NULL);
|
|
|
|
plugin = isc_mem_get(mctx, sizeof(*plugin));
|
|
*plugin = (ns_plugin_t){
|
|
.modpath = isc_mem_strdup(mctx, modpath),
|
|
};
|
|
|
|
isc_mem_attach(mctx, &plugin->mctx);
|
|
|
|
ISC_LINK_INIT(plugin, link);
|
|
|
|
r = uv_dlopen(modpath, &plugin->handle);
|
|
if (r != 0) {
|
|
const char *errmsg = uv_dlerror(&plugin->handle);
|
|
if (errmsg == NULL) {
|
|
errmsg = "unknown error";
|
|
}
|
|
isc_log_write(NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_HOOKS,
|
|
ISC_LOG_ERROR,
|
|
"failed to dlopen() plugin '%s': %s", modpath,
|
|
errmsg);
|
|
CHECK(ISC_R_FAILURE);
|
|
}
|
|
|
|
CHECK(load_symbol(&plugin->handle, modpath, "plugin_version",
|
|
(void **)&version_func));
|
|
|
|
version = version_func();
|
|
if (version < (NS_PLUGIN_VERSION - NS_PLUGIN_AGE) ||
|
|
version > NS_PLUGIN_VERSION)
|
|
{
|
|
isc_log_write(NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_HOOKS,
|
|
ISC_LOG_ERROR,
|
|
"plugin API version mismatch: %d/%d", version,
|
|
NS_PLUGIN_VERSION);
|
|
CHECK(ISC_R_FAILURE);
|
|
}
|
|
|
|
CHECK(load_symbol(&plugin->handle, modpath, "plugin_check",
|
|
(void **)&plugin->check_func));
|
|
CHECK(load_symbol(&plugin->handle, modpath, "plugin_register",
|
|
(void **)&plugin->register_func));
|
|
CHECK(load_symbol(&plugin->handle, modpath, "plugin_destroy",
|
|
(void **)&plugin->destroy_func));
|
|
|
|
*pluginp = plugin;
|
|
|
|
return ISC_R_SUCCESS;
|
|
|
|
cleanup:
|
|
isc_log_write(NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_HOOKS, ISC_LOG_ERROR,
|
|
"failed to dynamically load plugin '%s': %s", modpath,
|
|
isc_result_totext(result));
|
|
|
|
unload_plugin(&plugin);
|
|
|
|
return result;
|
|
}
|
|
|
|
static void
|
|
unload_plugin(ns_plugin_t **pluginp) {
|
|
ns_plugin_t *plugin = NULL;
|
|
|
|
REQUIRE(pluginp != NULL && *pluginp != NULL);
|
|
|
|
plugin = *pluginp;
|
|
*pluginp = NULL;
|
|
|
|
isc_log_write(NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_HOOKS,
|
|
ISC_LOG_DEBUG(1), "unloading plugin '%s'",
|
|
plugin->modpath);
|
|
|
|
if (plugin->inst != NULL) {
|
|
plugin->destroy_func(&plugin->inst);
|
|
}
|
|
|
|
uv_dlclose(&plugin->handle);
|
|
isc_mem_free(plugin->mctx, plugin->modpath);
|
|
isc_mem_putanddetach(&plugin->mctx, plugin, sizeof(*plugin));
|
|
}
|
|
|
|
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 *aclctx, ns_hook_data_t *hookdata) {
|
|
isc_result_t result;
|
|
ns_plugin_t *plugin = NULL;
|
|
|
|
REQUIRE(mctx != NULL);
|
|
REQUIRE(hookdata != NULL);
|
|
|
|
isc_log_write(NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_HOOKS, ISC_LOG_INFO,
|
|
"loading plugin '%s'", modpath);
|
|
|
|
CHECK(load_plugin(mctx, modpath, &plugin));
|
|
|
|
isc_log_write(NS_LOGCATEGORY_GENERAL, NS_LOGMODULE_HOOKS, ISC_LOG_INFO,
|
|
"registering plugin '%s'", modpath);
|
|
|
|
INSIST(hookdata->source != NS_HOOKSOURCE_UNDEFINED);
|
|
|
|
CHECK(plugin->check_func(parameters, cfg, cfg_file, cfg_line, mctx,
|
|
aclctx, hookdata->source));
|
|
CHECK(plugin->register_func(parameters, cfg, cfg_file, cfg_line, mctx,
|
|
aclctx, hookdata->hooktable,
|
|
hookdata->source, &plugin->inst));
|
|
|
|
ISC_LIST_APPEND(*hookdata->plugins, plugin, link);
|
|
|
|
cleanup:
|
|
if (result != ISC_R_SUCCESS && plugin != NULL) {
|
|
unload_plugin(&plugin);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
isc_result_t
|
|
ns_plugin_check(const char *modpath, const char *parameters, const void *cfg,
|
|
const char *cfg_file, unsigned long cfg_line, isc_mem_t *mctx,
|
|
void *aclctx, ns_hooksource_t source) {
|
|
isc_result_t result;
|
|
ns_plugin_t *plugin = NULL;
|
|
|
|
CHECK(load_plugin(mctx, modpath, &plugin));
|
|
|
|
result = plugin->check_func(parameters, cfg, cfg_file, cfg_line, mctx,
|
|
aclctx, source);
|
|
|
|
cleanup:
|
|
if (plugin != NULL) {
|
|
unload_plugin(&plugin);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void
|
|
ns_hooktable_init(ns_hooktable_t *hooktable) {
|
|
int i;
|
|
|
|
for (i = 0; i < NS_HOOKPOINTS_COUNT; i++) {
|
|
ISC_LIST_INIT((*hooktable)[i]);
|
|
}
|
|
}
|
|
|
|
void
|
|
ns_hooktable_create(isc_mem_t *mctx, ns_hooktable_t **tablep) {
|
|
ns_hooktable_t *hooktable = NULL;
|
|
|
|
REQUIRE(tablep != NULL && *tablep == NULL);
|
|
|
|
hooktable = isc_mem_get(mctx, sizeof(*hooktable));
|
|
|
|
ns_hooktable_init(hooktable);
|
|
|
|
*tablep = hooktable;
|
|
}
|
|
|
|
void
|
|
ns_hooktable_free(isc_mem_t *mctx, void **tablep) {
|
|
ns_hooktable_t *table = NULL;
|
|
int i = 0;
|
|
|
|
REQUIRE(tablep != NULL && *tablep != NULL);
|
|
|
|
table = *tablep;
|
|
*tablep = NULL;
|
|
|
|
for (i = 0; i < NS_HOOKPOINTS_COUNT; i++) {
|
|
ISC_LIST_FOREACH((*table)[i], hook, link) {
|
|
ISC_LIST_UNLINK((*table)[i], hook, link);
|
|
if (hook->mctx != NULL) {
|
|
isc_mem_putanddetach(&hook->mctx, hook,
|
|
sizeof(*hook));
|
|
}
|
|
}
|
|
}
|
|
|
|
isc_mem_put(mctx, table, sizeof(*table));
|
|
}
|
|
|
|
void
|
|
ns_hook_add(ns_hooktable_t *hooktable, isc_mem_t *mctx,
|
|
ns_hookpoint_t hookpoint, const ns_hook_t *hook) {
|
|
ns_hook_t *copy = NULL;
|
|
|
|
REQUIRE(hooktable != NULL);
|
|
REQUIRE(mctx != NULL);
|
|
REQUIRE(hookpoint < NS_HOOKPOINTS_COUNT);
|
|
REQUIRE(hook != NULL);
|
|
|
|
copy = isc_mem_get(mctx, sizeof(*copy));
|
|
*copy = (ns_hook_t){
|
|
.action = hook->action,
|
|
.action_data = hook->action_data,
|
|
};
|
|
isc_mem_attach(mctx, ©->mctx);
|
|
|
|
ISC_LINK_INIT(copy, link);
|
|
ISC_LIST_APPEND((*hooktable)[hookpoint], copy, link);
|
|
}
|
|
|
|
void
|
|
ns_plugins_create(isc_mem_t *mctx, ns_plugins_t **listp) {
|
|
ns_plugins_t *plugins = NULL;
|
|
|
|
REQUIRE(listp != NULL && *listp == NULL);
|
|
|
|
plugins = isc_mem_get(mctx, sizeof(*plugins));
|
|
*plugins = (ns_plugins_t){ 0 };
|
|
ISC_LIST_INIT(*plugins);
|
|
|
|
*listp = plugins;
|
|
}
|
|
|
|
void
|
|
ns_plugins_free(isc_mem_t *mctx, void **listp) {
|
|
ns_plugins_t *list = NULL;
|
|
|
|
REQUIRE(listp != NULL && *listp != NULL);
|
|
|
|
list = *listp;
|
|
*listp = NULL;
|
|
|
|
ISC_LIST_FOREACH(*list, plugin, link) {
|
|
ISC_LIST_UNLINK(*list, plugin, link);
|
|
unload_plugin(&plugin);
|
|
}
|
|
|
|
isc_mem_put(mctx, list, sizeof(*list));
|
|
}
|