FreeRDP
Loading...
Searching...
No Matches
pf_modules.c
1
23#include <winpr/assert.h>
24
25#include <winpr/file.h>
26#include <winpr/wlog.h>
27#include <winpr/path.h>
28#include <winpr/library.h>
29
30#include <freerdp/version.h>
31#include <freerdp/api.h>
32#include <freerdp/build-config.h>
33
34#include <freerdp/server/proxy/proxy_log.h>
35#include <freerdp/server/proxy/proxy_modules_api.h>
36
37#include <freerdp/server/proxy/proxy_context.h>
38#include "proxy_modules.h"
39
40#define TAG PROXY_TAG("modules")
41
42#define MODULE_ENTRY_POINT "proxy_module_entry_point"
43
44struct proxy_module
45{
46 proxyPluginsManager mgr;
47 wArrayList* plugins;
48 wArrayList* handles;
49};
50
51WINPR_ATTR_NODISCARD
52static const char* pf_modules_get_filter_type_string(PF_FILTER_TYPE result)
53{
54 switch (result)
55 {
56 case FILTER_TYPE_KEYBOARD:
57 return "FILTER_TYPE_KEYBOARD";
58 case FILTER_TYPE_UNICODE:
59 return "FILTER_TYPE_UNICODE";
60 case FILTER_TYPE_MOUSE:
61 return "FILTER_TYPE_MOUSE";
62 case FILTER_TYPE_MOUSE_EX:
63 return "FILTER_TYPE_MOUSE_EX";
64 case FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_DATA:
65 return "FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_DATA";
66 case FILTER_TYPE_SERVER_PASSTHROUGH_CHANNEL_DATA:
67 return "FILTER_TYPE_SERVER_PASSTHROUGH_CHANNEL_DATA";
68 case FILTER_TYPE_CLIENT_PASSTHROUGH_DYN_CHANNEL_CREATE:
69 return "FILTER_TYPE_CLIENT_PASSTHROUGH_DYN_CHANNEL_CREATE";
70 case FILTER_TYPE_SERVER_FETCH_TARGET_ADDR:
71 return "FILTER_TYPE_SERVER_FETCH_TARGET_ADDR";
72 case FILTER_TYPE_SERVER_PEER_LOGON:
73 return "FILTER_TYPE_SERVER_PEER_LOGON";
74 case FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_CREATE:
75 return "FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_CREATE";
76 case FILTER_TYPE_STATIC_INTERCEPT_LIST:
77 return "FILTER_TYPE_STATIC_INTERCEPT_LIST";
78 case FILTER_TYPE_DYN_INTERCEPT_LIST:
79 return "FILTER_TYPE_DYN_INTERCEPT_LIST";
80 case FILTER_TYPE_INTERCEPT_CHANNEL:
81 return "FILTER_TYPE_INTERCEPT_CHANNEL";
82 case FILTER_LAST:
83 return "FILTER_LAST";
84 default:
85 return "FILTER_UNKNOWN";
86 }
87}
88
89WINPR_ATTR_NODISCARD
90static const char* pf_modules_get_hook_type_string(PF_HOOK_TYPE result)
91{
92 switch (result)
93 {
94 case HOOK_TYPE_CLIENT_INIT_CONNECT:
95 return "HOOK_TYPE_CLIENT_INIT_CONNECT";
96 case HOOK_TYPE_CLIENT_UNINIT_CONNECT:
97 return "HOOK_TYPE_CLIENT_UNINIT_CONNECT";
98 case HOOK_TYPE_CLIENT_PRE_CONNECT:
99 return "HOOK_TYPE_CLIENT_PRE_CONNECT";
100 case HOOK_TYPE_CLIENT_POST_CONNECT:
101 return "HOOK_TYPE_CLIENT_POST_CONNECT";
102 case HOOK_TYPE_CLIENT_POST_DISCONNECT:
103 return "HOOK_TYPE_CLIENT_POST_DISCONNECT";
104 case HOOK_TYPE_CLIENT_REDIRECT:
105 return "HOOK_TYPE_CLIENT_REDIRECT";
106 case HOOK_TYPE_CLIENT_VERIFY_X509:
107 return "HOOK_TYPE_CLIENT_VERIFY_X509";
108 case HOOK_TYPE_CLIENT_LOGIN_FAILURE:
109 return "HOOK_TYPE_CLIENT_LOGIN_FAILURE";
110 case HOOK_TYPE_CLIENT_END_PAINT:
111 return "HOOK_TYPE_CLIENT_END_PAINT";
112 case HOOK_TYPE_SERVER_POST_CONNECT:
113 return "HOOK_TYPE_SERVER_POST_CONNECT";
114 case HOOK_TYPE_SERVER_ACTIVATE:
115 return "HOOK_TYPE_SERVER_ACTIVATE";
116 case HOOK_TYPE_SERVER_CHANNELS_INIT:
117 return "HOOK_TYPE_SERVER_CHANNELS_INIT";
118 case HOOK_TYPE_SERVER_CHANNELS_FREE:
119 return "HOOK_TYPE_SERVER_CHANNELS_FREE";
120 case HOOK_TYPE_SERVER_SESSION_END:
121 return "HOOK_TYPE_SERVER_SESSION_END";
122 case HOOK_TYPE_CLIENT_LOAD_CHANNELS:
123 return "HOOK_TYPE_CLIENT_LOAD_CHANNELS";
124 case HOOK_TYPE_SERVER_SESSION_INITIALIZE:
125 return "HOOK_TYPE_SERVER_SESSION_INITIALIZE";
126 case HOOK_TYPE_SERVER_SESSION_STARTED:
127 return "HOOK_TYPE_SERVER_SESSION_STARTED";
128 case HOOK_LAST:
129 return "HOOK_LAST";
130 default:
131 return "HOOK_TYPE_UNKNOWN";
132 }
133}
134
135WINPR_ATTR_NODISCARD
136static BOOL pf_modules_proxy_ArrayList_ForEachFkt(void* data, size_t index, va_list ap)
137{
138 proxyPlugin* plugin = (proxyPlugin*)data;
139 BOOL ok = FALSE;
140
141 WINPR_UNUSED(index);
142
143 PF_HOOK_TYPE type = va_arg(ap, PF_HOOK_TYPE);
144 proxyData* pdata = va_arg(ap, proxyData*);
145 void* custom = va_arg(ap, void*);
146
147 WLog_VRB(TAG, "running hook %s.%s", plugin->name, pf_modules_get_hook_type_string(type));
148
149 switch (type)
150 {
151 case HOOK_TYPE_CLIENT_INIT_CONNECT:
152 ok = IFCALLRESULT(TRUE, plugin->ClientInitConnect, plugin, pdata, custom);
153 break;
154 case HOOK_TYPE_CLIENT_UNINIT_CONNECT:
155 ok = IFCALLRESULT(TRUE, plugin->ClientUninitConnect, plugin, pdata, custom);
156 break;
157 case HOOK_TYPE_CLIENT_PRE_CONNECT:
158 ok = IFCALLRESULT(TRUE, plugin->ClientPreConnect, plugin, pdata, custom);
159 break;
160
161 case HOOK_TYPE_CLIENT_POST_CONNECT:
162 ok = IFCALLRESULT(TRUE, plugin->ClientPostConnect, plugin, pdata, custom);
163 break;
164
165 case HOOK_TYPE_CLIENT_REDIRECT:
166 ok = IFCALLRESULT(TRUE, plugin->ClientRedirect, plugin, pdata, custom);
167 break;
168
169 case HOOK_TYPE_CLIENT_POST_DISCONNECT:
170 ok = IFCALLRESULT(TRUE, plugin->ClientPostDisconnect, plugin, pdata, custom);
171 break;
172
173 /* ClientX509Certificate callback is special. If no callback is set, tell the parent the
174 * run failed. Only on failure the rest of the certificate policy is checked. If we
175 * return success it means no more certificate checks are done.
176 */
177 case HOOK_TYPE_CLIENT_VERIFY_X509:
178 ok = IFCALLRESULT(FALSE, plugin->ClientX509Certificate, plugin, pdata, custom);
179 break;
180
181 case HOOK_TYPE_CLIENT_LOGIN_FAILURE:
182 ok = IFCALLRESULT(TRUE, plugin->ClientLoginFailure, plugin, pdata, custom);
183 break;
184
185 case HOOK_TYPE_CLIENT_END_PAINT:
186 ok = IFCALLRESULT(TRUE, plugin->ClientEndPaint, plugin, pdata, custom);
187 break;
188
189 case HOOK_TYPE_CLIENT_LOAD_CHANNELS:
190 ok = IFCALLRESULT(TRUE, plugin->ClientLoadChannels, plugin, pdata, custom);
191 break;
192
193 case HOOK_TYPE_SERVER_POST_CONNECT:
194 ok = IFCALLRESULT(TRUE, plugin->ServerPostConnect, plugin, pdata, custom);
195 break;
196
197 case HOOK_TYPE_SERVER_ACTIVATE:
198 ok = IFCALLRESULT(TRUE, plugin->ServerPeerActivate, plugin, pdata, custom);
199 break;
200
201 case HOOK_TYPE_SERVER_CHANNELS_INIT:
202 ok = IFCALLRESULT(TRUE, plugin->ServerChannelsInit, plugin, pdata, custom);
203 break;
204
205 case HOOK_TYPE_SERVER_CHANNELS_FREE:
206 ok = IFCALLRESULT(TRUE, plugin->ServerChannelsFree, plugin, pdata, custom);
207 break;
208
209 case HOOK_TYPE_SERVER_SESSION_END:
210 ok = IFCALLRESULT(TRUE, plugin->ServerSessionEnd, plugin, pdata, custom);
211 break;
212
213 case HOOK_TYPE_SERVER_SESSION_INITIALIZE:
214 ok = IFCALLRESULT(TRUE, plugin->ServerSessionInitialize, plugin, pdata, custom);
215 break;
216
217 case HOOK_TYPE_SERVER_SESSION_STARTED:
218 ok = IFCALLRESULT(TRUE, plugin->ServerSessionStarted, plugin, pdata, custom);
219 break;
220
221 case HOOK_LAST:
222 default:
223 WLog_ERR(TAG, "invalid hook called");
224 }
225
226 if (!ok)
227 {
228 WLog_INFO(TAG, "plugin %s, hook %s failed!", plugin->name,
229 pf_modules_get_hook_type_string(type));
230 return FALSE;
231 }
232 return TRUE;
233}
234
235/*
236 * runs all hooks of type `type`.
237 *
238 * @type: hook type to run.
239 * @server: pointer of server's rdpContext struct of the current session.
240 */
241BOOL pf_modules_run_hook(proxyModule* module, PF_HOOK_TYPE type, proxyData* pdata, void* custom)
242{
243 WINPR_ASSERT(module);
244 WINPR_ASSERT(module->plugins);
245 return ArrayList_ForEach(module->plugins, pf_modules_proxy_ArrayList_ForEachFkt, type, pdata,
246 custom);
247}
248
249WINPR_ATTR_NODISCARD
250static BOOL pf_modules_ArrayList_ForEachFkt(void* data, size_t index, va_list ap)
251{
252 proxyPlugin* plugin = (proxyPlugin*)data;
253 BOOL result = FALSE;
254
255 WINPR_UNUSED(index);
256
257 PF_FILTER_TYPE type = va_arg(ap, PF_FILTER_TYPE);
258 proxyData* pdata = va_arg(ap, proxyData*);
259 void* param = va_arg(ap, void*);
260
261 WLog_VRB(TAG, "running filter: %s", plugin->name);
262
263 switch (type)
264 {
265 case FILTER_TYPE_KEYBOARD:
266 result = IFCALLRESULT(TRUE, plugin->KeyboardEvent, plugin, pdata, param);
267 break;
268
269 case FILTER_TYPE_UNICODE:
270 result = IFCALLRESULT(TRUE, plugin->UnicodeEvent, plugin, pdata, param);
271 break;
272
273 case FILTER_TYPE_MOUSE:
274 result = IFCALLRESULT(TRUE, plugin->MouseEvent, plugin, pdata, param);
275 break;
276
277 case FILTER_TYPE_MOUSE_EX:
278 result = IFCALLRESULT(TRUE, plugin->MouseExEvent, plugin, pdata, param);
279 break;
280
281 case FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_DATA:
282 result = IFCALLRESULT(TRUE, plugin->ClientChannelData, plugin, pdata, param);
283 break;
284
285 case FILTER_TYPE_SERVER_PASSTHROUGH_CHANNEL_DATA:
286 result = IFCALLRESULT(TRUE, plugin->ServerChannelData, plugin, pdata, param);
287 break;
288
289 case FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_CREATE:
290 result = IFCALLRESULT(TRUE, plugin->ChannelCreate, plugin, pdata, param);
291 break;
292
293 case FILTER_TYPE_CLIENT_PASSTHROUGH_DYN_CHANNEL_CREATE:
294 result = IFCALLRESULT(TRUE, plugin->DynamicChannelCreate, plugin, pdata, param);
295 break;
296
297 case FILTER_TYPE_SERVER_FETCH_TARGET_ADDR:
298 result = IFCALLRESULT(TRUE, plugin->ServerFetchTargetAddr, plugin, pdata, param);
299 break;
300
301 case FILTER_TYPE_SERVER_PEER_LOGON:
302 result = IFCALLRESULT(TRUE, plugin->ServerPeerLogon, plugin, pdata, param);
303 break;
304
305 case FILTER_TYPE_INTERCEPT_CHANNEL:
306 result = IFCALLRESULT(TRUE, plugin->DynChannelIntercept, plugin, pdata, param);
307 break;
308
309 case FILTER_TYPE_DYN_INTERCEPT_LIST:
310 result = IFCALLRESULT(TRUE, plugin->DynChannelToIntercept, plugin, pdata, param);
311 break;
312
313 case FILTER_TYPE_STATIC_INTERCEPT_LIST:
314 result = IFCALLRESULT(TRUE, plugin->StaticChannelToIntercept, plugin, pdata, param);
315 break;
316
317 case FILTER_LAST:
318 default:
319 WLog_ERR(TAG, "invalid filter called");
320 }
321
322 if (!result)
323 {
324 /* current filter return FALSE, no need to run other filters. */
325 WLog_DBG(TAG, "plugin %s, filter type [%s] returned FALSE", plugin->name,
326 pf_modules_get_filter_type_string(type));
327 }
328 return result;
329}
330
331/*
332 * runs all filters of type `type`.
333 *
334 * @type: filter type to run.
335 * @server: pointer of server's rdpContext struct of the current session.
336 */
337BOOL pf_modules_run_filter(proxyModule* module, PF_FILTER_TYPE type, proxyData* pdata, void* param)
338{
339 WINPR_ASSERT(module);
340 WINPR_ASSERT(module->plugins);
341
342 return ArrayList_ForEach(module->plugins, pf_modules_ArrayList_ForEachFkt, type, pdata, param);
343}
344
345/*
346 * stores per-session data needed by a plugin.
347 *
348 * @context: current session server's rdpContext instance.
349 * @info: pointer to per-session data.
350 */
351WINPR_ATTR_NODISCARD
352static BOOL pf_modules_set_plugin_data(WINPR_ATTR_UNUSED proxyPluginsManager* mgr,
353 const char* plugin_name, proxyData* pdata, void* data)
354{
355 union
356 {
357 const char* ccp;
358 char* cp;
359 } ccharconv;
360
361 WINPR_ASSERT(plugin_name);
362
363 ccharconv.ccp = plugin_name;
364 if (data == nullptr)
365 {
366 HashTable_Remove(pdata->modules_info, ccharconv.cp);
367 return FALSE;
368 }
369
370 if (!HashTable_Insert(pdata->modules_info, ccharconv.cp, data))
371 {
372 WLog_ERR(TAG, "HashTable_Insert failed!");
373 return FALSE;
374 }
375
376 return TRUE;
377}
378
379/*
380 * returns per-session data needed a plugin.
381 *
382 * @context: current session server's rdpContext instance.
383 * if there's no data related to `plugin_name` in `context` (current session), a nullptr will be
384 * returned.
385 */
386WINPR_ATTR_NODISCARD
387static void* pf_modules_get_plugin_data(WINPR_ATTR_UNUSED proxyPluginsManager* mgr,
388 const char* plugin_name, proxyData* pdata)
389{
390 union
391 {
392 const char* ccp;
393 char* cp;
394 } ccharconv;
395 WINPR_ASSERT(plugin_name);
396 WINPR_ASSERT(pdata);
397 ccharconv.ccp = plugin_name;
398
399 return HashTable_GetItemValue(pdata->modules_info, ccharconv.cp);
400}
401
402static void pf_modules_abort_connect(WINPR_ATTR_UNUSED proxyPluginsManager* mgr, proxyData* pdata)
403{
404 WINPR_ASSERT(pdata);
405 WLog_DBG(TAG, "is called!");
406 proxy_data_abort_connect(pdata);
407}
408
409WINPR_ATTR_NODISCARD
410static BOOL pf_modules_register_ArrayList_ForEachFkt(void* data, size_t index, va_list ap)
411{
412 proxyPlugin* plugin = (proxyPlugin*)data;
413 proxyPlugin* plugin_to_register = va_arg(ap, proxyPlugin*);
414
415 WINPR_UNUSED(index);
416
417 if (strcmp(plugin->name, plugin_to_register->name) == 0)
418 {
419 WLog_ERR(TAG, "can not register plugin '%s', it is already registered!", plugin->name);
420 return FALSE;
421 }
422 return TRUE;
423}
424
425WINPR_ATTR_NODISCARD
426static BOOL pf_modules_register_plugin(proxyPluginsManager* mgr,
427 const proxyPlugin* plugin_to_register)
428{
429 proxyPlugin internal = WINPR_C_ARRAY_INIT;
430 proxyModule* module = (proxyModule*)mgr;
431 WINPR_ASSERT(module);
432
433 if (!plugin_to_register)
434 return FALSE;
435
436 internal = *plugin_to_register;
437 internal.mgr = mgr;
438
439 /* make sure there's no other loaded plugin with the same name of `plugin_to_register`. */
440 if (!ArrayList_ForEach(module->plugins, pf_modules_register_ArrayList_ForEachFkt, &internal))
441 return FALSE;
442
443 if (!ArrayList_Append(module->plugins, &internal))
444 {
445 WLog_ERR(TAG, "failed adding plugin to list: %s", plugin_to_register->name);
446 return FALSE;
447 }
448 WLog_INFO(TAG, "Successfully registered proxy plugin '%s'", plugin_to_register->name);
449
450 return TRUE;
451}
452
453WINPR_ATTR_NODISCARD
454static BOOL pf_modules_load_ArrayList_ForEachFkt(void* data, size_t index, va_list ap)
455{
456 proxyPlugin* plugin = (proxyPlugin*)data;
457 const char* plugin_name = va_arg(ap, const char*);
458 BOOL* res = va_arg(ap, BOOL*);
459
460 WINPR_UNUSED(index);
461 WINPR_UNUSED(ap);
462 WINPR_ASSERT(res);
463
464 if (strcmp(plugin->name, plugin_name) == 0)
465 *res = TRUE;
466 return TRUE;
467}
468
469BOOL pf_modules_is_plugin_loaded(proxyModule* module, const char* plugin_name)
470{
471 BOOL rc = FALSE;
472 WINPR_ASSERT(module);
473 if (ArrayList_Count(module->plugins) < 1)
474 return FALSE;
475 if (!ArrayList_ForEach(module->plugins, pf_modules_load_ArrayList_ForEachFkt, plugin_name, &rc))
476 return FALSE;
477 return rc;
478}
479
480WINPR_ATTR_NODISCARD
481static BOOL pf_modules_print_ArrayList_ForEachFkt(void* data, size_t index, va_list ap)
482{
483 proxyPlugin* plugin = (proxyPlugin*)data;
484
485 WINPR_UNUSED(index);
486 WINPR_UNUSED(ap);
487
488 WLog_INFO(TAG, "\tName: %s", plugin->name);
489 WLog_INFO(TAG, "\tDescription: %s", plugin->description);
490 return TRUE;
491}
492
493BOOL pf_modules_list_loaded_plugins(proxyModule* module)
494{
495 size_t count = 0;
496
497 WINPR_ASSERT(module);
498 WINPR_ASSERT(module->plugins);
499
500 count = ArrayList_Count(module->plugins);
501
502 if (count > 0)
503 WLog_INFO(TAG, "Loaded plugins:");
504
505 return ArrayList_ForEach(module->plugins, pf_modules_print_ArrayList_ForEachFkt);
506}
507
508WINPR_ATTR_NODISCARD
509static BOOL pf_modules_load_static_module(const char* module_name, proxyModule* module,
510 void* userdata)
511{
512 WINPR_ASSERT(module);
513
514 HANDLE handle = GetModuleHandleA(nullptr);
515
516 if (handle == nullptr)
517 {
518 WLog_DBG(TAG, "failed loading static library: %s", module_name);
519 return FALSE;
520 }
521
522 char name[256] = WINPR_C_ARRAY_INIT;
523 (void)_snprintf(name, sizeof(name), "%s_%s", module_name, MODULE_ENTRY_POINT);
524 for (size_t x = 0; x < strnlen(name, sizeof(name)); x++)
525 {
526 if (name[x] == '-')
527 name[x] = '_';
528 }
529
530 proxyModuleEntryPoint pEntryPoint = GetProcAddressAs(handle, name, proxyModuleEntryPoint);
531 if (!pEntryPoint)
532 {
533 WLog_DBG(TAG, "GetProcAddress failed for static %s (module %s)", name, module_name);
534 goto error;
535 }
536 if (!ArrayList_Append(module->handles, handle))
537 {
538 WLog_ERR(TAG, "ArrayList_Append failed!");
539 goto error;
540 }
541 return pf_modules_add(module, pEntryPoint, userdata);
542
543error:
544 FreeLibrary(handle);
545 return FALSE;
546}
547
548WINPR_ATTR_NODISCARD
549static BOOL pf_modules_load_dynamic_module(const char* module_path, proxyModule* module,
550 void* userdata)
551{
552 if (!winpr_PathFileExists(module_path))
553 {
554 WLog_DBG(TAG, "failed loading external library: file '%s' does not exist", module_path);
555 return FALSE;
556 }
557
558 HANDLE handle = LoadLibraryX(module_path);
559
560 if (handle == nullptr)
561 {
562 WLog_DBG(TAG, "failed loading external library: %s", module_path);
563 return FALSE;
564 }
565
566 proxyModuleEntryPoint pEntryPoint =
567 GetProcAddressAs(handle, MODULE_ENTRY_POINT, proxyModuleEntryPoint);
568 if (!pEntryPoint)
569 {
570 WLog_DBG(TAG, "GetProcAddress failed while loading %s", module_path);
571 goto error;
572 }
573 if (!ArrayList_Append(module->handles, handle))
574 {
575 WLog_ERR(TAG, "ArrayList_Append failed!");
576 goto error;
577 }
578 return pf_modules_add(module, pEntryPoint, userdata);
579
580error:
581 FreeLibrary(handle);
582 return FALSE;
583}
584
585WINPR_ATTR_NODISCARD
586static BOOL pf_modules_try_load_dynamic_module(const char* module_path, proxyModule* module,
587 void* userdata, size_t count, const char* names[])
588{
589 for (size_t x = 0; x < count; x++)
590 {
591 const char* name = names[x];
592
593 char* fullpath = GetCombinedPath(module_path, name);
594 if (!fullpath)
595 continue;
596
597 const BOOL rc = pf_modules_load_dynamic_module(fullpath, module, userdata);
598 free(fullpath);
599 if (rc)
600 return TRUE;
601 }
602 return FALSE;
603}
604
605WINPR_ATTR_NODISCARD
606static BOOL pf_modules_load_module(const char* module_path, const char* module_name,
607 proxyModule* module, void* userdata)
608{
609 WINPR_ASSERT(module);
610
611 if (pf_modules_load_static_module(module_name, module, userdata))
612 return TRUE;
613
614 char names[5][MAX_PATH] = WINPR_C_ARRAY_INIT;
615 (void)_snprintf(names[0], sizeof(names[0]), "proxy-%s-plugin%s", module_name,
616 FREERDP_SHARED_LIBRARY_SUFFIX);
617 (void)_snprintf(names[1], sizeof(names[1]), "%sproxy-%s-plugin%s",
618 FREERDP_SHARED_LIBRARY_PREFIX, module_name, FREERDP_SHARED_LIBRARY_SUFFIX);
619 (void)_snprintf(names[2], sizeof(names[2]), "%sproxy-%s-plugin%s.%d",
620 FREERDP_SHARED_LIBRARY_PREFIX, module_name, FREERDP_SHARED_LIBRARY_SUFFIX,
621 FREERDP_VERSION_MAJOR);
622 (void)_snprintf(names[3], sizeof(names[3]), "%sproxy-%s-plugin%d%s",
623 FREERDP_SHARED_LIBRARY_PREFIX, module_name, FREERDP_VERSION_MAJOR,
624 FREERDP_SHARED_LIBRARY_SUFFIX);
625 (void)_snprintf(names[4], sizeof(names[4]), "%sproxy-%s-plugin%d%s.%d",
626 FREERDP_SHARED_LIBRARY_PREFIX, module_name, FREERDP_VERSION_MAJOR,
627 FREERDP_SHARED_LIBRARY_SUFFIX, FREERDP_VERSION_MAJOR);
628
629 const char* cnames[5] = { names[0], names[1], names[2], names[3], names[4] };
630 return pf_modules_try_load_dynamic_module(module_path, module, userdata, ARRAYSIZE(cnames),
631 cnames);
632}
633
634static void free_handle(void* obj)
635{
636 HANDLE handle = (HANDLE)obj;
637 if (handle)
638 FreeLibrary(handle);
639}
640
641static void free_plugin(void* obj)
642{
643 proxyPlugin* plugin = (proxyPlugin*)obj;
644 WINPR_ASSERT(plugin);
645
646 if (!IFCALLRESULT(TRUE, plugin->PluginUnload, plugin))
647 WLog_WARN(TAG, "PluginUnload failed for plugin '%s'", plugin->name);
648
649 free(plugin);
650}
651
652WINPR_ATTR_MALLOC(free_plugin, 1)
653WINPR_ATTR_NODISCARD
654static void* new_plugin(const void* obj)
655{
656 const proxyPlugin* src = obj;
657 proxyPlugin* proxy = calloc(1, sizeof(proxyPlugin));
658 if (!proxy)
659 return nullptr;
660 *proxy = *src;
661 return proxy;
662}
663
664proxyModule* pf_modules_new(const char* root_dir, const char** modules, size_t count)
665{
666 wObject* obj = nullptr;
667 char* path = nullptr;
668 proxyModule* module = calloc(1, sizeof(proxyModule));
669 if (!module)
670 return nullptr;
671
672 module->mgr.RegisterPlugin = pf_modules_register_plugin;
673 module->mgr.SetPluginData = pf_modules_set_plugin_data;
674 module->mgr.GetPluginData = pf_modules_get_plugin_data;
675 module->mgr.AbortConnect = pf_modules_abort_connect;
676 module->plugins = ArrayList_New(FALSE);
677
678 if (module->plugins == nullptr)
679 {
680 WLog_ERR(TAG, "ArrayList_New failed!");
681 goto error;
682 }
683 obj = ArrayList_Object(module->plugins);
684 WINPR_ASSERT(obj);
685
686 obj->fnObjectFree = free_plugin;
687 obj->fnObjectNew = new_plugin;
688
689 module->handles = ArrayList_New(FALSE);
690 if (module->handles == nullptr)
691 {
692
693 WLog_ERR(TAG, "ArrayList_New failed!");
694 goto error;
695 }
696 ArrayList_Object(module->handles)->fnObjectFree = free_handle;
697
698 if (count > 0)
699 {
700 WINPR_ASSERT(root_dir);
701 if (!winpr_PathFileExists(root_dir))
702 path = GetCombinedPath(FREERDP_INSTALL_PREFIX, root_dir);
703 else
704 path = _strdup(root_dir);
705
706 if (!winpr_PathFileExists(path))
707 {
708 if (!winpr_PathMakePath(path, nullptr))
709 {
710 WLog_ERR(TAG, "error occurred while creating modules directory: %s", root_dir);
711 }
712 }
713
714 if (winpr_PathFileExists(path))
715 WLog_DBG(TAG, "modules root directory: %s", path);
716
717 for (size_t i = 0; i < count; i++)
718 {
719 const char* module_name = modules[i];
720 if (!pf_modules_load_module(path, module_name, module, nullptr))
721 WLog_WARN(TAG, "Failed to load proxy module '%s'", module_name);
722 else
723 WLog_INFO(TAG, "Successfully loaded proxy module '%s'", module_name);
724 }
725 }
726
727 free(path);
728 return module;
729
730error:
731 free(path);
732 pf_modules_free(module);
733 return nullptr;
734}
735
736void pf_modules_free(proxyModule* module)
737{
738 if (!module)
739 return;
740
741 ArrayList_Free(module->plugins);
742 ArrayList_Free(module->handles);
743 free(module);
744}
745
746BOOL pf_modules_add(proxyModule* module, proxyModuleEntryPoint ep, void* userdata)
747{
748 WINPR_ASSERT(module);
749 WINPR_ASSERT(ep);
750
751 return ep(&module->mgr, userdata);
752}
This struct contains function pointer to initialize/free objects.
Definition collections.h:52
OBJECT_FREE_FN fnObjectFree
Definition collections.h:59
WINPR_ATTR_NODISCARD OBJECT_NEW_FN fnObjectNew
Definition collections.h:54