aboutsummaryrefslogtreecommitdiff
path: root/dynlibmod
diff options
context:
space:
mode:
Diffstat (limited to 'dynlibmod')
-rw-r--r--dynlibmod/dynlibmod.c301
-rw-r--r--dynlibmod/dynlibmod.h139
-rw-r--r--dynlibmod/examples/helloworld.c130
3 files changed, 570 insertions, 0 deletions
diff --git a/dynlibmod/dynlibmod.c b/dynlibmod/dynlibmod.c
new file mode 100644
index 000000000000..f9751d8c6f73
--- /dev/null
+++ b/dynlibmod/dynlibmod.c
@@ -0,0 +1,301 @@
+/**
+ * \file
+ * This file contains the dynamic library module for Unbound.
+ * This loads a dynamic library (.dll, .so) and calls that for the
+ * module actions.
+ */
+#include "config.h"
+#include "util/module.h"
+#include "util/config_file.h"
+#include "dynlibmod/dynlibmod.h"
+
+#if HAVE_WINDOWS_H
+#include <windows.h>
+#define __DYNMOD HMODULE
+#define __DYNSYM FARPROC
+#define __LOADSYM GetProcAddress
+void log_dlerror() {
+ DWORD dwLastError = GetLastError();
+ LPSTR MessageBuffer;
+ DWORD dwBufferLength;
+ DWORD dwFormatFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER |
+ FORMAT_MESSAGE_IGNORE_INSERTS |
+ FORMAT_MESSAGE_FROM_SYSTEM ;
+ if((dwBufferLength = FormatMessageA(
+ dwFormatFlags,
+ NULL, // module to get message from (NULL == system)
+ dwLastError,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language
+ (LPSTR) &MessageBuffer,
+ 0,
+ NULL
+ )))
+ {
+ log_err("dynlibmod: %s (%ld)", MessageBuffer, dwLastError);
+ LocalFree(MessageBuffer);
+ }
+
+}
+
+HMODULE open_library(const char* fname) {
+ return LoadLibrary(fname);
+}
+
+void close_library(const char* fname, __DYNMOD handle) {
+ (void)fname;
+ (void)handle;
+}
+#else
+#include <dlfcn.h>
+#define __DYNMOD void*
+#define __DYNSYM void*
+#define __LOADSYM dlsym
+void log_dlerror() {
+ log_err("dynlibmod: %s", dlerror());
+}
+
+void* open_library(const char* fname) {
+ return dlopen(fname, RTLD_LAZY | RTLD_GLOBAL);
+}
+
+void close_library(const char* fname, __DYNMOD handle) {
+ if(!handle) return;
+ if(dlclose(handle) != 0) {
+ log_err("dlclose %s: %s", fname, strerror(errno));
+ }
+}
+#endif
+
+/** module counter for multiple dynlib modules */
+static int dynlib_mod_count = 0;
+
+/** dynlib module init */
+int dynlibmod_init(struct module_env* env, int id) {
+ int dynlib_mod_idx = dynlib_mod_count++;
+ struct config_strlist* cfg_item = env->cfg->dynlib_file;
+ struct dynlibmod_env* de = (struct dynlibmod_env*)calloc(1, sizeof(struct dynlibmod_env));
+ __DYNMOD dynamic_library;
+ if (!de)
+ {
+ log_err("dynlibmod[%d]: malloc failure", dynlib_mod_idx);
+ return 0;
+ }
+
+ env->modinfo[id] = (void*) de;
+
+ de->fname = NULL;
+ for(int i = dynlib_mod_idx;
+ i != 0 && cfg_item != NULL;
+ i--, cfg_item = cfg_item->next) {}
+
+ if (cfg_item == NULL || cfg_item->str == NULL || cfg_item->str[0] == 0) {
+ log_err("dynlibmod[%d]: no dynamic library given.", dynlib_mod_idx);
+ return 0;
+ } else {
+ de->fname = cfg_item->str;
+ }
+ verbose(VERB_ALGO, "dynlibmod[%d]: Trying to load library %s", dynlib_mod_idx, de->fname);
+ dynamic_library = open_library(de->fname);
+ de->dynamic_library = (void*)dynamic_library;
+ if (dynamic_library == NULL) {
+ log_dlerror();
+ log_err("dynlibmod[%d]: unable to load dynamic library \"%s\".", dynlib_mod_idx, de->fname);
+ return 0;
+ } else {
+ __DYNSYM initializer;
+ __DYNSYM deinitializer;
+ __DYNSYM operate;
+ __DYNSYM inform;
+ __DYNSYM clear;
+ __DYNSYM get_mem;
+ initializer = __LOADSYM(dynamic_library,"init");
+ if (initializer == NULL) {
+ log_dlerror();
+ log_err("dynlibmod[%d]: unable to load init procedure from dynamic library \"%s\".", dynlib_mod_idx, de->fname);
+ return 0;
+ } else {
+ de->func_init = (func_init_t)(void*)initializer;
+ }
+ deinitializer = __LOADSYM(dynamic_library,"deinit");
+ if (deinitializer == NULL) {
+ log_dlerror();
+ log_err("dynlibmod[%d]: unable to load deinit procedure from dynamic library \"%s\".", dynlib_mod_idx, de->fname);
+ return 0;
+ } else {
+ de->func_deinit = (func_deinit_t)(void*)deinitializer;
+ }
+ operate = __LOADSYM(dynamic_library,"operate");
+ if (operate == NULL) {
+ log_dlerror();
+ log_err("dynlibmod[%d]: unable to load operate procedure from dynamic library \"%s\".", dynlib_mod_idx, de->fname);
+ return 0;
+ } else {
+ de->func_operate = (func_operate_t)(void*)operate;
+ }
+ inform = __LOADSYM(dynamic_library,"inform_super");
+ if (inform == NULL) {
+ log_dlerror();
+ log_err("dynlibmod[%d]: unable to load inform_super procedure from dynamic library \"%s\".", dynlib_mod_idx, de->fname);
+ return 0;
+ } else {
+ de->func_inform = (func_inform_t)(void*)inform;
+ }
+ clear = __LOADSYM(dynamic_library,"clear");
+ if (clear == NULL) {
+ log_dlerror();
+ log_err("dynlibmod[%d]: unable to load clear procedure from dynamic library \"%s\".", dynlib_mod_idx, de->fname);
+ return 0;
+ } else {
+ de->func_clear = (func_clear_t)(void*)clear;
+ }
+ get_mem = __LOADSYM(dynamic_library,"get_mem");
+ if (get_mem == NULL) {
+ log_dlerror();
+ log_err("dynlibmod[%d]: unable to load get_mem procedure from dynamic library \"%s\".", dynlib_mod_idx, de->fname);
+ return 0;
+ } else {
+ de->func_get_mem = (func_get_mem_t)(void*)get_mem;
+ }
+ }
+ de->inplace_cb_delete_wrapped = &inplace_cb_delete_wrapped;
+ de->inplace_cb_register_wrapped = &inplace_cb_register_wrapped;
+ return de->func_init(env, id);
+}
+
+/** dynlib module deinit */
+void dynlibmod_deinit(struct module_env* env, int id) {
+ struct dynlibmod_env* de = env->modinfo[id];
+ if(de == NULL)
+ return;
+ de->func_deinit(env, id);
+ close_library(de->fname, (__DYNMOD)de->dynamic_library);
+ dynlib_mod_count--;
+ de->fname = NULL;
+ free(de);
+}
+
+/** dynlib module operate on a query */
+void dynlibmod_operate(struct module_qstate* qstate, enum module_ev event,
+ int id, struct outbound_entry* outbound) {
+ struct dynlibmod_env* de = qstate->env->modinfo[id];
+
+ de->func_operate(qstate, event, id, outbound);
+}
+
+/** dynlib module */
+void dynlibmod_inform_super(struct module_qstate* qstate, int id,
+ struct module_qstate* super) {
+ struct dynlibmod_env* de = qstate->env->modinfo[id];
+
+ de->func_inform(qstate, id, super);
+}
+
+/** dynlib module cleanup query state */
+void dynlibmod_clear(struct module_qstate* qstate, int id) {
+ struct dynlibmod_env* de = qstate->env->modinfo[id];
+
+ de->func_clear(qstate, id);
+}
+
+/** dynlib module alloc size routine */
+size_t dynlibmod_get_mem(struct module_env* env, int id) {
+ struct dynlibmod_env* de = (struct dynlibmod_env*)env->modinfo[id];
+ size_t size;
+ verbose(VERB_ALGO, "dynlibmod: get_mem, id: %d, de:%p", id, de);
+ if(!de)
+ return 0;
+
+ size = de->func_get_mem(env, 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;
+ return ((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;
+ return ((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;
+ return ((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;
+ return ((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
+ */
+static struct module_func_block dynlibmod_block = {
+ "dynlib",
+ &dynlibmod_init, &dynlibmod_deinit, &dynlibmod_operate, &dynlibmod_inform_super,
+ &dynlibmod_clear, &dynlibmod_get_mem
+};
+
+struct module_func_block* dynlibmod_get_funcblock(void)
+{
+ return &dynlibmod_block;
+}
diff --git a/dynlibmod/dynlibmod.h b/dynlibmod/dynlibmod.h
new file mode 100644
index 000000000000..c34cf0e88d92
--- /dev/null
+++ b/dynlibmod/dynlibmod.h
@@ -0,0 +1,139 @@
+/*
+ * dynlibmod.h: module header file
+ *
+ * Copyright (c) 2019, Peter Munch-Ellingsen (peterme AT peterme.net)
+ *
+ * This software is open source.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * * Neither the name of the organization nor the names of its
+ * contributors may be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+/**
+ * \file
+ * Dynamic loading module for unbound. Loads dynamic library.
+ */
+#ifndef DYNLIBMOD_H
+#define DYNLIBMOD_H
+#include "util/module.h"
+#include "services/outbound_list.h"
+
+/**
+ * Get the module function block.
+ * @return: function block with function pointers to module methods.
+ */
+struct module_func_block* dynlibmod_get_funcblock(void);
+
+/** dynlib module init */
+int dynlibmod_init(struct module_env* env, int id);
+
+/** dynlib module deinit */
+void dynlibmod_deinit(struct module_env* env, int id);
+
+/** dynlib module operate on a query */
+void dynlibmod_operate(struct module_qstate* qstate, enum module_ev event,
+ int id, struct outbound_entry* outbound);
+
+/** dynlib module */
+void dynlibmod_inform_super(struct module_qstate* qstate, int id,
+ struct module_qstate* super);
+
+/** dynlib module cleanup query state */
+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 int (*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;
+ /** dynamic library handle */
+ void* dynamic_library;
+ /** 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
new file mode 100644
index 000000000000..acb6b5d9bda6
--- /dev/null
+++ b/dynlibmod/examples/helloworld.c
@@ -0,0 +1,130 @@
+/**
+ * \file
+ *
+ * This is an example to show how dynamic libraries can be made to work with
+ * unbound. To build a .so file simply run:
+ * 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
+ * to cross-compile a 64-bit Windows DLL.
+ */
+
+#include "../../config.h"
+#include "../../util/module.h"
+#include "../../sldns/parseutil.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 int init(struct module_env* env, int id) {
+ log_info("dynlib: 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;
+ return 1;
+}
+
+/* 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("dynlib: 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);
+}
+
+/* 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("dynlib: hello world from operate");
+ log_info("dynlib: incoming query: %s %s(%d) %s(%d)",
+ qstate->qinfo.qname,
+ sldns_lookup_by_id(sldns_rr_classes, qstate->qinfo.qclass)->name,
+ qstate->qinfo.qclass,
+ sldns_rr_descript(qstate->qinfo.qtype)->_name,
+ qstate->qinfo.qtype);
+ 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("dynlib: already has data!");
+ qstate->ext_state[id] = module_error;
+ }
+ } else if (event == module_event_moddone) {
+ qstate->ext_state[id] = module_finished;
+ } else {
+ qstate->ext_state[id] = module_error;
+ }
+}
+
+/* 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("dynlib: 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("dynlib: 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("dynlib: 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("dynlib: hello world from callback");
+ struct dynlibmod_env* env = qstate->env->modinfo[id];
+ if (env->dyn_env != NULL) {
+ log_info("dynlib: 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;
+}