]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
Add inplace callback to dynlibmod, improve example
authorPMunch <peterme@peterme.net>
Fri, 1 Nov 2019 09:44:26 +0000 (10:44 +0100)
committerPMunch <peterme@peterme.net>
Fri, 1 Nov 2019 09:44:26 +0000 (10:44 +0100)
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.

dynlibmod/dynlibmod.c
dynlibmod/dynlibmod.h
dynlibmod/examples/helloworld.c
util/fptr_wlist.c

index 977f4504714faf918de93b338635e3de110bc0d7..7c77009d72ff4fea78088099766714c3edc4afdd 100644 (file)
@@ -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
  */
index 1d826a1c882d642b5af9c7132d37c7d3819791c3..a8ba23cc9af6a3e3bb7d7bab677c3e9678453213 100644 (file)
@@ -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 */
index 6479d84601597f03ee2d697b4dc43b201dbc1303..3d7708af00f372a0d6a29fd781b7591428bdb294 100644 (file)
@@ -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;
+}
index 20eaeb13704e30d7689fa032f973c4d95321e1ef..dfd05957f05c17bebed4afd71b0e9ea415e710b1 100644 (file)
@@ -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;
 }