Add inplace callback to dynlibmod, improve example

This adds the possibility to properly register inplace callbacks in the
dynamic library module. It works by creating a wrapper procedure that
is available to the dynamic library and will call the given callback
through a whitelisted callback function.

The dynamic library example has already been improved to include
comments and some simple examples on allocating and deallocating memory
and registering callbacks.
This commit is contained in:
PMunch 2019-11-01 10:44:26 +01:00
parent f79811435b
commit d104d3be22
4 changed files with 243 additions and 32 deletions

View file

@ -48,35 +48,6 @@ void* open_library(const char* fname) {
} }
#endif #endif
/**
* Global state for the module.
*/
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. */
const char* fname;
/** 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;
};
/** dynlib module init */ /** dynlib module init */
int dynlibmod_init(struct module_env* env, int id) { int dynlibmod_init(struct module_env* env, int id) {
static int dynlib_mod_count; static int dynlib_mod_count;
@ -159,6 +130,8 @@ int dynlibmod_init(struct module_env* env, int id) {
de->func_get_mem = (func_get_mem_t) get_mem; de->func_get_mem = (func_get_mem_t) get_mem;
} }
} }
de->inplace_cb_delete_wrapped = &inplace_cb_delete_wrapped;
de->inplace_cb_register_wrapped = &inplace_cb_register_wrapped;
de->func_init(env, id); de->func_init(env, id);
return 1; return 1;
} }
@ -207,6 +180,83 @@ size_t dynlibmod_get_mem(struct module_env* env, int id) {
return size + sizeof(*de); 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;
((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;
((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;
((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;
((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 * The module function block
*/ */

View file

@ -67,4 +67,71 @@ void dynlibmod_clear(struct module_qstate* qstate, int id);
/** dynlib module alloc size routine */ /** dynlib module alloc size routine */
size_t dynlibmod_get_mem(struct module_env* env, int id); 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 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);
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;
/** 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 */ #endif /* DYNLIBMOD_H */

View file

@ -4,31 +4,70 @@
* gcc -I../.. -shared -Wall -Werror -fpic -o helloworld.so helloworld.c * gcc -I../.. -shared -Wall -Werror -fpic -o helloworld.so helloworld.c
* And to build for windows, first make unbound with the --with-dynlibmod * And to build for windows, first make unbound with the --with-dynlibmod
* switch, then use this command: * 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 * 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. * to cross-compile a 64-bit Windows DLL.
*/ */
#include "../../config.h" #include "../../config.h"
#include "../../util/module.h" #include "../../util/module.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 #ifdef HAVE_WINDOWS_H
#define EXPORT __declspec(dllexport) #define EXPORT __declspec(dllexport)
#else #else
#define EXPORT #define EXPORT
#endif #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 void init(struct module_env* env, int id) { EXPORT void init(struct module_env* env, int id) {
log_info("Hello world from init"); log_info("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;
} }
/* 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) { EXPORT void deinit(struct module_env* env, int id) {
log_info("Hello world from deinit"); log_info("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);
} }
EXPORT void operate(struct module_qstate* qstate, enum module_ev event, int id, struct outbound_entry* entry) { /* 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("Hello world from operate"); log_info("Hello world from operate");
if (event == module_event_new || event == module_event_pass) { if (event == module_event_new || event == module_event_pass) {
qstate->ext_state[id] = module_wait_module; 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("Already has data!");
qstate->ext_state[id] = module_error;
}
} else if (event == module_event_moddone) { } else if (event == module_event_moddone) {
qstate->ext_state[id] = module_finished; qstate->ext_state[id] = module_finished;
} else { } else {
@ -36,15 +75,46 @@ EXPORT void operate(struct module_qstate* qstate, enum module_ev event, int id,
} }
} }
EXPORT void inform_super(struct module_qstate* qstate, int id, struct module_qstate* super) { /* 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("Hello world from inform_super"); log_info("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) { EXPORT void clear(struct module_qstate* qstate, int id) {
log_info("Hello world from clear"); log_info("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) { EXPORT size_t get_mem(struct module_env* env, int id) {
log_info("Hello world from get_mem"); log_info("Hello world from get_mem");
return 0; 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("Hello world from callback");
struct dynlibmod_env* env = qstate->env->modinfo[id];
if (env->dyn_env != NULL) {
log_info("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;
}

View file

@ -585,18 +585,30 @@ int fptr_whitelist_inplace_cb_reply_generic(inplace_cb_reply_func_type* fptr,
if(type == inplace_cb_reply) { if(type == inplace_cb_reply) {
#ifdef WITH_PYTHONMODULE #ifdef WITH_PYTHONMODULE
if(fptr == &python_inplace_cb_reply_generic) return 1; if(fptr == &python_inplace_cb_reply_generic) return 1;
#endif
#ifdef WITH_DYNLIBMODULE
if(fptr == &dynlib_inplace_cb_reply_generic) return 1;
#endif #endif
} else if(type == inplace_cb_reply_cache) { } else if(type == inplace_cb_reply_cache) {
#ifdef WITH_PYTHONMODULE #ifdef WITH_PYTHONMODULE
if(fptr == &python_inplace_cb_reply_generic) return 1; if(fptr == &python_inplace_cb_reply_generic) return 1;
#endif
#ifdef WITH_DYNLIBMODULE
if(fptr == &dynlib_inplace_cb_reply_generic) return 1;
#endif #endif
} else if(type == inplace_cb_reply_local) { } else if(type == inplace_cb_reply_local) {
#ifdef WITH_PYTHONMODULE #ifdef WITH_PYTHONMODULE
if(fptr == &python_inplace_cb_reply_generic) return 1; if(fptr == &python_inplace_cb_reply_generic) return 1;
#endif
#ifdef WITH_DYNLIBMODULE
if(fptr == &dynlib_inplace_cb_reply_generic) return 1;
#endif #endif
} else if(type == inplace_cb_reply_servfail) { } else if(type == inplace_cb_reply_servfail) {
#ifdef WITH_PYTHONMODULE #ifdef WITH_PYTHONMODULE
if(fptr == &python_inplace_cb_reply_generic) return 1; if(fptr == &python_inplace_cb_reply_generic) return 1;
#endif
#ifdef WITH_DYNLIBMODULE
if(fptr == &dynlib_inplace_cb_reply_generic) return 1;
#endif #endif
} }
return 0; return 0;
@ -611,6 +623,10 @@ int fptr_whitelist_inplace_cb_query(inplace_cb_query_func_type* fptr)
#ifdef WITH_PYTHONMODULE #ifdef WITH_PYTHONMODULE
if(fptr == &python_inplace_cb_query_generic) if(fptr == &python_inplace_cb_query_generic)
return 1; return 1;
#endif
#ifdef WITH_DYNLIBMODULE
if(fptr == &dynlib_inplace_cb_query_generic)
return 1;
#endif #endif
(void)fptr; (void)fptr;
return 0; return 0;
@ -624,6 +640,10 @@ int fptr_whitelist_inplace_cb_edns_back_parsed(
return 1; return 1;
#else #else
(void)fptr; (void)fptr;
#endif
#ifdef WITH_DYNLIBMODULE
if(fptr == &dynlib_inplace_cb_edns_back_parsed)
return 1;
#endif #endif
return 0; return 0;
} }
@ -636,6 +656,10 @@ int fptr_whitelist_inplace_cb_query_response(
return 1; return 1;
#else #else
(void)fptr; (void)fptr;
#endif
#ifdef WITH_DYNLIBMODULE
if(fptr == &dynlib_inplace_cb_query_response)
return 1;
#endif #endif
return 0; return 0;
} }