diff options
Diffstat (limited to 'dynlibmod')
| -rw-r--r-- | dynlibmod/dynlibmod.c | 301 | ||||
| -rw-r--r-- | dynlibmod/dynlibmod.h | 139 | ||||
| -rw-r--r-- | dynlibmod/examples/helloworld.c | 130 |
3 files changed, 570 insertions, 0 deletions
diff --git a/dynlibmod/dynlibmod.c b/dynlibmod/dynlibmod.c new file mode 100644 index 000000000000..f9751d8c6f73 --- /dev/null +++ b/dynlibmod/dynlibmod.c @@ -0,0 +1,301 @@ +/** + * \file + * This file contains the dynamic library module for Unbound. + * This loads a dynamic library (.dll, .so) and calls that for the + * module actions. + */ +#include "config.h" +#include "util/module.h" +#include "util/config_file.h" +#include "dynlibmod/dynlibmod.h" + +#if HAVE_WINDOWS_H +#include <windows.h> +#define __DYNMOD HMODULE +#define __DYNSYM FARPROC +#define __LOADSYM GetProcAddress +void log_dlerror() { + DWORD dwLastError = GetLastError(); + LPSTR MessageBuffer; + DWORD dwBufferLength; + DWORD dwFormatFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_FROM_SYSTEM ; + if((dwBufferLength = FormatMessageA( + dwFormatFlags, + NULL, // module to get message from (NULL == system) + dwLastError, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language + (LPSTR) &MessageBuffer, + 0, + NULL + ))) + { + log_err("dynlibmod: %s (%ld)", MessageBuffer, dwLastError); + LocalFree(MessageBuffer); + } + +} + +HMODULE open_library(const char* fname) { + return LoadLibrary(fname); +} + +void close_library(const char* fname, __DYNMOD handle) { + (void)fname; + (void)handle; +} +#else +#include <dlfcn.h> +#define __DYNMOD void* +#define __DYNSYM void* +#define __LOADSYM dlsym +void log_dlerror() { + log_err("dynlibmod: %s", dlerror()); +} + +void* open_library(const char* fname) { + return dlopen(fname, RTLD_LAZY | RTLD_GLOBAL); +} + +void close_library(const char* fname, __DYNMOD handle) { + if(!handle) return; + if(dlclose(handle) != 0) { + log_err("dlclose %s: %s", fname, strerror(errno)); + } +} +#endif + +/** module counter for multiple dynlib modules */ +static int dynlib_mod_count = 0; + +/** dynlib module init */ +int dynlibmod_init(struct module_env* env, int id) { + int dynlib_mod_idx = dynlib_mod_count++; + struct config_strlist* cfg_item = env->cfg->dynlib_file; + struct dynlibmod_env* de = (struct dynlibmod_env*)calloc(1, sizeof(struct dynlibmod_env)); + __DYNMOD dynamic_library; + if (!de) + { + log_err("dynlibmod[%d]: malloc failure", dynlib_mod_idx); + return 0; + } + + env->modinfo[id] = (void*) de; + + de->fname = NULL; + for(int i = dynlib_mod_idx; + i != 0 && cfg_item != NULL; + i--, cfg_item = cfg_item->next) {} + + if (cfg_item == NULL || cfg_item->str == NULL || cfg_item->str[0] == 0) { + log_err("dynlibmod[%d]: no dynamic library given.", dynlib_mod_idx); + return 0; + } else { + de->fname = cfg_item->str; + } + verbose(VERB_ALGO, "dynlibmod[%d]: Trying to load library %s", dynlib_mod_idx, de->fname); + dynamic_library = open_library(de->fname); + de->dynamic_library = (void*)dynamic_library; + if (dynamic_library == NULL) { + log_dlerror(); + log_err("dynlibmod[%d]: unable to load dynamic library \"%s\".", dynlib_mod_idx, de->fname); + return 0; + } else { + __DYNSYM initializer; + __DYNSYM deinitializer; + __DYNSYM operate; + __DYNSYM inform; + __DYNSYM clear; + __DYNSYM get_mem; + initializer = __LOADSYM(dynamic_library,"init"); + if (initializer == NULL) { + log_dlerror(); + log_err("dynlibmod[%d]: unable to load init procedure from dynamic library \"%s\".", dynlib_mod_idx, de->fname); + return 0; + } else { + de->func_init = (func_init_t)(void*)initializer; + } + deinitializer = __LOADSYM(dynamic_library,"deinit"); + if (deinitializer == NULL) { + log_dlerror(); + log_err("dynlibmod[%d]: unable to load deinit procedure from dynamic library \"%s\".", dynlib_mod_idx, de->fname); + return 0; + } else { + de->func_deinit = (func_deinit_t)(void*)deinitializer; + } + operate = __LOADSYM(dynamic_library,"operate"); + if (operate == NULL) { + log_dlerror(); + log_err("dynlibmod[%d]: unable to load operate procedure from dynamic library \"%s\".", dynlib_mod_idx, de->fname); + return 0; + } else { + de->func_operate = (func_operate_t)(void*)operate; + } + inform = __LOADSYM(dynamic_library,"inform_super"); + if (inform == NULL) { + log_dlerror(); + log_err("dynlibmod[%d]: unable to load inform_super procedure from dynamic library \"%s\".", dynlib_mod_idx, de->fname); + return 0; + } else { + de->func_inform = (func_inform_t)(void*)inform; + } + clear = __LOADSYM(dynamic_library,"clear"); + if (clear == NULL) { + log_dlerror(); + log_err("dynlibmod[%d]: unable to load clear procedure from dynamic library \"%s\".", dynlib_mod_idx, de->fname); + return 0; + } else { + de->func_clear = (func_clear_t)(void*)clear; + } + get_mem = __LOADSYM(dynamic_library,"get_mem"); + if (get_mem == NULL) { + log_dlerror(); + log_err("dynlibmod[%d]: unable to load get_mem procedure from dynamic library \"%s\".", dynlib_mod_idx, de->fname); + return 0; + } else { + de->func_get_mem = (func_get_mem_t)(void*)get_mem; + } + } + de->inplace_cb_delete_wrapped = &inplace_cb_delete_wrapped; + de->inplace_cb_register_wrapped = &inplace_cb_register_wrapped; + return de->func_init(env, id); +} + +/** dynlib module deinit */ +void dynlibmod_deinit(struct module_env* env, int id) { + struct dynlibmod_env* de = env->modinfo[id]; + if(de == NULL) + return; + de->func_deinit(env, id); + close_library(de->fname, (__DYNMOD)de->dynamic_library); + dynlib_mod_count--; + de->fname = NULL; + free(de); +} + +/** dynlib module operate on a query */ +void dynlibmod_operate(struct module_qstate* qstate, enum module_ev event, + int id, struct outbound_entry* outbound) { + struct dynlibmod_env* de = qstate->env->modinfo[id]; + + de->func_operate(qstate, event, id, outbound); +} + +/** dynlib module */ +void dynlibmod_inform_super(struct module_qstate* qstate, int id, + struct module_qstate* super) { + struct dynlibmod_env* de = qstate->env->modinfo[id]; + + de->func_inform(qstate, id, super); +} + +/** dynlib module cleanup query state */ +void dynlibmod_clear(struct module_qstate* qstate, int id) { + struct dynlibmod_env* de = qstate->env->modinfo[id]; + + de->func_clear(qstate, id); +} + +/** dynlib module alloc size routine */ +size_t dynlibmod_get_mem(struct module_env* env, int id) { + struct dynlibmod_env* de = (struct dynlibmod_env*)env->modinfo[id]; + size_t size; + verbose(VERB_ALGO, "dynlibmod: get_mem, id: %d, de:%p", id, de); + if(!de) + return 0; + + size = de->func_get_mem(env, id); + return size + sizeof(*de); +} + +int dynlib_inplace_cb_reply_generic(struct query_info* qinfo, + struct module_qstate* qstate, struct reply_info* rep, int rcode, + struct edns_data* edns, struct edns_option** opt_list_out, + struct comm_reply* repinfo, struct regional* region, int id, + void* callback) { + struct cb_pair* cb_pair = (struct cb_pair*) callback; + return ((inplace_cb_reply_func_type*) cb_pair->cb)(qinfo, qstate, rep, rcode, edns, opt_list_out, repinfo, region, id, cb_pair->cb_arg); +} + +int dynlib_inplace_cb_query_generic(struct query_info* qinfo, uint16_t flags, + struct module_qstate* qstate, struct sockaddr_storage* addr, + socklen_t addrlen, uint8_t* zone, size_t zonelen, struct regional* region, + int id, void* callback) { + struct cb_pair* cb_pair = (struct cb_pair*) callback; + return ((inplace_cb_query_func_type*) cb_pair->cb)(qinfo, flags, qstate, addr, addrlen, zone, zonelen, region, id, cb_pair->cb_arg); +} + +int dynlib_inplace_cb_edns_back_parsed(struct module_qstate* qstate, + int id, void* cb_args) { + struct cb_pair* cb_pair = (struct cb_pair*) cb_args; + return ((inplace_cb_edns_back_parsed_func_type*) cb_pair->cb)(qstate, id, cb_pair->cb_arg); +} + +int dynlib_inplace_cb_query_response(struct module_qstate* qstate, + struct dns_msg* response, int id, void* cb_args) { + struct cb_pair* cb_pair = (struct cb_pair*) cb_args; + return ((inplace_cb_query_response_func_type*) cb_pair->cb)(qstate, response, id, cb_pair->cb_arg); +} + +int +inplace_cb_register_wrapped(void* cb, enum inplace_cb_list_type type, void* cbarg, + struct module_env* env, int id) { + struct cb_pair* cb_pair = malloc(sizeof(struct cb_pair)); + cb_pair->cb = cb; + cb_pair->cb_arg = cbarg; + if(type >= inplace_cb_reply && type <= inplace_cb_reply_servfail) { + return inplace_cb_register(&dynlib_inplace_cb_reply_generic, type, (void*) cb_pair, env, id); + } else if(type == inplace_cb_query) { + return inplace_cb_register(&dynlib_inplace_cb_query_generic, type, (void*) cb_pair, env, id); + } else if(type == inplace_cb_query_response) { + return inplace_cb_register(&dynlib_inplace_cb_query_response, type, (void*) cb_pair, env, id); + } else if(type == inplace_cb_edns_back_parsed) { + return inplace_cb_register(&dynlib_inplace_cb_edns_back_parsed, type, (void*) cb_pair, env, id); + } else { + return 0; + } +} + +void +inplace_cb_delete_wrapped(struct module_env* env, enum inplace_cb_list_type type, + int id) { + struct inplace_cb* temp = env->inplace_cb_lists[type]; + struct inplace_cb* prev = NULL; + + while(temp) { + if(temp->id == id) { + if(!prev) { + env->inplace_cb_lists[type] = temp->next; + free(temp->cb_arg); + free(temp); + temp = env->inplace_cb_lists[type]; + } + else { + prev->next = temp->next; + free(temp->cb_arg); + free(temp); + temp = prev->next; + } + } + else { + prev = temp; + temp = temp->next; + } + } +} + + +/** + * The module function block + */ +static struct module_func_block dynlibmod_block = { + "dynlib", + &dynlibmod_init, &dynlibmod_deinit, &dynlibmod_operate, &dynlibmod_inform_super, + &dynlibmod_clear, &dynlibmod_get_mem +}; + +struct module_func_block* dynlibmod_get_funcblock(void) +{ + return &dynlibmod_block; +} diff --git a/dynlibmod/dynlibmod.h b/dynlibmod/dynlibmod.h new file mode 100644 index 000000000000..c34cf0e88d92 --- /dev/null +++ b/dynlibmod/dynlibmod.h @@ -0,0 +1,139 @@ +/* + * dynlibmod.h: module header file + * + * Copyright (c) 2019, Peter Munch-Ellingsen (peterme AT peterme.net) + * + * This software is open source. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of the organization nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/** + * \file + * Dynamic loading module for unbound. Loads dynamic library. + */ +#ifndef DYNLIBMOD_H +#define DYNLIBMOD_H +#include "util/module.h" +#include "services/outbound_list.h" + +/** + * Get the module function block. + * @return: function block with function pointers to module methods. + */ +struct module_func_block* dynlibmod_get_funcblock(void); + +/** dynlib module init */ +int dynlibmod_init(struct module_env* env, int id); + +/** dynlib module deinit */ +void dynlibmod_deinit(struct module_env* env, int id); + +/** dynlib module operate on a query */ +void dynlibmod_operate(struct module_qstate* qstate, enum module_ev event, + int id, struct outbound_entry* outbound); + +/** dynlib module */ +void dynlibmod_inform_super(struct module_qstate* qstate, int id, + struct module_qstate* super); + +/** dynlib module cleanup query state */ +void dynlibmod_clear(struct module_qstate* qstate, int id); + +/** dynlib module alloc size routine */ +size_t dynlibmod_get_mem(struct module_env* env, int id); + +int dynlib_inplace_cb_reply_generic(struct query_info* qinfo, + struct module_qstate* qstate, struct reply_info* rep, int rcode, + struct edns_data* edns, struct edns_option** opt_list_out, + struct comm_reply* repinfo, struct regional* region, int id, + void* callback); + +int dynlib_inplace_cb_query_generic(struct query_info* qinfo, uint16_t flags, + struct module_qstate* qstate, struct sockaddr_storage* addr, + socklen_t addrlen, uint8_t* zone, size_t zonelen, struct regional* region, + int id, void* callback); + +int dynlib_inplace_cb_edns_back_parsed(struct module_qstate* qstate, + int id, void* cb_args); + +int dynlib_inplace_cb_query_response(struct module_qstate* qstate, + struct dns_msg* response, int id, void* cb_args); + +int +inplace_cb_register_wrapped(void* cb, enum inplace_cb_list_type type, void* cbarg, + struct module_env* env, int id); + +void +inplace_cb_delete_wrapped(struct module_env* env, enum inplace_cb_list_type type, + int id); + +struct cb_pair { + void *cb; + void *cb_arg; +}; + +/** + * Global state for the module. + */ + +typedef int (*func_init_t)(struct module_env*, int); +typedef void (*func_deinit_t)(struct module_env*, int); +typedef void (*func_operate_t)(struct module_qstate*, enum module_ev, int, struct outbound_entry*); +typedef void (*func_inform_t)(struct module_qstate*, int, struct module_qstate*); +typedef void (*func_clear_t)(struct module_qstate*, int); +typedef size_t (*func_get_mem_t)(struct module_env*, int); +typedef void (*inplace_cb_delete_wrapped_t)(struct module_env*, enum inplace_cb_list_type, int); +typedef int (*inplace_cb_register_wrapped_t)(void*, enum inplace_cb_list_type, void*, struct module_env*, int); + + +struct dynlibmod_env { + /** Dynamic library filename. */ + const char* fname; + /** dynamic library handle */ + void* dynamic_library; + /** Module init function */ + func_init_t func_init; + /** Module deinit function */ + func_deinit_t func_deinit; + /** Module operate function */ + func_operate_t func_operate; + /** Module super_inform function */ + func_inform_t func_inform; + /** Module clear function */ + func_clear_t func_clear; + /** Module get_mem function */ + func_get_mem_t func_get_mem; + /** Wrapped inplace callback functions to circumvent callback whitelisting */ + inplace_cb_delete_wrapped_t inplace_cb_delete_wrapped; + inplace_cb_register_wrapped_t inplace_cb_register_wrapped; + /** Pointer to any data the dynamic library might want to keep */ + void *dyn_env; +}; + + +#endif /* DYNLIBMOD_H */ diff --git a/dynlibmod/examples/helloworld.c b/dynlibmod/examples/helloworld.c new file mode 100644 index 000000000000..acb6b5d9bda6 --- /dev/null +++ b/dynlibmod/examples/helloworld.c @@ -0,0 +1,130 @@ +/** + * \file + * + * This is an example to show how dynamic libraries can be made to work with + * unbound. To build a .so file simply run: + * gcc -I../.. -shared -Wall -Werror -fpic -o helloworld.so helloworld.c + * And to build for windows, first make unbound with the --with-dynlibmod + * switch, then use this command: + * x86_64-w64-mingw32-gcc -m64 -I../.. -shared -Wall -Werror -fpic + * -o helloworld.dll helloworld.c -L../.. -l:libunbound.a + * to cross-compile a 64-bit Windows DLL. + */ + +#include "../../config.h" +#include "../../util/module.h" +#include "../../sldns/parseutil.h" +#include "../dynlibmod.h" + +/* Declare the EXPORT macro that expands to exporting the symbol for DLLs when + * compiling for Windows. All procedures marked with EXPORT in this example are + * called directly by the dynlib module and must be present for the module to + * load correctly. */ +#ifdef HAVE_WINDOWS_H +#define EXPORT __declspec(dllexport) +#else +#define EXPORT +#endif + +/* Forward declare a callback, implemented at the bottom of this file */ +int reply_callback(struct query_info* qinfo, + struct module_qstate* qstate, struct reply_info* rep, int rcode, + struct edns_data* edns, struct edns_option** opt_list_out, + struct comm_reply* repinfo, struct regional* region, int id, + void* callback); + +/* Init is called when the module is first loaded. It should be used to set up + * the environment for this module and do any other initialisation required. */ +EXPORT int init(struct module_env* env, int id) { + log_info("dynlib: hello world from init"); + struct dynlibmod_env* de = (struct dynlibmod_env*) env->modinfo[id]; + de->inplace_cb_register_wrapped(&reply_callback, + inplace_cb_reply, + NULL, env, id); + struct dynlibmod_env* local_env = env->modinfo[id]; + local_env->dyn_env = NULL; + return 1; +} + +/* Deinit is run as the program is shutting down. It should be used to clean up + * the environment and any left over data. */ +EXPORT void deinit(struct module_env* env, int id) { + log_info("dynlib: hello world from deinit"); + struct dynlibmod_env* de = (struct dynlibmod_env*) env->modinfo[id]; + de->inplace_cb_delete_wrapped(env, inplace_cb_reply, id); + if (de->dyn_env != NULL) free(de->dyn_env); +} + +/* Operate is called every time a query passes by this module. The event can be + * used to determine which direction in the module chain it came from. */ +EXPORT void operate(struct module_qstate* qstate, enum module_ev event, + int id, struct outbound_entry* entry) { + log_info("dynlib: hello world from operate"); + log_info("dynlib: incoming query: %s %s(%d) %s(%d)", + qstate->qinfo.qname, + sldns_lookup_by_id(sldns_rr_classes, qstate->qinfo.qclass)->name, + qstate->qinfo.qclass, + sldns_rr_descript(qstate->qinfo.qtype)->_name, + qstate->qinfo.qtype); + if (event == module_event_new || event == module_event_pass) { + qstate->ext_state[id] = module_wait_module; + struct dynlibmod_env* env = qstate->env->modinfo[id]; + if (env->dyn_env == NULL) { + env->dyn_env = calloc(3, sizeof(int)); + ((int *)env->dyn_env)[0] = 42; + ((int *)env->dyn_env)[1] = 102; + ((int *)env->dyn_env)[2] = 192; + } else { + log_err("dynlib: already has data!"); + qstate->ext_state[id] = module_error; + } + } else if (event == module_event_moddone) { + qstate->ext_state[id] = module_finished; + } else { + qstate->ext_state[id] = module_error; + } +} + +/* Inform super is called when a query is completed or errors out, but only if + * a sub-query has been registered to it by this module. Look at + * mesh_attach_sub in services/mesh.h to see how this is done. */ +EXPORT void inform_super(struct module_qstate* qstate, int id, + struct module_qstate* super) { + log_info("dynlib: hello world from inform_super"); +} + +/* Clear is called once a query is complete and the response has been sent + * back. It is used to clear up any per-query allocations. */ +EXPORT void clear(struct module_qstate* qstate, int id) { + log_info("dynlib: hello world from clear"); + struct dynlibmod_env* env = qstate->env->modinfo[id]; + if (env->dyn_env != NULL) { + free(env->dyn_env); + env->dyn_env = NULL; + } +} + +/* Get mem is called when Unbound is printing performance information. This + * only happens explicitly and is only used to show memory usage to the user. */ +EXPORT size_t get_mem(struct module_env* env, int id) { + log_info("dynlib: hello world from get_mem"); + return 0; +} + +/* The callback that was forward declared earlier. It is registered in the init + * procedure to run when a query is being replied to. */ +int reply_callback(struct query_info* qinfo, + struct module_qstate* qstate, struct reply_info* rep, int rcode, + struct edns_data* edns, struct edns_option** opt_list_out, + struct comm_reply* repinfo, struct regional* region, int id, + void* callback) { + log_info("dynlib: hello world from callback"); + struct dynlibmod_env* env = qstate->env->modinfo[id]; + if (env->dyn_env != NULL) { + log_info("dynlib: numbers gotten from query: %d, %d, and %d", + ((int *)env->dyn_env)[0], + ((int *)env->dyn_env)[1], + ((int *)env->dyn_env)[2]); + } + return 0; +} |
