From: PMunch Date: Fri, 1 Nov 2019 09:44:26 +0000 (+0100) Subject: Add inplace callback to dynlibmod, improve example X-Git-Tag: release-1.11.0~45^2^2~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=d104d3be2243dbf0d678ee5e939acb78637f58e4;p=thirdparty%2Funbound.git 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. --- diff --git a/dynlibmod/dynlibmod.c b/dynlibmod/dynlibmod.c index 977f45047..7c77009d7 100644 --- a/dynlibmod/dynlibmod.c +++ b/dynlibmod/dynlibmod.c @@ -48,35 +48,6 @@ void* open_library(const char* fname) { } #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 */ int dynlibmod_init(struct module_env* env, int id) { 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->inplace_cb_delete_wrapped = &inplace_cb_delete_wrapped; + de->inplace_cb_register_wrapped = &inplace_cb_register_wrapped; de->func_init(env, id); return 1; } @@ -207,6 +180,83 @@ size_t dynlibmod_get_mem(struct module_env* env, int 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; + ((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 */ diff --git a/dynlibmod/dynlibmod.h b/dynlibmod/dynlibmod.h index 1d826a1c8..a8ba23cc9 100644 --- a/dynlibmod/dynlibmod.h +++ b/dynlibmod/dynlibmod.h @@ -67,4 +67,71 @@ 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 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 */ diff --git a/dynlibmod/examples/helloworld.c b/dynlibmod/examples/helloworld.c index 6479d8460..3d7708af0 100644 --- a/dynlibmod/examples/helloworld.c +++ b/dynlibmod/examples/helloworld.c @@ -4,31 +4,70 @@ * 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 + * 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 "../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 void init(struct module_env* env, int id) { 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) { 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"); 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("Already has data!"); + qstate->ext_state[id] = module_error; + } } else if (event == module_event_moddone) { qstate->ext_state[id] = module_finished; } 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"); } +/* 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("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("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("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; +} diff --git a/util/fptr_wlist.c b/util/fptr_wlist.c index 20eaeb137..dfd05957f 100644 --- a/util/fptr_wlist.c +++ b/util/fptr_wlist.c @@ -585,18 +585,30 @@ int fptr_whitelist_inplace_cb_reply_generic(inplace_cb_reply_func_type* fptr, if(type == inplace_cb_reply) { #ifdef WITH_PYTHONMODULE if(fptr == &python_inplace_cb_reply_generic) return 1; +#endif +#ifdef WITH_DYNLIBMODULE + if(fptr == &dynlib_inplace_cb_reply_generic) return 1; #endif } else if(type == inplace_cb_reply_cache) { #ifdef WITH_PYTHONMODULE if(fptr == &python_inplace_cb_reply_generic) return 1; +#endif +#ifdef WITH_DYNLIBMODULE + if(fptr == &dynlib_inplace_cb_reply_generic) return 1; #endif } else if(type == inplace_cb_reply_local) { #ifdef WITH_PYTHONMODULE if(fptr == &python_inplace_cb_reply_generic) return 1; +#endif +#ifdef WITH_DYNLIBMODULE + if(fptr == &dynlib_inplace_cb_reply_generic) return 1; #endif } else if(type == inplace_cb_reply_servfail) { #ifdef WITH_PYTHONMODULE if(fptr == &python_inplace_cb_reply_generic) return 1; +#endif +#ifdef WITH_DYNLIBMODULE + if(fptr == &dynlib_inplace_cb_reply_generic) return 1; #endif } return 0; @@ -611,6 +623,10 @@ int fptr_whitelist_inplace_cb_query(inplace_cb_query_func_type* fptr) #ifdef WITH_PYTHONMODULE if(fptr == &python_inplace_cb_query_generic) return 1; +#endif +#ifdef WITH_DYNLIBMODULE + if(fptr == &dynlib_inplace_cb_query_generic) + return 1; #endif (void)fptr; return 0; @@ -624,6 +640,10 @@ int fptr_whitelist_inplace_cb_edns_back_parsed( return 1; #else (void)fptr; +#endif +#ifdef WITH_DYNLIBMODULE + if(fptr == &dynlib_inplace_cb_edns_back_parsed) + return 1; #endif return 0; } @@ -636,6 +656,10 @@ int fptr_whitelist_inplace_cb_query_response( return 1; #else (void)fptr; +#endif +#ifdef WITH_DYNLIBMODULE + if(fptr == &dynlib_inplace_cb_query_response) + return 1; #endif return 0; }