Improve dynlib module and add documentation

Dynamic library module is now only a thin wrapper that loads dynamic
libraries and forwards all function calls directly to the loaded module.
This meant adding get_mem and clear, and get_mem calls have been added
in the expected places.

Documentation has also been added to the example.conf and the
unbound.conf manpage.
This commit is contained in:
PMunch 2019-10-21 14:20:33 +02:00
parent 1762437121
commit 8eeb910e3d
9 changed files with 119 additions and 88 deletions

View file

@ -803,6 +803,9 @@ print_mem(RES* ssl, struct worker* worker, struct daemon* daemon,
size_t dnscrypt_shared_secret = 0;
size_t dnscrypt_nonce = 0;
#endif /* USE_DNSCRYPT */
#ifdef WITH_DYNLIBMODULE
size_t dynlib = 0;
#endif /* WITH_DYNLIBMODULE */
msg = slabhash_get_mem(daemon->env->msg_cache);
rrset = slabhash_get_mem(&daemon->env->rrset_cache->table);
val = mod_get_mem(&worker->env, "validator");
@ -821,6 +824,9 @@ print_mem(RES* ssl, struct worker* worker, struct daemon* daemon,
dnscrypt_nonce = slabhash_get_mem(daemon->dnscenv->nonces_cache);
}
#endif /* USE_DNSCRYPT */
#ifdef WITH_DYNLIBMODULE
dynlib = mod_get_mem(&worker->env, "dynlib");
#endif /* WITH_DYNLIBMODULE */
if(!print_longnum(ssl, "mem.cache.rrset"SQ, rrset))
return 0;
@ -848,6 +854,10 @@ print_mem(RES* ssl, struct worker* worker, struct daemon* daemon,
dnscrypt_nonce))
return 0;
#endif /* USE_DNSCRYPT */
#ifdef WITH_DYNLIBMODULE
if(!print_longnum(ssl, "mem.mod.dynlibmod"SQ, dynlib))
return 0;
#endif /* WITH_DYNLIBMODULE */
if(!print_longnum(ssl, "mem.streamwait"SQ,
(size_t)s->svr.mem_stream_wait))
return 0;

View file

@ -837,6 +837,16 @@ python:
# Script file to load
# python-script: "@UNBOUND_SHARE_DIR@/ubmodule-tst.py"
# Dynamic library config section. To enable:
# o use --with-dynlibmodule to configure before compiling.
# o list dynlib in the module-config string (above) to enable.
# It can be placed anywhere, the dynlib module is only a very thin wrapper
# to load modules dynamically.
# o and give a dynlib-file to run.
dynlib:
# Script file to load
# dynlib-file: "@UNBOUND_SHARE_DIR@/dynlib.so"
# Remote control config section.
remote-control:
# Enable remote control with unbound-control(8) here.

View file

@ -940,7 +940,9 @@ EDNS client subnet support the default is "subnetcache validator iterator".
Most modules that need to be listed here have to be listed at the beginning
of the line. The cachedb module has to be listed just before the iterator.
The python module can be listed in different places, it then processes the
output of the module it is just before.
output of the module it is just before. The dynlib module can be listed pretty
much anywhere, it is only a very thin wrapper that allows dynamic libraries to
run in its place.
.TP
.B trust\-anchor\-file: \fI<filename>
File with trusted keys for validation. Both DS and DNSKEY entries can appear
@ -1782,6 +1784,22 @@ directory.
.B python\-script: \fI<python file>\fR
The script file to load. Repeat this option for every python module instance
added to the \fBmodule\-config:\fR option.
.SS "Dynamic Library Module Options"
.LP
The
.B dynlib:
clause gives the settings for the \fIdynlib\fR(1) module. This module is only
a very small wrapper that allows dynamic modules to be loaded on runtime
instead of being compiled into the application. To enable the dynlib module it
has to be compiled into the daemon, and the word "dynlib" has to be put in the
\fBmodule\-config:\fR option.
.LP
The \fBdynlib\-file:\fR path should be specified as an absolute path relative
to the new path set by \fBchroot:\fR option, or as a relative path to the
working directory.
.TP
.B dynlib\-file: \fI<dynlib file>\fR
The dynamic library file to load.
.SS "DNS64 Module Options"
.LP
The dns64 module must be configured in the \fBmodule\-config:\fR "dns64

View file

@ -5,6 +5,7 @@
#if HAVE_WINDOWS_H
#include <windows.h>
#define __DYNMOD HMODULE
#define __DYNSYM FARPROC
#define __LOADSYM GetProcAddress
void log_dlerror() {
@ -25,43 +26,39 @@ void log_dlerror() {
))
{
DWORD dwBytesWritten;
//
// Output message string on stderr.
//
log_info("dynlibmod: %s (%ld)", MessageBuffer, dwLastError);
//WriteFile(
// GetStdHandle(STD_ERROR_HANDLE),
// MessageBuffer,
// dwBufferLength,
// &dwBytesWritten,
// NULL
// );
//
// Free the buffer allocated by the system.
//
log_err("dynlibmod: %s (%ld)", MessageBuffer, dwLastError);
LocalFree(MessageBuffer);
}
}
HMODULE open_library(const char* fname) {
return LoadLibrary(fname);
}
#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);
}
#endif
/**
* Global state for the module.
*/
typedef int (*func_init_t)(int, struct config_file*);
typedef int (*func_deinit_t)(int);
typedef int (*func_operate_t)(int, enum module_ev event, struct module_qstate* qstate, void*);
typedef int (*func_inform_t)(int, struct module_qstate* qstate, struct module_qstate* super, void*);
typedef void (*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);
struct dynlibmod_env {
/** Dynamic library filename. */
@ -75,20 +72,19 @@ struct dynlibmod_env {
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;
/** Module qstate. */
struct module_qstate* qstate;
};
struct dynlibmod_qstate {
/** Module per query data. */
void* data;
};
/** dynlib module init */
int dynlibmod_init(struct module_env* env, int id) {
struct dynlibmod_env* de = (struct dynlibmod_env*)calloc(1, sizeof(struct dynlibmod_env));
__DYNMOD dynamic_library;
if (!de)
{
log_err("dynlibmod: malloc failure");
@ -102,59 +98,63 @@ int dynlibmod_init(struct module_env* env, int id) {
log_err("dynlibmod: no dynamic library given.");
return 0;
}
log_info("Trying to load library %s", de->fname);
#ifndef HAVE_WINDOWS_H
void* dynamic_library = dlopen(de->fname, RTLD_LAZY | RTLD_GLOBAL);
#else
HMODULE dynamic_library = LoadLibrary(de->fname);
#endif
verbose(VERB_ALGO, "dynlibmod: Trying to load library %s", de->fname);
dynamic_library = open_library(de->fname);
if (dynamic_library == NULL) {
log_dlerror();
log_err("dynlibmod: unable to load dynamic library.");
log_err("dynlibmod: unable to load dynamic library \"%s\".", de->fname);
return 0;
} else {
__DYNSYM initializer = __LOADSYM(dynamic_library,"init");
if (initializer == NULL) {
log_err("dynlibmod: unable to load init procedure from dynamic library.");
#ifndef HAVE_WINDOWS_H
log_err("dynlibmod: %s", dlerror());
#endif
log_dlerror();
log_err("dynlibmod: unable to load init procedure from dynamic library \"%s\".", de->fname);
return 0;
} else {
de->func_init = (func_init_t) initializer;
}
__DYNSYM deinitializer = __LOADSYM(dynamic_library,"deinit");
if (deinitializer == NULL) {
log_err("dynlibmod: unable to load deinit procedure from dynamic library.");
#ifndef HAVE_WINDOWS_H
log_err("dynlibmod: %s", dlerror());
#endif
log_dlerror();
log_err("dynlibmod: unable to load deinit procedure from dynamic library \"%s\".", de->fname);
return 0;
} else {
de->func_deinit = (func_deinit_t) deinitializer;
}
__DYNSYM operate = __LOADSYM(dynamic_library,"operate");
if (operate == NULL) {
log_err("dynlibmod: unable to load operate procedure from dynamic library.");
#ifndef HAVE_WINDOWS_H
log_err("dynlibmod: %s", dlerror());
#endif
log_dlerror();
log_err("dynlibmod: unable to load operate procedure from dynamic library \"%s\".", de->fname);
return 0;
} else {
de->func_operate = (func_operate_t) operate;
}
__DYNSYM inform = __LOADSYM(dynamic_library,"inform_super");
if (inform == NULL) {
log_err("dynlibmod: unable to load inform_super procedure from dynamic library.");
#ifndef HAVE_WINDOWS_H
log_err("dynlibmod: %s", dlerror());
#endif
log_dlerror();
log_err("dynlibmod: unable to load inform_super procedure from dynamic library \"%s\".", de->fname);
return 0;
} else {
de->func_inform = (func_inform_t) inform;
}
__DYNSYM clear = __LOADSYM(dynamic_library,"clear");
if (clear == NULL) {
log_dlerror();
log_err("dynlibmod: unable to load clear procedure from dynamic library \"%s\".", de->fname);
return 0;
} else {
de->func_clear = (func_clear_t) clear;
}
__DYNSYM get_mem = __LOADSYM(dynamic_library,"get_mem");
if (get_mem == NULL) {
log_dlerror();
log_err("dynlibmod: unable to load get_mem procedure from dynamic library \"%s\".", de->fname);
return 0;
} else {
de->func_get_mem = (func_get_mem_t) get_mem;
}
}
de->func_init(id, env->cfg);
de->func_init(env, id);
return 1;
}
@ -163,7 +163,7 @@ void dynlibmod_deinit(struct module_env* env, int id) {
struct dynlibmod_env* de = env->modinfo[id];
if(de == NULL)
return;
de->func_deinit(id);
de->func_deinit(env, id);
de->fname = NULL;
free(de);
}
@ -172,44 +172,23 @@ void dynlibmod_deinit(struct module_env* env, int id) {
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];
struct dynlibmod_qstate* dq = (struct dynlibmod_qstate*)qstate->minfo[id];
void * data = dq == NULL ? NULL : dq->data;
int ret = de->func_operate(id, event, qstate, data);
if (ret != 1) {
log_err("dynlibmod: dynamic library returned bad code from operate %d.", ret);
qstate->ext_state[id] = module_error;
}
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];
struct dynlibmod_qstate* dq = (struct dynlibmod_qstate*)qstate->minfo[id];
void * data = dq == NULL ? NULL : dq->data;
int ret = de->func_inform(id, qstate, super, data);
if (ret != 1) {
log_err("dynlibmod: dynamic library returned bad code from inform_super %d.", ret);
qstate->ext_state[id] = module_error;
}
de->func_inform(qstate, id, super);
}
/** dynlib module cleanup query state */
void dynlibmod_clear(struct module_qstate* qstate, int id) {
struct dynlibmod_qstate* dq;
if (qstate == NULL)
return;
struct dynlibmod_env* de = qstate->env->modinfo[id];
dq = (struct dynlibmod_qstate*)qstate->minfo[id];
verbose(VERB_ALGO, "dynlibmod: clear, id: %d, dq:%p", id, dq);
if(dq != NULL) {
/* Free qstate */
free(dq);
}
qstate->minfo[id] = NULL;
de->func_clear(qstate, id);
}
/** dynlib module alloc size routine */
@ -218,7 +197,9 @@ size_t dynlibmod_get_mem(struct module_env* env, int id) {
verbose(VERB_ALGO, "dynlibmod: get_mem, id: %d, de:%p", id, de);
if(!de)
return 0;
return sizeof(*de);
size_t size = de->func_get_mem(env, id);
return size + sizeof(*de);
}
/**

View file

@ -1,8 +1,7 @@
/*
* dynlibmod.h: module header file
*
* Copyright (c) 2009, Zdenek Vasicek (vasicek AT fit.vutbr.cz)
* Marek Vavrusa (xvavru00 AT stud.fit.vutbr.cz)
* Copyright (c) 2019, Peter Munch-Ellingsen (peterme AT peterme.net)
*
* This software is open source.
*

View file

@ -17,17 +17,15 @@
#define EXPORT
#endif
EXPORT int init(int id, struct config_file* cfg) {
EXPORT void init(struct module_env* env, int id) {
log_info("Hello world from init");
return 1;
}
EXPORT int deinit(int id) {
EXPORT void deinit(struct module_env* env, int id) {
log_info("Hello world from deinit");
return 1;
}
EXPORT int operate(int id, enum module_ev event, struct module_qstate* qstate, void* data) {
EXPORT void operate(struct module_qstate* qstate, enum module_ev event, int id, struct outbound_entry* entry) {
log_info("Hello world from operate");
if (event == module_event_new || event == module_event_pass) {
qstate->ext_state[id] = module_wait_module;
@ -36,10 +34,17 @@ EXPORT int operate(int id, enum module_ev event, struct module_qstate* qstate, v
} else {
qstate->ext_state[id] = module_error;
}
return 1;
}
EXPORT int inform_super(int id, struct module_qstate* qstate, struct module_qstate* super, void* data) {
EXPORT void inform_super(struct module_qstate* qstate, int id, struct module_qstate* super) {
log_info("Hello world from inform_super");
return 1;
}
EXPORT void clear(struct module_qstate* qstate, int id) {
log_info("Hello world from clear");
}
EXPORT size_t get_mem(struct module_env* env, int id) {
log_info("Hello world from get_mem");
return 0;
}

View file

@ -641,6 +641,7 @@ struct ub_shm_stat_info {
long long respip;
long long dnscrypt_shared_secret;
long long dnscrypt_nonce;
long long dynlib;
} mem;
};

View file

@ -261,6 +261,9 @@ static void print_mem(struct ub_shm_stat_info* shm_stat,
#ifdef USE_IPSECMOD
PR_LL("mem.mod.ipsecmod", shm_stat->mem.ipsecmod);
#endif
#ifdef WITH_DYNLIBMODULE
PR_LL("mem.mod.dynlib", shm_stat->mem.dynlib);
#endif
#ifdef USE_DNSCRYPT
PR_LL("mem.cache.dnscrypt_shared_secret",
shm_stat->mem.dnscrypt_shared_secret);

View file

@ -280,6 +280,10 @@ void shm_main_run(struct worker *worker)
#ifdef USE_IPSECMOD
shm_stat->mem.ipsecmod = (long long)mod_get_mem(&worker->env,
"ipsecmod");
#endif
#ifdef WITH_DYNLIBMODULE
shm_stat->mem.dynlib = (long long)mod_get_mem(&worker->env,
"dynlib");
#endif
}