dnscrypto-proxy: Update to release 1.3.0
[tomato.git] / release / src / router / dnscrypt / src / proxy / plugin_support.c
blob7f283d02b10e5849dc556aeccbcb3cdc2ec3b705
2 #include <config.h>
4 #include <sys/types.h>
5 #ifndef _WIN32
6 # include <sys/stat.h>
7 #endif
9 #include <assert.h>
10 #include <errno.h>
11 #include <limits.h>
12 #include <stdint.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #ifndef _WIN32
17 # include <unistd.h>
18 #endif
20 #include <dnscrypt/plugin.h>
21 #include <ltdl.h>
23 #include "logger.h"
24 #include "plugin_support.h"
25 #include "plugin_support_p.h"
26 #include "queue.h"
28 int
29 plugin_support_add_option(DCPluginSupport * const dcps, char * const arg)
31 char **argv;
33 if (dcps->argc >= INT_MAX) {
34 return -1;
36 if (SIZE_MAX / sizeof *argv <= (unsigned int) (dcps->argc + 2U) ||
37 (argv = realloc(dcps->argv, (unsigned int) (dcps->argc + 2U) *
38 sizeof *argv)) == NULL) {
39 return -1;
41 argv[dcps->argc++] = arg;
42 argv[dcps->argc] = NULL;
43 dcps->argv = argv;
45 return 0;
48 static void *
49 plugin_support_load_symbol(DCPluginSupport * const dcps,
50 const char * const symbol)
52 assert(dcps->handle != NULL);
54 return lt_dlsym(dcps->handle, symbol);
57 static void *
58 plugin_support_load_symbol_err(DCPluginSupport * const dcps,
59 const char * const symbol)
61 void *fn;
63 if ((fn = plugin_support_load_symbol(dcps, symbol)) == NULL) {
64 logger(NULL, LOG_ERR, "Plugin: symbol [%s] not found in [%s]",
65 symbol, dcps->plugin_file);
67 return fn;
70 static const char *
71 plugin_support_description(DCPluginSupport * const dcps)
73 DCPluginDescription dcplugin_description;
74 const char *description;
76 assert(dcps != NULL);
77 if ((dcplugin_description =
78 plugin_support_load_symbol(dcps, "dcplugin_description")) == NULL) {
79 return NULL;
81 if ((description = dcplugin_description(dcps->plugin)) == NULL ||
82 *description == 0) {
83 return NULL;
85 return description;
88 static int
89 plugin_support_call_init(DCPluginSupport * const dcps)
91 DCPluginInit dcplugin_init;
92 const char *description;
94 if ((dcplugin_init =
95 plugin_support_load_symbol_err(dcps, "dcplugin_init")) == NULL) {
96 return -1;
98 assert(dcps->argc > 0 && dcps->argv != NULL);
99 if (dcplugin_init(dcps->plugin, dcps->argc, dcps->argv) != 0) {
100 logger(NULL, LOG_ERR, "Unable to initialize plugin [%s]",
101 dcps->plugin_file);
102 return -1;
104 dcps->sync_post_filter =
105 plugin_support_load_symbol(dcps, "dcplugin_sync_post_filter");
106 dcps->sync_pre_filter =
107 plugin_support_load_symbol(dcps, "dcplugin_sync_pre_filter");
108 if ((description = plugin_support_description(dcps)) == NULL) {
109 logger_noformat(NULL, LOG_INFO, "Plugin loaded");
110 } else {
111 logger(NULL, LOG_INFO, "Loaded plugin: [%s]", description);
113 return 0;
116 static int
117 plugin_support_check_permissions(const char * const plugin_file)
119 assert(plugin_file != NULL);
121 #ifndef _WIN32
122 struct stat st;
124 if (stat(plugin_file, &st) != 0 || !S_ISREG(st.st_mode)) {
125 return -1;
127 # ifndef RELAXED_PLUGINS_PERMISSIONS
128 const uid_t uid = getuid();
130 if (st.st_uid != (uid_t) 0) {
131 if (uid == (uid_t) 0) {
132 errno = EPERM;
133 return -1;
135 if (access(plugin_file, W_OK) != 0) {
136 return -1;
139 # endif
140 #endif
142 return 0;
145 static int
146 plugin_support_load(DCPluginSupport * const dcps)
148 lt_dladvise advise;
149 lt_dlhandle handle;
151 assert(dcps != NULL && dcps->plugin_file != NULL);
152 assert(dcps->handle == NULL);
153 if (lt_dladvise_init(&advise) != 0) {
154 return -1;
156 lt_dladvise_local(&advise);
157 lt_dladvise_ext(&advise);
158 logger(NULL, LOG_INFO, "Loading plugin [%s]", dcps->plugin_file);
160 if (plugin_support_check_permissions(dcps->plugin_file) != 0) {
161 logger(NULL, LOG_ERR, "Plugin [%s] can't be loaded: [%s]",
162 dcps->plugin_file, strerror(errno));
163 lt_dladvise_destroy(&advise);
164 return -1;
166 if ((handle = lt_dlopenadvise(dcps->plugin_file, advise)) == NULL) {
167 logger(NULL, LOG_ERR, "Unable to load [%s]: [%s]",
168 dcps->plugin_file, lt_dlerror());
169 lt_dladvise_destroy(&advise);
170 return -1;
172 lt_dladvise_destroy(&advise);
173 dcps->handle = handle;
175 return plugin_support_call_init(dcps);
178 static int
179 plugin_support_unload(DCPluginSupport * const dcps)
181 DCPluginDestroy dcplugin_destroy;
183 if (dcps->handle == NULL) {
184 return 0;
186 dcplugin_destroy = plugin_support_load_symbol(dcps, "dcplugin_destroy");
187 if (dcplugin_destroy != NULL) {
188 dcplugin_destroy(dcps->plugin);
190 if (lt_dlclose(dcps->handle) != 0) {
191 return -1;
193 dcps->handle = NULL;
195 return 0;
198 static char *
199 plugin_support_expand_plugin_file(const char * const plugin_file)
201 char *expanded_plugin_file;
202 size_t plugin_file_len;
203 size_t plugins_root_len = sizeof PLUGINS_ROOT - (size_t) 1U;
204 size_t sizeof_expanded_plugin_file;
206 #ifdef ENABLE_PLUGINS_ROOT
207 if (strstr(plugin_file, "..") != NULL || *plugin_file == '/') {
208 return NULL;
210 if (strncmp(plugin_file, PLUGINS_ROOT, plugins_root_len) == 0) {
211 return strdup(plugin_file);
213 #else
214 # ifdef _WIN32
215 const char *chr_column;
216 const char *chr_pathsep;
218 if (((chr_pathsep = strchr(plugin_file, '/')) != NULL ||
219 (chr_pathsep = strchr(plugin_file, '\\')) != NULL) &&
220 (chr_pathsep == plugin_file ||
221 ((chr_column = strchr(plugin_file, ':')) != NULL &&
222 chr_column - plugin_file < chr_pathsep - plugin_file))) {
223 return strdup(plugin_file);
225 # else
226 if (*plugin_file == '/') {
227 return strdup(plugin_file);
229 # endif
230 #endif
231 plugin_file_len = strlen(plugin_file);
232 assert(SIZE_MAX - plugins_root_len > plugin_file_len);
233 sizeof_expanded_plugin_file = plugins_root_len + plugin_file_len + 1U;
234 if ((expanded_plugin_file = malloc(sizeof_expanded_plugin_file)) == NULL) {
235 return NULL;
237 memcpy(expanded_plugin_file, PLUGINS_ROOT, plugins_root_len);
238 memcpy(expanded_plugin_file + plugins_root_len, plugin_file,
239 plugin_file_len + 1U);
241 return expanded_plugin_file;
244 DCPluginSupport *
245 plugin_support_new(const char * const plugin_file)
247 DCPluginSupport *dcps;
249 if ((dcps = calloc((size_t) 1U, sizeof *dcps)) == NULL) {
250 return NULL;
252 if ((dcps->plugin = calloc((size_t) 1U, sizeof *dcps->plugin)) == NULL) {
253 free(dcps);
254 return NULL;
256 assert(plugin_file != NULL && *plugin_file != 0);
257 if ((dcps->plugin_file =
258 plugin_support_expand_plugin_file(plugin_file)) == NULL) {
259 free(dcps);
260 return NULL;
262 dcps->argv = NULL;
263 dcps->handle = NULL;
264 dcps->sync_post_filter = NULL;
265 dcps->sync_pre_filter = NULL;
267 return dcps;
270 void
271 plugin_support_free(DCPluginSupport * const dcps)
273 plugin_support_unload(dcps);
274 assert(dcps->plugin_file != NULL && *dcps->plugin_file != 0);
275 assert(dcps->plugin != NULL);
276 free(dcps->plugin);
277 free(dcps->plugin_file);
278 dcps->plugin_file = NULL;
279 free(dcps->argv);
280 dcps->argv = NULL;
281 free(dcps);
284 DCPluginSupportContext *
285 plugin_support_context_new(void)
287 DCPluginSupportContext *dcps_context;
289 if ((dcps_context = calloc((size_t) 1U, sizeof *dcps_context)) == NULL) {
290 return NULL;
292 SLIST_INIT(&dcps_context->dcps_list);
294 return dcps_context;
298 plugin_support_context_insert(DCPluginSupportContext * const dcps_context,
299 DCPluginSupport * const dcps)
301 assert(dcps_context != NULL);
302 assert(dcps != NULL);
303 SLIST_INSERT_HEAD(&dcps_context->dcps_list, dcps, next);
305 return 0;
309 plugin_support_context_remove(DCPluginSupportContext * const dcps_context,
310 DCPluginSupport * const dcps)
312 assert(! SLIST_EMPTY(&dcps_context->dcps_list));
313 SLIST_REMOVE(&dcps_context->dcps_list, dcps, DCPluginSupport_, next);
315 return 0;
318 void
319 plugin_support_context_free(DCPluginSupportContext * const dcps_context)
321 DCPluginSupport *dcps;
322 DCPluginSupport *dcps_tmp;
324 SLIST_FOREACH_SAFE(dcps, &dcps_context->dcps_list, next, dcps_tmp) {
325 plugin_support_free(dcps);
327 if (dcps_context->lt_enabled != 0) {
328 lt_dlexit();
330 free(dcps_context);
334 plugin_support_context_load(DCPluginSupportContext * const dcps_context)
336 DCPluginSupport *dcps;
337 _Bool failed = 0;
339 assert(dcps_context != NULL);
340 if (dcps_context->lt_enabled == 0 && lt_dlinit() != 0) {
341 return -1;
343 SLIST_FOREACH(dcps, &dcps_context->dcps_list, next) {
344 if (plugin_support_load(dcps) != 0) {
345 failed = 1;
348 if (failed != 0) {
349 return -1;
351 return 0;
354 static DCPluginSyncFilterResult
355 plugin_support_context_get_result_from_dcps(DCPluginSyncFilterResult result,
356 DCPluginSyncFilterResult result_dcps)
358 switch (result_dcps) {
359 case DCP_SYNC_FILTER_RESULT_OK:
360 break;
361 case DCP_SYNC_FILTER_RESULT_DIRECT:
362 if (result == DCP_SYNC_FILTER_RESULT_OK) {
363 result = DCP_SYNC_FILTER_RESULT_DIRECT;
365 break;
366 case DCP_SYNC_FILTER_RESULT_KILL:
367 if (result == DCP_SYNC_FILTER_RESULT_OK) {
368 result = DCP_SYNC_FILTER_RESULT_KILL;
370 break;
371 case DCP_SYNC_FILTER_RESULT_ERROR:
372 if (result != DCP_SYNC_FILTER_RESULT_FATAL) {
373 result = DCP_SYNC_FILTER_RESULT_ERROR;
375 break;
376 case DCP_SYNC_FILTER_RESULT_FATAL:
377 default:
378 result = DCP_SYNC_FILTER_RESULT_FATAL;
380 return result;
383 DCPluginSyncFilterResult
384 plugin_support_context_apply_sync_post_filters(DCPluginSupportContext *dcps_context,
385 DCPluginDNSPacket *dcp_packet)
387 DCPluginSupport *dcps;
388 const size_t dns_packet_max_len = dcp_packet->dns_packet_max_len;
389 DCPluginSyncFilterResult result = DCP_SYNC_FILTER_RESULT_OK;
390 DCPluginSyncFilterResult result_dcps;
392 assert(dcp_packet->dns_packet != NULL &&
393 dcp_packet->dns_packet_len_p != NULL &&
394 *dcp_packet->dns_packet_len_p > (size_t) 0U);
395 SLIST_FOREACH(dcps, &dcps_context->dcps_list, next) {
396 if (dcps->sync_post_filter != NULL) {
397 result_dcps = dcps->sync_post_filter(dcps->plugin, dcp_packet);
398 result = plugin_support_context_get_result_from_dcps(result,
399 result_dcps);
400 assert(*dcp_packet->dns_packet_len_p <= dns_packet_max_len);
401 assert(*dcp_packet->dns_packet_len_p > (size_t) 0U);
404 return result;
407 DCPluginSyncFilterResult
408 plugin_support_context_apply_sync_pre_filters(DCPluginSupportContext *dcps_context,
409 DCPluginDNSPacket *dcp_packet)
411 DCPluginSupport *dcps;
412 const size_t dns_packet_max_len = dcp_packet->dns_packet_max_len;
413 DCPluginSyncFilterResult result = DCP_SYNC_FILTER_RESULT_OK;
414 DCPluginSyncFilterResult result_dcps;
416 assert(dcp_packet->dns_packet != NULL &&
417 dcp_packet->dns_packet_len_p != NULL &&
418 *dcp_packet->dns_packet_len_p > (size_t) 0U);
419 SLIST_FOREACH(dcps, &dcps_context->dcps_list, next) {
420 if (dcps->sync_pre_filter != NULL) {
421 result_dcps = dcps->sync_pre_filter(dcps->plugin, dcp_packet);
422 result = plugin_support_context_get_result_from_dcps(result,
423 result_dcps);
425 assert(*dcp_packet->dns_packet_len_p <= dns_packet_max_len);
426 assert(*dcp_packet->dns_packet_len_p > (size_t) 0U);
428 return result;