}
#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;
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;
}
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
*/
/** 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 */
* 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 {
}
}
-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;
+}
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;
#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;
return 1;
#else
(void)fptr;
+#endif
+#ifdef WITH_DYNLIBMODULE
+ if(fptr == &dynlib_inplace_cb_edns_back_parsed)
+ return 1;
#endif
return 0;
}
return 1;
#else
(void)fptr;
+#endif
+#ifdef WITH_DYNLIBMODULE
+ if(fptr == &dynlib_inplace_cb_query_response)
+ return 1;
#endif
return 0;
}