Milestone 5: deliver embedded RDP sessions and lifecycle hardening
This commit is contained in:
745
third_party/FreeRDP/server/proxy/pf_modules.c
vendored
Normal file
745
third_party/FreeRDP/server/proxy/pf_modules.c
vendored
Normal file
@@ -0,0 +1,745 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* FreeRDP Proxy Server modules API
|
||||
*
|
||||
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
|
||||
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
|
||||
* Copyright 2021 Armin Novak <anovak@thincast.com>
|
||||
* Copyright 2021 Thincast Technologies GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <winpr/assert.h>
|
||||
|
||||
#include <winpr/file.h>
|
||||
#include <winpr/wlog.h>
|
||||
#include <winpr/path.h>
|
||||
#include <winpr/library.h>
|
||||
|
||||
#include <freerdp/version.h>
|
||||
#include <freerdp/api.h>
|
||||
#include <freerdp/build-config.h>
|
||||
|
||||
#include <freerdp/server/proxy/proxy_log.h>
|
||||
#include <freerdp/server/proxy/proxy_modules_api.h>
|
||||
|
||||
#include <freerdp/server/proxy/proxy_context.h>
|
||||
#include "proxy_modules.h"
|
||||
|
||||
#define TAG PROXY_TAG("modules")
|
||||
|
||||
#define MODULE_ENTRY_POINT "proxy_module_entry_point"
|
||||
|
||||
struct proxy_module
|
||||
{
|
||||
proxyPluginsManager mgr;
|
||||
wArrayList* plugins;
|
||||
wArrayList* handles;
|
||||
};
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static const char* pf_modules_get_filter_type_string(PF_FILTER_TYPE result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
case FILTER_TYPE_KEYBOARD:
|
||||
return "FILTER_TYPE_KEYBOARD";
|
||||
case FILTER_TYPE_UNICODE:
|
||||
return "FILTER_TYPE_UNICODE";
|
||||
case FILTER_TYPE_MOUSE:
|
||||
return "FILTER_TYPE_MOUSE";
|
||||
case FILTER_TYPE_MOUSE_EX:
|
||||
return "FILTER_TYPE_MOUSE_EX";
|
||||
case FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_DATA:
|
||||
return "FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_DATA";
|
||||
case FILTER_TYPE_SERVER_PASSTHROUGH_CHANNEL_DATA:
|
||||
return "FILTER_TYPE_SERVER_PASSTHROUGH_CHANNEL_DATA";
|
||||
case FILTER_TYPE_CLIENT_PASSTHROUGH_DYN_CHANNEL_CREATE:
|
||||
return "FILTER_TYPE_CLIENT_PASSTHROUGH_DYN_CHANNEL_CREATE";
|
||||
case FILTER_TYPE_SERVER_FETCH_TARGET_ADDR:
|
||||
return "FILTER_TYPE_SERVER_FETCH_TARGET_ADDR";
|
||||
case FILTER_TYPE_SERVER_PEER_LOGON:
|
||||
return "FILTER_TYPE_SERVER_PEER_LOGON";
|
||||
case FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_CREATE:
|
||||
return "FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_CREATE";
|
||||
case FILTER_TYPE_STATIC_INTERCEPT_LIST:
|
||||
return "FILTER_TYPE_STATIC_INTERCEPT_LIST";
|
||||
case FILTER_TYPE_DYN_INTERCEPT_LIST:
|
||||
return "FILTER_TYPE_DYN_INTERCEPT_LIST";
|
||||
case FILTER_TYPE_INTERCEPT_CHANNEL:
|
||||
return "FILTER_TYPE_INTERCEPT_CHANNEL";
|
||||
case FILTER_LAST:
|
||||
return "FILTER_LAST";
|
||||
default:
|
||||
return "FILTER_UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static const char* pf_modules_get_hook_type_string(PF_HOOK_TYPE result)
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
case HOOK_TYPE_CLIENT_INIT_CONNECT:
|
||||
return "HOOK_TYPE_CLIENT_INIT_CONNECT";
|
||||
case HOOK_TYPE_CLIENT_UNINIT_CONNECT:
|
||||
return "HOOK_TYPE_CLIENT_UNINIT_CONNECT";
|
||||
case HOOK_TYPE_CLIENT_PRE_CONNECT:
|
||||
return "HOOK_TYPE_CLIENT_PRE_CONNECT";
|
||||
case HOOK_TYPE_CLIENT_POST_CONNECT:
|
||||
return "HOOK_TYPE_CLIENT_POST_CONNECT";
|
||||
case HOOK_TYPE_CLIENT_POST_DISCONNECT:
|
||||
return "HOOK_TYPE_CLIENT_POST_DISCONNECT";
|
||||
case HOOK_TYPE_CLIENT_REDIRECT:
|
||||
return "HOOK_TYPE_CLIENT_REDIRECT";
|
||||
case HOOK_TYPE_CLIENT_VERIFY_X509:
|
||||
return "HOOK_TYPE_CLIENT_VERIFY_X509";
|
||||
case HOOK_TYPE_CLIENT_LOGIN_FAILURE:
|
||||
return "HOOK_TYPE_CLIENT_LOGIN_FAILURE";
|
||||
case HOOK_TYPE_CLIENT_END_PAINT:
|
||||
return "HOOK_TYPE_CLIENT_END_PAINT";
|
||||
case HOOK_TYPE_SERVER_POST_CONNECT:
|
||||
return "HOOK_TYPE_SERVER_POST_CONNECT";
|
||||
case HOOK_TYPE_SERVER_ACTIVATE:
|
||||
return "HOOK_TYPE_SERVER_ACTIVATE";
|
||||
case HOOK_TYPE_SERVER_CHANNELS_INIT:
|
||||
return "HOOK_TYPE_SERVER_CHANNELS_INIT";
|
||||
case HOOK_TYPE_SERVER_CHANNELS_FREE:
|
||||
return "HOOK_TYPE_SERVER_CHANNELS_FREE";
|
||||
case HOOK_TYPE_SERVER_SESSION_END:
|
||||
return "HOOK_TYPE_SERVER_SESSION_END";
|
||||
case HOOK_TYPE_CLIENT_LOAD_CHANNELS:
|
||||
return "HOOK_TYPE_CLIENT_LOAD_CHANNELS";
|
||||
case HOOK_TYPE_SERVER_SESSION_INITIALIZE:
|
||||
return "HOOK_TYPE_SERVER_SESSION_INITIALIZE";
|
||||
case HOOK_TYPE_SERVER_SESSION_STARTED:
|
||||
return "HOOK_TYPE_SERVER_SESSION_STARTED";
|
||||
case HOOK_LAST:
|
||||
return "HOOK_LAST";
|
||||
default:
|
||||
return "HOOK_TYPE_UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_modules_proxy_ArrayList_ForEachFkt(void* data, size_t index, va_list ap)
|
||||
{
|
||||
proxyPlugin* plugin = (proxyPlugin*)data;
|
||||
BOOL ok = FALSE;
|
||||
|
||||
WINPR_UNUSED(index);
|
||||
|
||||
PF_HOOK_TYPE type = va_arg(ap, PF_HOOK_TYPE);
|
||||
proxyData* pdata = va_arg(ap, proxyData*);
|
||||
void* custom = va_arg(ap, void*);
|
||||
|
||||
WLog_VRB(TAG, "running hook %s.%s", plugin->name, pf_modules_get_hook_type_string(type));
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case HOOK_TYPE_CLIENT_INIT_CONNECT:
|
||||
ok = IFCALLRESULT(TRUE, plugin->ClientInitConnect, plugin, pdata, custom);
|
||||
break;
|
||||
case HOOK_TYPE_CLIENT_UNINIT_CONNECT:
|
||||
ok = IFCALLRESULT(TRUE, plugin->ClientUninitConnect, plugin, pdata, custom);
|
||||
break;
|
||||
case HOOK_TYPE_CLIENT_PRE_CONNECT:
|
||||
ok = IFCALLRESULT(TRUE, plugin->ClientPreConnect, plugin, pdata, custom);
|
||||
break;
|
||||
|
||||
case HOOK_TYPE_CLIENT_POST_CONNECT:
|
||||
ok = IFCALLRESULT(TRUE, plugin->ClientPostConnect, plugin, pdata, custom);
|
||||
break;
|
||||
|
||||
case HOOK_TYPE_CLIENT_REDIRECT:
|
||||
ok = IFCALLRESULT(TRUE, plugin->ClientRedirect, plugin, pdata, custom);
|
||||
break;
|
||||
|
||||
case HOOK_TYPE_CLIENT_POST_DISCONNECT:
|
||||
ok = IFCALLRESULT(TRUE, plugin->ClientPostDisconnect, plugin, pdata, custom);
|
||||
break;
|
||||
|
||||
case HOOK_TYPE_CLIENT_VERIFY_X509:
|
||||
ok = IFCALLRESULT(TRUE, plugin->ClientX509Certificate, plugin, pdata, custom);
|
||||
break;
|
||||
|
||||
case HOOK_TYPE_CLIENT_LOGIN_FAILURE:
|
||||
ok = IFCALLRESULT(TRUE, plugin->ClientLoginFailure, plugin, pdata, custom);
|
||||
break;
|
||||
|
||||
case HOOK_TYPE_CLIENT_END_PAINT:
|
||||
ok = IFCALLRESULT(TRUE, plugin->ClientEndPaint, plugin, pdata, custom);
|
||||
break;
|
||||
|
||||
case HOOK_TYPE_CLIENT_LOAD_CHANNELS:
|
||||
ok = IFCALLRESULT(TRUE, plugin->ClientLoadChannels, plugin, pdata, custom);
|
||||
break;
|
||||
|
||||
case HOOK_TYPE_SERVER_POST_CONNECT:
|
||||
ok = IFCALLRESULT(TRUE, plugin->ServerPostConnect, plugin, pdata, custom);
|
||||
break;
|
||||
|
||||
case HOOK_TYPE_SERVER_ACTIVATE:
|
||||
ok = IFCALLRESULT(TRUE, plugin->ServerPeerActivate, plugin, pdata, custom);
|
||||
break;
|
||||
|
||||
case HOOK_TYPE_SERVER_CHANNELS_INIT:
|
||||
ok = IFCALLRESULT(TRUE, plugin->ServerChannelsInit, plugin, pdata, custom);
|
||||
break;
|
||||
|
||||
case HOOK_TYPE_SERVER_CHANNELS_FREE:
|
||||
ok = IFCALLRESULT(TRUE, plugin->ServerChannelsFree, plugin, pdata, custom);
|
||||
break;
|
||||
|
||||
case HOOK_TYPE_SERVER_SESSION_END:
|
||||
ok = IFCALLRESULT(TRUE, plugin->ServerSessionEnd, plugin, pdata, custom);
|
||||
break;
|
||||
|
||||
case HOOK_TYPE_SERVER_SESSION_INITIALIZE:
|
||||
ok = IFCALLRESULT(TRUE, plugin->ServerSessionInitialize, plugin, pdata, custom);
|
||||
break;
|
||||
|
||||
case HOOK_TYPE_SERVER_SESSION_STARTED:
|
||||
ok = IFCALLRESULT(TRUE, plugin->ServerSessionStarted, plugin, pdata, custom);
|
||||
break;
|
||||
|
||||
case HOOK_LAST:
|
||||
default:
|
||||
WLog_ERR(TAG, "invalid hook called");
|
||||
}
|
||||
|
||||
if (!ok)
|
||||
{
|
||||
WLog_INFO(TAG, "plugin %s, hook %s failed!", plugin->name,
|
||||
pf_modules_get_hook_type_string(type));
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* runs all hooks of type `type`.
|
||||
*
|
||||
* @type: hook type to run.
|
||||
* @server: pointer of server's rdpContext struct of the current session.
|
||||
*/
|
||||
BOOL pf_modules_run_hook(proxyModule* module, PF_HOOK_TYPE type, proxyData* pdata, void* custom)
|
||||
{
|
||||
WINPR_ASSERT(module);
|
||||
WINPR_ASSERT(module->plugins);
|
||||
return ArrayList_ForEach(module->plugins, pf_modules_proxy_ArrayList_ForEachFkt, type, pdata,
|
||||
custom);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_modules_ArrayList_ForEachFkt(void* data, size_t index, va_list ap)
|
||||
{
|
||||
proxyPlugin* plugin = (proxyPlugin*)data;
|
||||
BOOL result = FALSE;
|
||||
|
||||
WINPR_UNUSED(index);
|
||||
|
||||
PF_FILTER_TYPE type = va_arg(ap, PF_FILTER_TYPE);
|
||||
proxyData* pdata = va_arg(ap, proxyData*);
|
||||
void* param = va_arg(ap, void*);
|
||||
|
||||
WLog_VRB(TAG, "running filter: %s", plugin->name);
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case FILTER_TYPE_KEYBOARD:
|
||||
result = IFCALLRESULT(TRUE, plugin->KeyboardEvent, plugin, pdata, param);
|
||||
break;
|
||||
|
||||
case FILTER_TYPE_UNICODE:
|
||||
result = IFCALLRESULT(TRUE, plugin->UnicodeEvent, plugin, pdata, param);
|
||||
break;
|
||||
|
||||
case FILTER_TYPE_MOUSE:
|
||||
result = IFCALLRESULT(TRUE, plugin->MouseEvent, plugin, pdata, param);
|
||||
break;
|
||||
|
||||
case FILTER_TYPE_MOUSE_EX:
|
||||
result = IFCALLRESULT(TRUE, plugin->MouseExEvent, plugin, pdata, param);
|
||||
break;
|
||||
|
||||
case FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_DATA:
|
||||
result = IFCALLRESULT(TRUE, plugin->ClientChannelData, plugin, pdata, param);
|
||||
break;
|
||||
|
||||
case FILTER_TYPE_SERVER_PASSTHROUGH_CHANNEL_DATA:
|
||||
result = IFCALLRESULT(TRUE, plugin->ServerChannelData, plugin, pdata, param);
|
||||
break;
|
||||
|
||||
case FILTER_TYPE_CLIENT_PASSTHROUGH_CHANNEL_CREATE:
|
||||
result = IFCALLRESULT(TRUE, plugin->ChannelCreate, plugin, pdata, param);
|
||||
break;
|
||||
|
||||
case FILTER_TYPE_CLIENT_PASSTHROUGH_DYN_CHANNEL_CREATE:
|
||||
result = IFCALLRESULT(TRUE, plugin->DynamicChannelCreate, plugin, pdata, param);
|
||||
break;
|
||||
|
||||
case FILTER_TYPE_SERVER_FETCH_TARGET_ADDR:
|
||||
result = IFCALLRESULT(TRUE, plugin->ServerFetchTargetAddr, plugin, pdata, param);
|
||||
break;
|
||||
|
||||
case FILTER_TYPE_SERVER_PEER_LOGON:
|
||||
result = IFCALLRESULT(TRUE, plugin->ServerPeerLogon, plugin, pdata, param);
|
||||
break;
|
||||
|
||||
case FILTER_TYPE_INTERCEPT_CHANNEL:
|
||||
result = IFCALLRESULT(TRUE, plugin->DynChannelIntercept, plugin, pdata, param);
|
||||
break;
|
||||
|
||||
case FILTER_TYPE_DYN_INTERCEPT_LIST:
|
||||
result = IFCALLRESULT(TRUE, plugin->DynChannelToIntercept, plugin, pdata, param);
|
||||
break;
|
||||
|
||||
case FILTER_TYPE_STATIC_INTERCEPT_LIST:
|
||||
result = IFCALLRESULT(TRUE, plugin->StaticChannelToIntercept, plugin, pdata, param);
|
||||
break;
|
||||
|
||||
case FILTER_LAST:
|
||||
default:
|
||||
WLog_ERR(TAG, "invalid filter called");
|
||||
}
|
||||
|
||||
if (!result)
|
||||
{
|
||||
/* current filter return FALSE, no need to run other filters. */
|
||||
WLog_DBG(TAG, "plugin %s, filter type [%s] returned FALSE", plugin->name,
|
||||
pf_modules_get_filter_type_string(type));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* runs all filters of type `type`.
|
||||
*
|
||||
* @type: filter type to run.
|
||||
* @server: pointer of server's rdpContext struct of the current session.
|
||||
*/
|
||||
BOOL pf_modules_run_filter(proxyModule* module, PF_FILTER_TYPE type, proxyData* pdata, void* param)
|
||||
{
|
||||
WINPR_ASSERT(module);
|
||||
WINPR_ASSERT(module->plugins);
|
||||
|
||||
return ArrayList_ForEach(module->plugins, pf_modules_ArrayList_ForEachFkt, type, pdata, param);
|
||||
}
|
||||
|
||||
/*
|
||||
* stores per-session data needed by a plugin.
|
||||
*
|
||||
* @context: current session server's rdpContext instance.
|
||||
* @info: pointer to per-session data.
|
||||
*/
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_modules_set_plugin_data(WINPR_ATTR_UNUSED proxyPluginsManager* mgr,
|
||||
const char* plugin_name, proxyData* pdata, void* data)
|
||||
{
|
||||
union
|
||||
{
|
||||
const char* ccp;
|
||||
char* cp;
|
||||
} ccharconv;
|
||||
|
||||
WINPR_ASSERT(plugin_name);
|
||||
|
||||
ccharconv.ccp = plugin_name;
|
||||
if (data == nullptr) /* no need to store anything */
|
||||
return FALSE;
|
||||
|
||||
if (!HashTable_Insert(pdata->modules_info, ccharconv.cp, data))
|
||||
{
|
||||
WLog_ERR(TAG, "HashTable_Insert failed!");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* returns per-session data needed a plugin.
|
||||
*
|
||||
* @context: current session server's rdpContext instance.
|
||||
* if there's no data related to `plugin_name` in `context` (current session), a nullptr will be
|
||||
* returned.
|
||||
*/
|
||||
WINPR_ATTR_NODISCARD
|
||||
static void* pf_modules_get_plugin_data(WINPR_ATTR_UNUSED proxyPluginsManager* mgr,
|
||||
const char* plugin_name, proxyData* pdata)
|
||||
{
|
||||
union
|
||||
{
|
||||
const char* ccp;
|
||||
char* cp;
|
||||
} ccharconv;
|
||||
WINPR_ASSERT(plugin_name);
|
||||
WINPR_ASSERT(pdata);
|
||||
ccharconv.ccp = plugin_name;
|
||||
|
||||
return HashTable_GetItemValue(pdata->modules_info, ccharconv.cp);
|
||||
}
|
||||
|
||||
static void pf_modules_abort_connect(WINPR_ATTR_UNUSED proxyPluginsManager* mgr, proxyData* pdata)
|
||||
{
|
||||
WINPR_ASSERT(pdata);
|
||||
WLog_DBG(TAG, "is called!");
|
||||
proxy_data_abort_connect(pdata);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_modules_register_ArrayList_ForEachFkt(void* data, size_t index, va_list ap)
|
||||
{
|
||||
proxyPlugin* plugin = (proxyPlugin*)data;
|
||||
proxyPlugin* plugin_to_register = va_arg(ap, proxyPlugin*);
|
||||
|
||||
WINPR_UNUSED(index);
|
||||
|
||||
if (strcmp(plugin->name, plugin_to_register->name) == 0)
|
||||
{
|
||||
WLog_ERR(TAG, "can not register plugin '%s', it is already registered!", plugin->name);
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_modules_register_plugin(proxyPluginsManager* mgr,
|
||||
const proxyPlugin* plugin_to_register)
|
||||
{
|
||||
proxyPlugin internal = WINPR_C_ARRAY_INIT;
|
||||
proxyModule* module = (proxyModule*)mgr;
|
||||
WINPR_ASSERT(module);
|
||||
|
||||
if (!plugin_to_register)
|
||||
return FALSE;
|
||||
|
||||
internal = *plugin_to_register;
|
||||
internal.mgr = mgr;
|
||||
|
||||
/* make sure there's no other loaded plugin with the same name of `plugin_to_register`. */
|
||||
if (!ArrayList_ForEach(module->plugins, pf_modules_register_ArrayList_ForEachFkt, &internal))
|
||||
return FALSE;
|
||||
|
||||
if (!ArrayList_Append(module->plugins, &internal))
|
||||
{
|
||||
WLog_ERR(TAG, "failed adding plugin to list: %s", plugin_to_register->name);
|
||||
return FALSE;
|
||||
}
|
||||
WLog_INFO(TAG, "Successfully registered proxy plugin '%s'", plugin_to_register->name);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_modules_load_ArrayList_ForEachFkt(void* data, size_t index, va_list ap)
|
||||
{
|
||||
proxyPlugin* plugin = (proxyPlugin*)data;
|
||||
const char* plugin_name = va_arg(ap, const char*);
|
||||
BOOL* res = va_arg(ap, BOOL*);
|
||||
|
||||
WINPR_UNUSED(index);
|
||||
WINPR_UNUSED(ap);
|
||||
WINPR_ASSERT(res);
|
||||
|
||||
if (strcmp(plugin->name, plugin_name) == 0)
|
||||
*res = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL pf_modules_is_plugin_loaded(proxyModule* module, const char* plugin_name)
|
||||
{
|
||||
BOOL rc = FALSE;
|
||||
WINPR_ASSERT(module);
|
||||
if (ArrayList_Count(module->plugins) < 1)
|
||||
return FALSE;
|
||||
if (!ArrayList_ForEach(module->plugins, pf_modules_load_ArrayList_ForEachFkt, plugin_name, &rc))
|
||||
return FALSE;
|
||||
return rc;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_modules_print_ArrayList_ForEachFkt(void* data, size_t index, va_list ap)
|
||||
{
|
||||
proxyPlugin* plugin = (proxyPlugin*)data;
|
||||
|
||||
WINPR_UNUSED(index);
|
||||
WINPR_UNUSED(ap);
|
||||
|
||||
WLog_INFO(TAG, "\tName: %s", plugin->name);
|
||||
WLog_INFO(TAG, "\tDescription: %s", plugin->description);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void pf_modules_list_loaded_plugins(proxyModule* module)
|
||||
{
|
||||
size_t count = 0;
|
||||
|
||||
WINPR_ASSERT(module);
|
||||
WINPR_ASSERT(module->plugins);
|
||||
|
||||
count = ArrayList_Count(module->plugins);
|
||||
|
||||
if (count > 0)
|
||||
WLog_INFO(TAG, "Loaded plugins:");
|
||||
|
||||
ArrayList_ForEach(module->plugins, pf_modules_print_ArrayList_ForEachFkt);
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_modules_load_static_module(const char* module_name, proxyModule* module,
|
||||
void* userdata)
|
||||
{
|
||||
WINPR_ASSERT(module);
|
||||
|
||||
HANDLE handle = GetModuleHandleA(nullptr);
|
||||
|
||||
if (handle == nullptr)
|
||||
{
|
||||
WLog_DBG(TAG, "failed loading static library: %s", module_name);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
char name[256] = WINPR_C_ARRAY_INIT;
|
||||
(void)_snprintf(name, sizeof(name), "%s_%s", module_name, MODULE_ENTRY_POINT);
|
||||
for (size_t x = 0; x < strnlen(name, sizeof(name)); x++)
|
||||
{
|
||||
if (name[x] == '-')
|
||||
name[x] = '_';
|
||||
}
|
||||
|
||||
proxyModuleEntryPoint pEntryPoint = GetProcAddressAs(handle, name, proxyModuleEntryPoint);
|
||||
if (!pEntryPoint)
|
||||
{
|
||||
WLog_DBG(TAG, "GetProcAddress failed for static %s (module %s)", name, module_name);
|
||||
goto error;
|
||||
}
|
||||
if (!ArrayList_Append(module->handles, handle))
|
||||
{
|
||||
WLog_ERR(TAG, "ArrayList_Append failed!");
|
||||
goto error;
|
||||
}
|
||||
return pf_modules_add(module, pEntryPoint, userdata);
|
||||
|
||||
error:
|
||||
FreeLibrary(handle);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_modules_load_dynamic_module(const char* module_path, proxyModule* module,
|
||||
void* userdata)
|
||||
{
|
||||
if (!winpr_PathFileExists(module_path))
|
||||
{
|
||||
WLog_DBG(TAG, "failed loading external library: file '%s' does not exist", module_path);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
HANDLE handle = LoadLibraryX(module_path);
|
||||
|
||||
if (handle == nullptr)
|
||||
{
|
||||
WLog_DBG(TAG, "failed loading external library: %s", module_path);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
proxyModuleEntryPoint pEntryPoint =
|
||||
GetProcAddressAs(handle, MODULE_ENTRY_POINT, proxyModuleEntryPoint);
|
||||
if (!pEntryPoint)
|
||||
{
|
||||
WLog_DBG(TAG, "GetProcAddress failed while loading %s", module_path);
|
||||
goto error;
|
||||
}
|
||||
if (!ArrayList_Append(module->handles, handle))
|
||||
{
|
||||
WLog_ERR(TAG, "ArrayList_Append failed!");
|
||||
goto error;
|
||||
}
|
||||
return pf_modules_add(module, pEntryPoint, userdata);
|
||||
|
||||
error:
|
||||
FreeLibrary(handle);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_modules_try_load_dynamic_module(const char* module_path, proxyModule* module,
|
||||
void* userdata, size_t count, const char* names[])
|
||||
{
|
||||
for (size_t x = 0; x < count; x++)
|
||||
{
|
||||
const char* name = names[x];
|
||||
|
||||
char* fullpath = GetCombinedPath(module_path, name);
|
||||
if (!fullpath)
|
||||
continue;
|
||||
|
||||
const BOOL rc = pf_modules_load_dynamic_module(fullpath, module, userdata);
|
||||
free(fullpath);
|
||||
if (rc)
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
WINPR_ATTR_NODISCARD
|
||||
static BOOL pf_modules_load_module(const char* module_path, const char* module_name,
|
||||
proxyModule* module, void* userdata)
|
||||
{
|
||||
WINPR_ASSERT(module);
|
||||
|
||||
if (pf_modules_load_static_module(module_name, module, userdata))
|
||||
return TRUE;
|
||||
|
||||
char names[5][MAX_PATH] = WINPR_C_ARRAY_INIT;
|
||||
(void)_snprintf(names[0], sizeof(names[0]), "proxy-%s-plugin%s", module_name,
|
||||
FREERDP_SHARED_LIBRARY_SUFFIX);
|
||||
(void)_snprintf(names[1], sizeof(names[1]), "%sproxy-%s-plugin%s",
|
||||
FREERDP_SHARED_LIBRARY_PREFIX, module_name, FREERDP_SHARED_LIBRARY_SUFFIX);
|
||||
(void)_snprintf(names[2], sizeof(names[2]), "%sproxy-%s-plugin%s.%d",
|
||||
FREERDP_SHARED_LIBRARY_PREFIX, module_name, FREERDP_SHARED_LIBRARY_SUFFIX,
|
||||
FREERDP_VERSION_MAJOR);
|
||||
(void)_snprintf(names[3], sizeof(names[3]), "%sproxy-%s-plugin%d%s",
|
||||
FREERDP_SHARED_LIBRARY_PREFIX, module_name, FREERDP_VERSION_MAJOR,
|
||||
FREERDP_SHARED_LIBRARY_SUFFIX);
|
||||
(void)_snprintf(names[4], sizeof(names[4]), "%sproxy-%s-plugin%d%s.%d",
|
||||
FREERDP_SHARED_LIBRARY_PREFIX, module_name, FREERDP_VERSION_MAJOR,
|
||||
FREERDP_SHARED_LIBRARY_SUFFIX, FREERDP_VERSION_MAJOR);
|
||||
|
||||
const char* cnames[5] = { names[0], names[1], names[2], names[3], names[4] };
|
||||
return pf_modules_try_load_dynamic_module(module_path, module, userdata, ARRAYSIZE(cnames),
|
||||
cnames);
|
||||
}
|
||||
|
||||
static void free_handle(void* obj)
|
||||
{
|
||||
HANDLE handle = (HANDLE)obj;
|
||||
if (handle)
|
||||
FreeLibrary(handle);
|
||||
}
|
||||
|
||||
static void free_plugin(void* obj)
|
||||
{
|
||||
proxyPlugin* plugin = (proxyPlugin*)obj;
|
||||
WINPR_ASSERT(plugin);
|
||||
|
||||
if (!IFCALLRESULT(TRUE, plugin->PluginUnload, plugin))
|
||||
WLog_WARN(TAG, "PluginUnload failed for plugin '%s'", plugin->name);
|
||||
|
||||
free(plugin);
|
||||
}
|
||||
|
||||
WINPR_ATTR_MALLOC(free_plugin, 1)
|
||||
WINPR_ATTR_NODISCARD
|
||||
static void* new_plugin(const void* obj)
|
||||
{
|
||||
const proxyPlugin* src = obj;
|
||||
proxyPlugin* proxy = calloc(1, sizeof(proxyPlugin));
|
||||
if (!proxy)
|
||||
return nullptr;
|
||||
*proxy = *src;
|
||||
return proxy;
|
||||
}
|
||||
|
||||
proxyModule* pf_modules_new(const char* root_dir, const char** modules, size_t count)
|
||||
{
|
||||
wObject* obj = nullptr;
|
||||
char* path = nullptr;
|
||||
proxyModule* module = calloc(1, sizeof(proxyModule));
|
||||
if (!module)
|
||||
return nullptr;
|
||||
|
||||
module->mgr.RegisterPlugin = pf_modules_register_plugin;
|
||||
module->mgr.SetPluginData = pf_modules_set_plugin_data;
|
||||
module->mgr.GetPluginData = pf_modules_get_plugin_data;
|
||||
module->mgr.AbortConnect = pf_modules_abort_connect;
|
||||
module->plugins = ArrayList_New(FALSE);
|
||||
|
||||
if (module->plugins == nullptr)
|
||||
{
|
||||
WLog_ERR(TAG, "ArrayList_New failed!");
|
||||
goto error;
|
||||
}
|
||||
obj = ArrayList_Object(module->plugins);
|
||||
WINPR_ASSERT(obj);
|
||||
|
||||
obj->fnObjectFree = free_plugin;
|
||||
obj->fnObjectNew = new_plugin;
|
||||
|
||||
module->handles = ArrayList_New(FALSE);
|
||||
if (module->handles == nullptr)
|
||||
{
|
||||
|
||||
WLog_ERR(TAG, "ArrayList_New failed!");
|
||||
goto error;
|
||||
}
|
||||
ArrayList_Object(module->handles)->fnObjectFree = free_handle;
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
WINPR_ASSERT(root_dir);
|
||||
if (!winpr_PathFileExists(root_dir))
|
||||
path = GetCombinedPath(FREERDP_INSTALL_PREFIX, root_dir);
|
||||
else
|
||||
path = _strdup(root_dir);
|
||||
|
||||
if (!winpr_PathFileExists(path))
|
||||
{
|
||||
if (!winpr_PathMakePath(path, nullptr))
|
||||
{
|
||||
WLog_ERR(TAG, "error occurred while creating modules directory: %s", root_dir);
|
||||
}
|
||||
}
|
||||
|
||||
if (winpr_PathFileExists(path))
|
||||
WLog_DBG(TAG, "modules root directory: %s", path);
|
||||
|
||||
for (size_t i = 0; i < count; i++)
|
||||
{
|
||||
const char* module_name = modules[i];
|
||||
if (!pf_modules_load_module(path, module_name, module, nullptr))
|
||||
WLog_WARN(TAG, "Failed to load proxy module '%s'", module_name);
|
||||
else
|
||||
WLog_INFO(TAG, "Successfully loaded proxy module '%s'", module_name);
|
||||
}
|
||||
}
|
||||
|
||||
free(path);
|
||||
return module;
|
||||
|
||||
error:
|
||||
free(path);
|
||||
pf_modules_free(module);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void pf_modules_free(proxyModule* module)
|
||||
{
|
||||
if (!module)
|
||||
return;
|
||||
|
||||
ArrayList_Free(module->plugins);
|
||||
ArrayList_Free(module->handles);
|
||||
free(module);
|
||||
}
|
||||
|
||||
BOOL pf_modules_add(proxyModule* module, proxyModuleEntryPoint ep, void* userdata)
|
||||
{
|
||||
WINPR_ASSERT(module);
|
||||
WINPR_ASSERT(ep);
|
||||
|
||||
return ep(&module->mgr, userdata);
|
||||
}
|
||||
Reference in New Issue
Block a user