1 /**
2  * \file
3  *
4  * This is an example to show how dynamic libraries can be made to work with
5  * unbound. To build a .so file simply run:
6  *   gcc -I../.. -shared -Wall -Werror -fpic  -o helloworld.so helloworld.c
7  * And to build for windows, first make unbound with the --with-dynlibmod
8  * switch, then use this command:
9  *   x86_64-w64-mingw32-gcc -m64 -I../.. -shared -Wall -Werror -fpic
10  *      -o helloworld.dll helloworld.c -L../.. -l:libunbound.dll.a
11  * to cross-compile a 64-bit Windows DLL.  The libunbound.dll.a is produced
12  * by the compile step that makes unbound.exe and allows the dynlib dll to
13  * access definitions in unbound.exe.
14  */
15 
16 #include "../../config.h"
17 #include "../../util/module.h"
18 #include "../../sldns/parseutil.h"
19 #include "../dynlibmod.h"
20 
21 /* Declare the EXPORT macro that expands to exporting the symbol for DLLs when
22  * compiling for Windows. All procedures marked with EXPORT in this example are
23  * called directly by the dynlib module and must be present for the module to
24  * load correctly. */
25 #ifdef HAVE_WINDOWS_H
26 #define EXPORT __declspec(dllexport)
27 #else
28 #define EXPORT
29 #endif
30 
31 /* Forward declare a callback, implemented at the bottom of this file */
32 int reply_callback(struct query_info* qinfo,
33     struct module_qstate* qstate, struct reply_info* rep, int rcode,
34     struct edns_data* edns, struct edns_option** opt_list_out,
35     struct comm_reply* repinfo, struct regional* region,
36     struct timeval* start_time, int id, void* callback);
37 
38 /* Init is called when the module is first loaded. It should be used to set up
39  * the environment for this module and do any other initialisation required. */
40 EXPORT int init(struct module_env* env, int id) {
41     log_info("dynlib: hello world from init");
42     struct dynlibmod_env* de = (struct dynlibmod_env*) env->modinfo[id];
43     de->inplace_cb_register_wrapped(&reply_callback,
44                                     inplace_cb_reply,
45                                     NULL, env, id);
46     struct dynlibmod_env* local_env = env->modinfo[id];
47     local_env->dyn_env = NULL;
48     return 1;
49 }
50 
51 /* Deinit is run as the program is shutting down. It should be used to clean up
52  * the environment and any left over data. */
53 EXPORT void deinit(struct module_env* env, int id) {
54     log_info("dynlib: hello world from deinit");
55     struct dynlibmod_env* de = (struct dynlibmod_env*) env->modinfo[id];
56     de->inplace_cb_delete_wrapped(env, inplace_cb_reply, id);
57     if (de->dyn_env != NULL) free(de->dyn_env);
58 }
59 
60 /* Operate is called every time a query passes by this module. The event can be
61  * used to determine which direction in the module chain it came from. */
62 EXPORT void operate(struct module_qstate* qstate, enum module_ev event,
63                     int id, struct outbound_entry* entry) {
64     log_info("dynlib: hello world from operate");
65     log_info("dynlib: incoming query: %s %s(%d) %s(%d)",
66             qstate->qinfo.qname,
67             sldns_lookup_by_id(sldns_rr_classes, qstate->qinfo.qclass)->name,
68             qstate->qinfo.qclass,
69             sldns_rr_descript(qstate->qinfo.qtype)->_name,
70             qstate->qinfo.qtype);
71     if (event == module_event_new || event == module_event_pass) {
72         qstate->ext_state[id] = module_wait_module;
73         struct dynlibmod_env* env = qstate->env->modinfo[id];
74         if (env->dyn_env == NULL) {
75             env->dyn_env = calloc(3, sizeof(int));
76             ((int *)env->dyn_env)[0] = 42;
77             ((int *)env->dyn_env)[1] = 102;
78             ((int *)env->dyn_env)[2] = 192;
79         } else {
80             log_err("dynlib: already has data!");
81             qstate->ext_state[id] = module_error;
82         }
83     } else if (event == module_event_moddone) {
84         qstate->ext_state[id] = module_finished;
85     } else {
86         qstate->ext_state[id] = module_error;
87     }
88 }
89 
90 /* Inform super is called when a query is completed or errors out, but only if
91  * a sub-query has been registered to it by this module. Look at
92  * mesh_attach_sub in services/mesh.h to see how this is done. */
93 EXPORT void inform_super(struct module_qstate* qstate, int id,
94                          struct module_qstate* super) {
95     log_info("dynlib: hello world from inform_super");
96 }
97 
98 /* Clear is called once a query is complete and the response has been sent
99  * back. It is used to clear up any per-query allocations. */
100 EXPORT void clear(struct module_qstate* qstate, int id) {
101     log_info("dynlib: hello world from clear");
102     struct dynlibmod_env* env = qstate->env->modinfo[id];
103     if (env->dyn_env != NULL) {
104         free(env->dyn_env);
105         env->dyn_env = NULL;
106     }
107 }
108 
109 /* Get mem is called when Unbound is printing performance information. This
110  * only happens explicitly and is only used to show memory usage to the user. */
111 EXPORT size_t get_mem(struct module_env* env, int id) {
112     log_info("dynlib: hello world from get_mem");
113     return 0;
114 }
115 
116 /* The callback that was forward declared earlier. It is registered in the init
117  * procedure to run when a query is being replied to. */
118 int reply_callback(struct query_info* qinfo,
119     struct module_qstate* qstate, struct reply_info* rep, int rcode,
120     struct edns_data* edns, struct edns_option** opt_list_out,
121     struct comm_reply* repinfo, struct regional* region,
122     struct timeval* start_time, int id, void* callback) {
123     log_info("dynlib: hello world from callback");
124     struct dynlibmod_env* env = qstate->env->modinfo[id];
125     if (env->dyn_env != NULL) {
126         log_info("dynlib: numbers gotten from query: %d, %d, and %d",
127             ((int *)env->dyn_env)[0],
128             ((int *)env->dyn_env)[1],
129             ((int *)env->dyn_env)[2]);
130     }
131     return 0;
132 }
133