1/**
2 * \file
3 * This file contains the dynamic library module for Unbound.
4 * This loads a dynamic library (.dll, .so) and calls that for the
5 * module actions.
6 */
7#include "config.h"
8#include "dynlibmod/dynlibmod.h"
9#include "util/module.h"
10#include "util/config_file.h"
11
12#if HAVE_WINDOWS_H
13#include <windows.h>
14#define __DYNMOD HMODULE
15#define __DYNSYM FARPROC
16#define __LOADSYM GetProcAddress
17static void log_dlerror() {
18    DWORD dwLastError = GetLastError();
19    LPSTR MessageBuffer;
20    DWORD dwBufferLength;
21    DWORD dwFormatFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER |
22        FORMAT_MESSAGE_IGNORE_INSERTS |
23        FORMAT_MESSAGE_FROM_SYSTEM ;
24    if((dwBufferLength = FormatMessageA(
25        dwFormatFlags,
26        NULL, // module to get message from (NULL == system)
27        dwLastError,
28        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language
29        (LPSTR) &MessageBuffer,
30        0,
31        NULL
32        )))
33    {
34        log_err("dynlibmod: %s (%ld)", MessageBuffer, dwLastError);
35        LocalFree(MessageBuffer);
36    }
37
38}
39
40static HMODULE open_library(const char* fname) {
41    return LoadLibrary(fname);
42}
43
44static void close_library(const char* fname, __DYNMOD handle) {
45	(void)fname;
46	(void)handle;
47}
48#else
49#include <dlfcn.h>
50#define __DYNMOD void*
51#define __DYNSYM void*
52#define __LOADSYM dlsym
53static void log_dlerror() {
54    log_err("dynlibmod: %s", dlerror());
55}
56
57static void* open_library(const char* fname) {
58    return dlopen(fname, RTLD_LAZY | RTLD_GLOBAL);
59}
60
61static void close_library(const char* fname, __DYNMOD handle) {
62	if(!handle) return;
63	if(dlclose(handle) != 0) {
64		log_err("dlclose %s: %s", fname, strerror(errno));
65	}
66}
67#endif
68
69/** module counter for multiple dynlib modules */
70static int dynlib_mod_count = 0;
71
72/** dynlib module init */
73int dynlibmod_init(struct module_env* env, int id) {
74    int dynlib_mod_idx = dynlib_mod_count++;
75    struct config_strlist* cfg_item = env->cfg->dynlib_file;
76    struct dynlibmod_env* de = (struct dynlibmod_env*)calloc(1, sizeof(struct dynlibmod_env));
77    __DYNMOD dynamic_library;
78    int i;
79    if (!de)
80    {
81        log_err("dynlibmod[%d]: malloc failure", dynlib_mod_idx);
82        return 0;
83    }
84
85    env->modinfo[id] = (void*) de;
86
87    de->fname = NULL;
88    for(i = dynlib_mod_idx;
89        i != 0 && cfg_item != NULL;
90        i--, cfg_item = cfg_item->next) {}
91
92    if (cfg_item == NULL || cfg_item->str == NULL || cfg_item->str[0] == 0) {
93        log_err("dynlibmod[%d]: no dynamic library given.", dynlib_mod_idx);
94        return 0;
95    } else {
96        de->fname = cfg_item->str;
97    }
98    verbose(VERB_ALGO, "dynlibmod[%d]: Trying to load library %s", dynlib_mod_idx, de->fname);
99    dynamic_library = open_library(de->fname);
100    de->dynamic_library = (void*)dynamic_library;
101    if (dynamic_library == NULL) {
102        log_dlerror();
103        log_err("dynlibmod[%d]: unable to load dynamic library \"%s\".", dynlib_mod_idx, de->fname);
104        return 0;
105    } else {
106	__DYNSYM initializer;
107	__DYNSYM deinitializer;
108	__DYNSYM operate;
109	__DYNSYM inform;
110	__DYNSYM clear;
111	__DYNSYM get_mem;
112        initializer = __LOADSYM(dynamic_library,"init");
113        if (initializer == NULL) {
114            log_dlerror();
115            log_err("dynlibmod[%d]: unable to load init procedure from dynamic library \"%s\".", dynlib_mod_idx, de->fname);
116            return 0;
117        } else {
118            de->func_init = (func_init_t)(void*)initializer;
119        }
120        deinitializer = __LOADSYM(dynamic_library,"deinit");
121        if (deinitializer == NULL) {
122            log_dlerror();
123            log_err("dynlibmod[%d]: unable to load deinit procedure from dynamic library \"%s\".", dynlib_mod_idx, de->fname);
124            return 0;
125        } else {
126            de->func_deinit = (func_deinit_t)(void*)deinitializer;
127        }
128        operate = __LOADSYM(dynamic_library,"operate");
129        if (operate == NULL) {
130            log_dlerror();
131            log_err("dynlibmod[%d]: unable to load operate procedure from dynamic library \"%s\".", dynlib_mod_idx, de->fname);
132            return 0;
133        } else {
134            de->func_operate = (func_operate_t)(void*)operate;
135        }
136        inform = __LOADSYM(dynamic_library,"inform_super");
137        if (inform == NULL) {
138            log_dlerror();
139            log_err("dynlibmod[%d]: unable to load inform_super procedure from dynamic library \"%s\".", dynlib_mod_idx, de->fname);
140            return 0;
141        } else {
142            de->func_inform = (func_inform_t)(void*)inform;
143        }
144        clear = __LOADSYM(dynamic_library,"clear");
145        if (clear == NULL) {
146            log_dlerror();
147            log_err("dynlibmod[%d]: unable to load clear procedure from dynamic library \"%s\".", dynlib_mod_idx, de->fname);
148            return 0;
149        } else {
150            de->func_clear = (func_clear_t)(void*)clear;
151        }
152        get_mem = __LOADSYM(dynamic_library,"get_mem");
153        if (get_mem == NULL) {
154            log_dlerror();
155            log_err("dynlibmod[%d]: unable to load get_mem procedure from dynamic library \"%s\".", dynlib_mod_idx, de->fname);
156            return 0;
157        } else {
158            de->func_get_mem = (func_get_mem_t)(void*)get_mem;
159        }
160    }
161    de->inplace_cb_delete_wrapped = &inplace_cb_delete_wrapped;
162    de->inplace_cb_register_wrapped = &inplace_cb_register_wrapped;
163    return de->func_init(env, id);
164}
165
166/** dynlib module deinit */
167void dynlibmod_deinit(struct module_env* env, int id) {
168    struct dynlibmod_env* de = env->modinfo[id];
169    if(de == NULL)
170        return;
171    de->func_deinit(env, id);
172    close_library(de->fname, (__DYNMOD)de->dynamic_library);
173    dynlib_mod_count--;
174    de->fname = NULL;
175    free(de);
176}
177
178/** dynlib module operate on a query */
179void dynlibmod_operate(struct module_qstate* qstate, enum module_ev event,
180    int id, struct outbound_entry* outbound) {
181    struct dynlibmod_env* de = qstate->env->modinfo[id];
182
183    de->func_operate(qstate, event, id, outbound);
184}
185
186/** dynlib module  */
187void dynlibmod_inform_super(struct module_qstate* qstate, int id,
188    struct module_qstate* super) {
189    struct dynlibmod_env* de = qstate->env->modinfo[id];
190
191    de->func_inform(qstate, id, super);
192}
193
194/** dynlib module cleanup query state */
195void dynlibmod_clear(struct module_qstate* qstate, int id) {
196    struct dynlibmod_env* de = qstate->env->modinfo[id];
197
198    de->func_clear(qstate, id);
199}
200
201/** dynlib module alloc size routine */
202size_t dynlibmod_get_mem(struct module_env* env, int id) {
203    struct dynlibmod_env* de = (struct dynlibmod_env*)env->modinfo[id];
204    size_t size;
205    verbose(VERB_ALGO, "dynlibmod: get_mem, id: %d, de:%p", id, de);
206    if(!de)
207        return 0;
208
209    size = de->func_get_mem(env, id);
210    return size + sizeof(*de);
211}
212
213int dynlib_inplace_cb_reply_generic(struct query_info* qinfo,
214    struct module_qstate* qstate, struct reply_info* rep, int rcode,
215    struct edns_data* edns, struct edns_option** opt_list_out,
216    struct comm_reply* repinfo, struct regional* region,
217    struct timeval* start_time, int id, void* callback) {
218    struct cb_pair* cb_pair = (struct cb_pair*) callback;
219    return ((inplace_cb_reply_func_type*) cb_pair->cb)(qinfo, qstate, rep, rcode, edns, opt_list_out, repinfo, region, start_time, id, cb_pair->cb_arg);
220}
221
222int dynlib_inplace_cb_query_generic(struct query_info* qinfo, uint16_t flags,
223    struct module_qstate* qstate, struct sockaddr_storage* addr,
224    socklen_t addrlen, uint8_t* zone, size_t zonelen, struct regional* region,
225    int id, void* callback) {
226    struct cb_pair* cb_pair = (struct cb_pair*) callback;
227    return ((inplace_cb_query_func_type*) cb_pair->cb)(qinfo, flags, qstate, addr, addrlen, zone, zonelen, region, id, cb_pair->cb_arg);
228}
229
230int dynlib_inplace_cb_edns_back_parsed(struct module_qstate* qstate,
231    int id, void* cb_args) {
232    struct cb_pair* cb_pair = (struct cb_pair*) cb_args;
233    return ((inplace_cb_edns_back_parsed_func_type*) cb_pair->cb)(qstate, id, cb_pair->cb_arg);
234}
235
236int dynlib_inplace_cb_query_response(struct module_qstate* qstate,
237    struct dns_msg* response, int id, void* cb_args) {
238    struct cb_pair* cb_pair = (struct cb_pair*) cb_args;
239    return ((inplace_cb_query_response_func_type*) cb_pair->cb)(qstate, response, id, cb_pair->cb_arg);
240}
241
242int
243inplace_cb_register_wrapped(void* cb, enum inplace_cb_list_type type, void* cbarg,
244    struct module_env* env, int id) {
245    struct cb_pair* cb_pair = malloc(sizeof(struct cb_pair));
246    if(cb_pair == NULL) {
247	log_err("dynlibmod[%d]: malloc failure", id);
248        return 0;
249    }
250    cb_pair->cb = cb;
251    cb_pair->cb_arg = cbarg;
252    if(type >= inplace_cb_reply && type <= inplace_cb_reply_servfail) {
253        return inplace_cb_register(&dynlib_inplace_cb_reply_generic, type, (void*) cb_pair, env, id);
254    } else if(type == inplace_cb_query) {
255        return inplace_cb_register(&dynlib_inplace_cb_query_generic, type, (void*) cb_pair, env, id);
256    } else if(type == inplace_cb_query_response) {
257        return inplace_cb_register(&dynlib_inplace_cb_query_response, type, (void*) cb_pair, env, id);
258    } else if(type == inplace_cb_edns_back_parsed) {
259        return inplace_cb_register(&dynlib_inplace_cb_edns_back_parsed, type, (void*) cb_pair, env, id);
260    } else {
261        free(cb_pair);
262        return 0;
263    }
264}
265
266void
267inplace_cb_delete_wrapped(struct module_env* env, enum inplace_cb_list_type type,
268    int id) {
269    struct inplace_cb* temp = env->inplace_cb_lists[type];
270    struct inplace_cb* prev = NULL;
271
272    while(temp) {
273        if(temp->id == id) {
274            if(!prev) {
275                env->inplace_cb_lists[type] = temp->next;
276                free(temp->cb_arg);
277                free(temp);
278                temp = env->inplace_cb_lists[type];
279            }
280            else {
281                prev->next = temp->next;
282                free(temp->cb_arg);
283                free(temp);
284                temp = prev->next;
285            }
286        }
287        else {
288            prev = temp;
289            temp = temp->next;
290        }
291    }
292}
293
294
295/**
296 * The module function block
297 */
298static struct module_func_block dynlibmod_block = {
299   "dynlib",
300   &dynlibmod_init, &dynlibmod_deinit, &dynlibmod_operate, &dynlibmod_inform_super,
301   &dynlibmod_clear, &dynlibmod_get_mem
302};
303
304struct module_func_block* dynlibmod_get_funcblock(void)
305{
306   return &dynlibmod_block;
307}
308