* Add support for C++ plugins.
* Seperate between a "module" and a "plugin": a module is a shared
library that exposes a function called "proxy_module_entry_point". This
function gets a parameter to `proxyPluginsManager*`, which can be used to
register plugins.
* Refine the API of the modules infrastructure:
* use proxyData* instead of rdpContext* when firing a hook or calling a filter.
* use clearer names.
* Instead of having to specify a path for each module to be loaded in
the configuration, the proxy now loads modules from specificed
directory, using the CMAKE variable PROXY_PLUGINDIR.
* Add an option to specify required plugins: plugins that the proxy
wouldn't start without having them fully loaded.
# Path to put extensions
set(FREERDP_EXTENSION_PATH "${CMAKE_INSTALL_FULL_LIBDIR}/freerdp${FREERDP_VERSION_MAJOR}/extensions")
+# Proxy plugins path
+if(NOT DEFINED PROXY_PLUGINDIR)
+ message("using default plugins location")
+ set(FREERDP_PROXY_PLUGINDIR "${CMAKE_BINARY_DIR}/server/proxy/plugins")
+else()
+ set(FREERDP_PROXY_PLUGINDIR "${PROXY_PLUGINDIR}")
+endif()
+
# Include directories
include_directories(${CMAKE_CURRENT_BINARY_DIR})
include_directories(${CMAKE_CURRENT_BINARY_DIR}/include)
#define FREERDP_VENDOR_STRING "${VENDOR}"
#define FREERDP_PRODUCT_STRING "${PRODUCT}"
+#define FREERDP_PROXY_PLUGINDIR "${FREERDP_PROXY_PLUGINDIR}"
+
#endif /* FREERDP_BUILD_CONFIG_H */
TextOnly = FALSE
MaxTextLength = 10 # 0 for no limit.
-[Modules]
-Demo = "modules/demo/libdemo.so"
+[Plugins]
+Required = "demo"
#include "pf_log.h"
#include "pf_modules.h"
+#include <freerdp/build-config.h>
#include <winpr/collections.h>
#define TAG PROXY_TAG("server")
if (argc > 1)
cfg = argv[1];
- if (!pf_modules_init())
+ if (!pf_modules_init(FREERDP_PROXY_PLUGINDIR))
+ {
+ WLog_ERR(TAG, "failed to initialize proxy plugins!");
goto fail;
+ }
+
+ pf_modules_list_loaded_plugins();
if (!pf_server_config_load(cfg, config))
goto fail;
+#
+# FreeRDP: A Remote Desktop Protocol Implementation
+# FreeRDP Proxy Server Demo C++ Module
+#
+# Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
+#
+# 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.
+#
-add_library(demo SHARED
- demo.c
+set(PLUGIN_NAME "proxy-demo-plugin")
+
+add_library(${PLUGIN_NAME} MODULE
+ demo.cpp
)
+
+set_target_properties(${PLUGIN_NAME} PROPERTIES PREFIX "")
+set_target_properties(${PLUGIN_NAME} PROPERTIES NO_SONAME 1)
+set_target_properties(${PLUGIN_NAME} PROPERTIES
+LIBRARY_OUTPUT_DIRECTORY "${FREERDP_PROXY_PLUGINDIR}")
+++ /dev/null
-/**
- * FreeRDP: A Remote Desktop Protocol Implementation
- * FreeRDP Proxy Server
- *
- * Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
- *
- * 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 <stdio.h>
-
-#include "modules_api.h"
-
-static BOOL demo_filter_keyboard_event(moduleOperations* module, rdpContext* context, void* param)
-{
- proxyKeyboardEventInfo* event_data = (proxyKeyboardEventInfo*)param;
- WINPR_UNUSED(event_data);
-
- return TRUE;
-}
-
-static BOOL demo_filter_mouse_event(moduleOperations* module, rdpContext* context, void* param)
-{
- proxyMouseEventInfo* event_data = (proxyMouseEventInfo*)param;
-
- if (event_data->x % 100 == 0)
- {
- printf("filter_demo: mouse x is currently %" PRIu16 "\n", event_data->x);
- }
-
- return TRUE;
-}
-
-BOOL module_init(moduleOperations* module)
-{
- module->KeyboardEvent = demo_filter_keyboard_event;
- module->MouseEvent = demo_filter_mouse_event;
-
- return TRUE;
-}
-
-BOOL module_exit(moduleOperations* module)
-{
- printf("bye bye\n");
-
- return TRUE;
-}
--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * FreeRDP Proxy Server Demo C++ Module
+ *
+ * Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
+ *
+ * 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 <iostream>
+
+#include "modules_api.h"
+
+#define TAG MODULE_TAG("demo")
+
+static constexpr char plugin_name[] = "demo";
+static constexpr char plugin_desc[] = "this is a test plugin";
+
+static proxyPluginsManager* g_plugins_manager = NULL;
+
+static BOOL demo_filter_keyboard_event(proxyData* pdata, void* param)
+{
+ auto event_data = static_cast<proxyKeyboardEventInfo*>(param);
+ if (event_data == NULL)
+ return FALSE;
+
+ if (event_data->rdp_scan_code == RDP_SCANCODE_KEY_B)
+ {
+ /* user typed 'B', that means bye :) */
+ std::cout << "C++ demo plugin: aborting connection" << std::endl;
+ g_plugins_manager->AbortConnect(pdata);
+ }
+
+ return TRUE;
+}
+
+static BOOL demo_plugin_unload()
+{
+ std::cout << "C++ demo plugin: unloading..." << std::endl;
+ return TRUE;
+}
+
+static proxyPlugin demo_plugin = {
+ plugin_name, /* name */
+ plugin_desc, /* description */
+ NULL, /* ClientPreConnect */
+ NULL, /* ClientLoginFailure */
+ NULL, /* ServerPostConnect */
+ NULL, /* ServerChannelsInit */
+ NULL, /* ServerChannelsFree */
+ demo_filter_keyboard_event, /* KeyboardEvent */
+ NULL, /* MouseEvent */
+ demo_plugin_unload /* PluginUnload */
+};
+
+BOOL proxy_module_entry_point(proxyPluginsManager* plugins_manager)
+{
+ g_plugins_manager = plugins_manager;
+
+ return plugins_manager->RegisterPlugin(&demo_plugin);
+}
#include <freerdp/freerdp.h>
#include <winpr/winpr.h>
-#define PROXY_API FREERDP_API
+#include "../pf_context.h"
-typedef struct module_operations moduleOperations;
+#define MODULE_TAG(module) "proxy.modules." module
-/* used for filtering */
-typedef BOOL (*proxyFilterFn)(moduleOperations*, rdpContext*, void*);
-
-/* used for hooks */
-typedef BOOL (*proxyHookFn)(moduleOperations*, rdpContext*);
-
-/*
- * used for per-session info.
- *
- * each module is allowed to store data per session.
- * it is useful, for example, when a module wants to create a server channel in runtime,
- * or to save information about a session (for example, the count of mouse clicks in the last
- * minute), and then do something with this information (maybe abort the connection).
- */
-typedef BOOL (*moduleSetSessionData)(moduleOperations*, rdpContext*, void*);
-typedef void* (*moduleGetSessionData)(moduleOperations*, rdpContext*);
+/* hook callback. should return TRUE on success or FALSE on error. */
+typedef BOOL (*proxyHookFn)(proxyData*);
/*
- * used for connection management. when a module wants to forcibly close a connection, it should
- * call this method.
+ * Filter callback:
+ * It MUST return TRUE if the related event should be proxied,
+ * or FALSE if it should be ignored.
*/
-typedef void (*moduleAbortConnect)(moduleOperations*, rdpContext*);
-
-typedef struct connection_info connectionInfo;
-typedef struct proxy_keyboard_event_info proxyKeyboardEventInfo;
-typedef struct proxy_mouse_event_info proxyMouseEventInfo;
+typedef BOOL (*proxyFilterFn)(proxyData*, void*);
-/* represents a set of operations that a module can do */
-struct module_operations
+/* describes a plugin: name, description and callbacks to execute. */
+typedef struct proxy_plugin
{
- /* per-session API. a module must not change these function pointers. */
- moduleSetSessionData SetSessionData;
- moduleGetSessionData GetSessionData;
- moduleAbortConnect AbortConnect;
+ const char* name; /* unique module name */
+ const char* description; /* module description */
- /* proxy hooks. a module can set these function pointers to register hooks. */
+ /* proxy hooks. a module can set these function pointers to register hooks */
proxyHookFn ClientPreConnect;
+ proxyHookFn ClientLoginFailure;
+ proxyHookFn ServerPostConnect;
proxyHookFn ServerChannelsInit;
proxyHookFn ServerChannelsFree;
- /* proxy filters a module can set these function pointers to register filters. */
+ /* proxy filters. a module can set these function pointers to register filters */
proxyFilterFn KeyboardEvent;
proxyFilterFn MouseEvent;
-};
+
+ BOOL (*PluginUnload)();
+} proxyPlugin;
+
+/*
+ * Main API for use by external modules.
+ * Supports:
+ * - Registering a plugin.
+ * - Setting/getting plugin's per-session specific data.
+ * - Aborting a session.
+ */
+typedef struct proxy_plugins_manager
+{
+ /* used for registering a fresh new proxy plugin. */
+ BOOL (*RegisterPlugin)(proxyPlugin* plugin);
+
+ /* used for setting plugin's per-session info. */
+ BOOL (*SetPluginData)(const char*, proxyData*, void*);
+
+ /* used for getting plugin's per-session info. */
+ void* (*GetPluginData)(const char*, proxyData*);
+
+ /* used for aborting a session. */
+ void (*AbortConnect)(proxyData*);
+} proxyPluginsManager;
/* filter events parameters */
#define WINPR_PACK_PUSH
#include <winpr/pack.h>
-struct proxy_keyboard_event_info
+typedef struct proxy_keyboard_event_info
{
UINT16 flags;
UINT16 rdp_scan_code;
-};
+} proxyKeyboardEventInfo;
-struct proxy_mouse_event_info
+typedef struct proxy_mouse_event_info
{
UINT16 flags;
UINT16 x;
UINT16 y;
-};
+} proxyMouseEventInfo;
#define WINPR_PACK_POP
#include <winpr/pack.h>
-/*
- * these two functions must be implemented by any proxy module.
- * module_init: used for module initialization, hooks and filters registration.
- */
-PROXY_API BOOL module_init(moduleOperations* module);
-PROXY_API BOOL module_exit(moduleOperations* module);
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ FREERDP_API BOOL proxy_module_entry_point(proxyPluginsManager* plugins_manager);
+
+#ifdef __cplusplus
+};
+#endif
#endif /* FREERDP_SERVER_PROXY_MODULES_API_H */
return FALSE;
}
- return pf_modules_run_hook(HOOK_TYPE_SERVER_CHANNELS_INIT, context);
+ return pf_modules_run_hook(HOOK_TYPE_SERVER_CHANNELS_INIT, ps->pdata);
}
void pf_server_channels_free(pServerContext* ps)
ps->rail = NULL;
}
- pf_modules_run_hook(HOOK_TYPE_SERVER_CHANNELS_FREE, (rdpContext*)ps);
+ pf_modules_run_hook(HOOK_TYPE_SERVER_CHANNELS_FREE, ps->pdata);
}
goto out;
LOG_ERR(TAG, pc, "failed to connect with NLA. retrying to connect without NLA");
+ pf_modules_run_hook(HOOK_TYPE_CLIENT_LOGIN_FAILURE, pc->pdata);
if (!pf_client_connect_without_nla(pc))
{
{
freerdp* instance = (freerdp*)arg;
pClientContext* pc = (pClientContext*)instance->context;
- pServerContext* ps = pc->pdata->ps;
proxyData* pdata = pc->pdata;
DWORD nCount;
DWORD status;
*/
handles[64] = pdata->abort_event;
- if (!pf_modules_run_hook(HOOK_TYPE_CLIENT_PRE_CONNECT, (rdpContext*)ps))
+ if (!pf_modules_run_hook(HOOK_TYPE_CLIENT_PRE_CONNECT, pdata))
{
proxy_data_abort_connect(pdata);
return FALSE;
if (nCount == 0)
{
- LOG_ERR(TAG, ps, "freerdp_get_event_handles failed!");
+ LOG_ERR(TAG, pc, "freerdp_get_event_handles failed!");
break;
}
if (!value)
{
- WLog_ERR(TAG, "[%s]: key '%s.%s' not found.", __FUNCTION__, key, section);
+ WLog_ERR(TAG, "[%s]: key '%s.%s' not found.", __FUNCTION__, section, key);
return NULL;
}
static BOOL pf_config_load_modules(wIniFile* ini, proxyConfig* config)
{
- int index;
- int modules_count = 0;
- char** module_names = NULL;
+ const char* required_modules;
+ char* tmp;
- module_names = IniFile_GetSectionKeyNames(ini, "Modules", &modules_count);
-
- for (index = 0; index < modules_count; index++)
- {
- char* module_name = module_names[index];
- const char* path = pf_config_get_str(ini, "Modules", module_name);
+ /* make sure that all required modules are loaded */
+ required_modules = IniFile_GetKeyValueString(ini, "Plugins", "Required");
+ if (!required_modules)
+ return TRUE;
- if (!path)
- continue;
+ tmp = strtok((char*)required_modules, ",");
- if (!pf_modules_register_new(path, module_name))
+ while (tmp != NULL)
+ {
+ if (!pf_modules_is_plugin_loaded(tmp))
{
- WLog_ERR(TAG, "pf_config_load_modules(): failed to register %s (%s)", module_name,
- path);
- continue;
+ WLog_ERR(TAG, "Required plugin '%s' is not loaded. stopping.", tmp);
+ return FALSE;
}
- WLog_INFO(TAG, "module '%s' is loaded!", module_name);
+ tmp = strtok(NULL, ",");
}
- free(module_names);
return TRUE;
}
static BOOL client_to_proxy_context_new(freerdp_peer* client, pServerContext* context)
{
context->dynvcReady = NULL;
- context->modules_info = NULL;
-
- context->modules_info = HashTable_New(TRUE);
- if (!context->modules_info)
- return FALSE;
context->vcm = WTSOpenServerA((LPSTR)client->context);
return TRUE;
error:
- HashTable_Free(context->modules_info);
WTSCloseServer((HANDLE)context->vcm);
context->vcm = NULL;
CloseHandle(context->dynvcReady);
context->dynvcReady = NULL;
}
-
- HashTable_Free(context->modules_info);
}
BOOL pf_context_init_server_context(freerdp_peer* client)
return NULL;
}
+ if (!(pdata->modules_info = HashTable_New(FALSE)))
+ {
+ proxy_data_free(pdata);
+ return NULL;
+ }
+
+ /* modules_info maps between plugin name to custom data */
+ pdata->modules_info->hash = HashTable_StringHash;
+ pdata->modules_info->keyCompare = HashTable_StringCompare;
+ pdata->modules_info->keyClone = HashTable_StringClone;
+ pdata->modules_info->keyFree = HashTable_StringFree;
return pdata;
}
if (pdata->session_id)
free(pdata->session_id);
+ if (pdata->modules_info)
+ HashTable_Free(pdata->modules_info);
+
free(pdata);
}
DispServerContext* disp;
CliprdrServerContext* cliprdr;
RdpsndServerContext* rdpsnd;
-
- /* used to external modules to store per-session info */
- wHashTable* modules_info;
};
typedef struct p_server_context pServerContext;
HANDLE gfx_server_ready;
char* session_id;
+
+ /* used to external modules to store per-session info */
+ wHashTable* modules_info;
};
BOOL pf_context_copy_settings(rdpSettings* dst, const rdpSettings* src);
event.flags = flags;
event.rdp_scan_code = code;
- if (pf_modules_run_filter(FILTER_TYPE_KEYBOARD, input->context, &event))
+ if (pf_modules_run_filter(FILTER_TYPE_KEYBOARD, pc->pdata, &event))
return freerdp_input_send_keyboard_event(pc->context.input, flags, code);
return TRUE;
event.x = x;
event.y = y;
- if (pf_modules_run_filter(FILTER_TYPE_MOUSE, input->context, &event))
+ if (pf_modules_run_filter(FILTER_TYPE_MOUSE, pc->pdata, &event))
return freerdp_input_send_mouse_event(pc->context.input, flags, x, y);
return TRUE;
#include <assert.h>
+#include <winpr/file.h>
#include <winpr/wlog.h>
#include <winpr/library.h>
#include <freerdp/api.h>
+#include <freerdp/build-config.h>
#include "pf_log.h"
#include "pf_modules.h"
#define TAG PROXY_TAG("modules")
-#define MODULE_INIT_METHOD "module_init"
-#define MODULE_EXIT_METHOD "module_exit"
+#define MODULE_ENTRY_POINT "proxy_module_entry_point"
-static modules_list* proxy_modules = NULL;
+static wArrayList* plugins_list = NULL; /* list of all loaded plugins */
+static wArrayList* handles_list = NULL; /* list of module handles to free at shutdown */
-/* module init/exit methods */
-typedef BOOL (*moduleInitFn)(moduleOperations* ops);
-typedef BOOL (*moduleExitFn)(moduleOperations* ops);
+typedef BOOL (*moduleEntryPoint)(proxyPluginsManager* plugins_manager);
static const char* FILTER_TYPE_STRINGS[] = {
"KEYBOARD_EVENT",
};
static const char* HOOK_TYPE_STRINGS[] = {
- "CLIENT_PRE_CONNECT",
- "SERVER_CHANNELS_INIT",
- "SERVER_CHANNELS_FREE",
+ "CLIENT_PRE_CONNECT", "CLIENT_LOGIN_FAILURE", "SERVER_POST_CONNECT",
+ "SERVER_CHANNELS_INIT", "SERVER_CHANNELS_FREE",
};
static const char* pf_modules_get_filter_type_string(PF_FILTER_TYPE result)
{
- if (result >= FILTER_TYPE_KEYBOARD && result <= FILTER_TYPE_MOUSE)
+ if (result >= FILTER_TYPE_KEYBOARD && result < FILTER_LAST)
return FILTER_TYPE_STRINGS[result];
else
return "FILTER_UNKNOWN";
static const char* pf_modules_get_hook_type_string(PF_HOOK_TYPE result)
{
- if (result >= HOOK_TYPE_CLIENT_PRE_CONNECT && result <= HOOK_TYPE_SERVER_CHANNELS_FREE)
+ if (result >= HOOK_TYPE_CLIENT_PRE_CONNECT && result < HOOK_LAST)
return HOOK_TYPE_STRINGS[result];
else
return "HOOK_UNKNOWN";
}
-BOOL pf_modules_init(void)
-{
- proxy_modules = ArrayList_New(FALSE);
-
- if (proxy_modules == NULL)
- {
- WLog_ERR(TAG, "pf_modules_init(): ArrayList_New failed!");
- 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(PF_HOOK_TYPE type, rdpContext* context)
+BOOL pf_modules_run_hook(PF_HOOK_TYPE type, proxyData* pdata)
{
-
- proxyModule* module;
- moduleOperations* ops;
BOOL ok = TRUE;
- const size_t count = (size_t)ArrayList_Count(proxy_modules);
size_t index;
+ proxyPlugin* plugin;
- for (index = 0; index < count; index++)
+ ArrayList_ForEach(plugins_list, proxyPlugin*, index, plugin)
{
- module = (proxyModule*)ArrayList_GetItem(proxy_modules, index);
- ops = module->ops;
- WLog_VRB(TAG, "[%s]: Running module %s, hook %s", __FUNCTION__, module->name,
- pf_modules_get_hook_type_string(type));
+ WLog_VRB(TAG, "running hook %s.%s", plugin->name, pf_modules_get_hook_type_string(type));
switch (type)
{
case HOOK_TYPE_CLIENT_PRE_CONNECT:
- IFCALLRET(ops->ClientPreConnect, ok, ops, context);
+ IFCALLRET(plugin->ClientPreConnect, ok, pdata);
+ break;
+
+ case HOOK_TYPE_CLIENT_LOGIN_FAILURE:
+ IFCALLRET(plugin->ClientLoginFailure, ok, pdata);
+ break;
+
+ case HOOK_TYPE_SERVER_POST_CONNECT:
+ IFCALLRET(plugin->ServerPostConnect, ok, pdata);
break;
case HOOK_TYPE_SERVER_CHANNELS_INIT:
- IFCALLRET(ops->ServerChannelsInit, ok, ops, context);
+ IFCALLRET(plugin->ServerChannelsInit, ok, pdata);
break;
case HOOK_TYPE_SERVER_CHANNELS_FREE:
- IFCALLRET(ops->ServerChannelsFree, ok, ops, context);
+ IFCALLRET(plugin->ServerChannelsFree, ok, pdata);
break;
+
+ default:
+ WLog_ERR(TAG, "invalid hook called");
}
if (!ok)
{
- WLog_INFO(TAG, "Module %s, hook %s failed!", module->name,
+ WLog_INFO(TAG, "plugin %s, hook %s failed!", plugin->name,
pf_modules_get_hook_type_string(type));
return FALSE;
}
* @type: filter type to run.
* @server: pointer of server's rdpContext struct of the current session.
*/
-BOOL pf_modules_run_filter(PF_FILTER_TYPE type, rdpContext* server, void* param)
+BOOL pf_modules_run_filter(PF_FILTER_TYPE type, proxyData* pdata, void* param)
{
- proxyModule* module;
- moduleOperations* ops;
BOOL result = TRUE;
- const size_t count = (size_t)ArrayList_Count(proxy_modules);
size_t index;
+ proxyPlugin* plugin;
- for (index = 0; index < count; index++)
+ ArrayList_ForEach(plugins_list, proxyPlugin*, index, plugin)
{
- module = (proxyModule*)ArrayList_GetItem(proxy_modules, index);
- ops = module->ops;
- WLog_VRB(TAG, "[%s]: running filter: %s", __FUNCTION__, module->name);
+ WLog_VRB(TAG, "[%s]: running filter: %s", __FUNCTION__, plugin->name);
switch (type)
{
case FILTER_TYPE_KEYBOARD:
- IFCALLRET(ops->KeyboardEvent, result, ops, server, param);
+ IFCALLRET(plugin->KeyboardEvent, result, pdata, param);
break;
case FILTER_TYPE_MOUSE:
- IFCALLRET(ops->MouseEvent, result, ops, server, param);
+ IFCALLRET(plugin->MouseEvent, result, pdata, param);
break;
+
+ default:
+ WLog_ERR(TAG, "invalid filter called");
}
if (!result)
{
/* current filter return FALSE, no need to run other filters. */
- WLog_INFO(TAG, "module %s, filter type [%s] returned FALSE", module->name,
+ WLog_INFO(TAG, "plugin %s, filter type [%s] returned FALSE", plugin->name,
pf_modules_get_filter_type_string(type));
return result;
}
return TRUE;
}
-static void pf_modules_module_free(proxyModule* module)
+/*
+ * stores per-session data needed by a plugin.
+ *
+ * @context: current session server's rdpContext instance.
+ * @info: pointer to per-session data.
+ */
+static BOOL pf_modules_set_plugin_data(const char* plugin_name, proxyData* pdata, void* data)
{
- moduleExitFn exitFn;
-
- assert(module);
- assert(module->handle);
+ assert(plugin_name);
- exitFn = (moduleExitFn)GetProcAddress(module->handle, MODULE_EXIT_METHOD);
+ if (data == NULL) /* no need to store anything */
+ return FALSE;
- if (!exitFn)
+ if (HashTable_Add(pdata->modules_info, (void*)plugin_name, data) < 0)
{
- WLog_ERR(TAG, "[%s]: GetProcAddress module_exit for %s failed!", __FUNCTION__,
- module->name);
- }
- else
- {
- if (!exitFn(module->ops))
- {
- WLog_ERR(TAG, "[%s]: module_exit failed for %s!", __FUNCTION__, module->name);
- }
+ WLog_ERR(TAG, "[%s]: HashTable_Add failed!");
+ return FALSE;
}
- FreeLibrary(module->handle);
- module->handle = NULL;
-
- free(module->name);
- free(module->ops);
- free(module);
+ return TRUE;
}
-void pf_modules_free(void)
+/*
+ * 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 NULL will be
+ * returned.
+ */
+static void* pf_modules_get_plugin_data(const char* plugin_name, proxyData* pdata)
{
- size_t index, count;
+ assert(plugin_name);
+ assert(pdata);
- if (proxy_modules == NULL)
- return;
-
- count = (size_t)ArrayList_Count(proxy_modules);
-
- for (index = 0; index < count; index++)
- {
- proxyModule* module = (proxyModule*)ArrayList_GetItem(proxy_modules, index);
- WLog_INFO(TAG, "[%s]: freeing module: %s", __FUNCTION__, module->name);
- pf_modules_module_free(module);
- }
+ return HashTable_GetItemValue(pdata->modules_info, (void*)plugin_name);
+}
- ArrayList_Free(proxy_modules);
+static void pf_modules_abort_connect(proxyData* pdata)
+{
+ assert(pdata);
+ WLog_DBG(TAG, "%s is called!", __FUNCTION__);
+ proxy_data_abort_connect(pdata);
}
-/*
- * stores per-session data needed by module.
- *
- * @context: current session server's rdpContext instance.
- * @info: pointer to per-session data.
- */
-static BOOL pf_modules_set_session_data(moduleOperations* module, rdpContext* context, void* data)
+static BOOL pf_modules_register_plugin(proxyPlugin* plugin_to_register)
{
- pServerContext* ps;
+ size_t index;
+ proxyPlugin* plugin;
- assert(module);
- assert(context);
+ assert(plugins_list != NULL);
- if (data == NULL) /* no need to store anything */
- return FALSE;
+ /* make sure there's no other loaded plugin with the same name of `plugin_to_register`. */
+ ArrayList_ForEach(plugins_list, proxyPlugin*, index, plugin)
+ {
+ if (strcmp(plugin->name, plugin_to_register->name) == 0)
+ {
+ WLog_ERR(TAG, "can not register plugin '%s', it is already registered!");
+ return FALSE;
+ }
+ }
- ps = (pServerContext*)context;
- if (HashTable_Add(ps->modules_info, (void*)module, data) < 0)
+ if (ArrayList_Add(plugins_list, plugin_to_register) < 0)
{
- WLog_ERR(TAG, "[%s]: HashTable_Add failed!");
+ WLog_ERR(TAG, "[%s]: failed adding plugin to list: %s", __FUNCTION__, plugin->name);
return FALSE;
}
return TRUE;
}
-/*
- * returns per-session data needed by module.
- *
- * @context: current session server's rdpContext instance.
- * if there's no data related to `module` in `context` (current session), a NULL will be returned.
- */
-static void* pf_modules_get_session_data(moduleOperations* module, rdpContext* context)
+BOOL pf_modules_is_plugin_loaded(const char* plugin_name)
{
- pServerContext* ps;
+ size_t i;
+ proxyPlugin* plugin;
+
+ if (plugins_list == NULL)
+ return FALSE;
- assert(module);
- assert(context);
+ ArrayList_ForEach(plugins_list, proxyPlugin*, i, plugin)
+ {
+ if (strcmp(plugin->name, plugin_name) == 0)
+ return TRUE;
+ }
- ps = (pServerContext*)context;
- return HashTable_GetItemValue(ps->modules_info, module);
+ return FALSE;
}
-static void pf_modules_abort_connect(moduleOperations* module, rdpContext* context)
+void pf_modules_list_loaded_plugins(void)
{
- pServerContext* ps;
+ size_t count;
+ size_t i;
+ proxyPlugin* plugin;
- assert(module);
- assert(context);
+ if (plugins_list == NULL)
+ return;
- WLog_INFO(TAG, "%s is called!", __FUNCTION__);
+ count = (size_t)ArrayList_Count(plugins_list);
- ps = (pServerContext*)context;
- proxy_data_abort_connect(ps->pdata);
+ if (count > 0)
+ WLog_INFO(TAG, "Loaded plugins:");
+
+ ArrayList_ForEach(plugins_list, proxyPlugin*, i, plugin)
+ {
+
+ WLog_INFO(TAG, "\tName: %s", plugin->name);
+ WLog_INFO(TAG, "\tDescription: %s", plugin->description);
+ }
}
-BOOL pf_modules_register_new(const char* module_path, const char* module_name)
+static proxyPluginsManager plugins_manager = { pf_modules_register_plugin,
+ pf_modules_set_plugin_data,
+ pf_modules_get_plugin_data,
+ pf_modules_abort_connect };
+
+static BOOL pf_modules_load_module(const char* module_path)
{
- moduleOperations* ops = NULL;
- proxyModule* module = NULL;
HMODULE handle = NULL;
- moduleInitFn fn;
-
- assert(proxy_modules != NULL);
+ moduleEntryPoint pEntryPoint;
handle = LoadLibraryA(module_path);
if (handle == NULL)
{
- WLog_ERR(TAG, "pf_modules_register_new(): failed loading external module: %s", module_path);
+ WLog_ERR(TAG, "[%s]: failed loading external library: %s", __FUNCTION__, module_path);
return FALSE;
}
- if (!(fn = (moduleInitFn)GetProcAddress(handle, MODULE_INIT_METHOD)))
+ if (!(pEntryPoint = (moduleEntryPoint)GetProcAddress(handle, MODULE_ENTRY_POINT)))
{
- WLog_ERR(TAG, "pf_modules_register_new(): GetProcAddress failed while loading %s",
- module_path);
+ WLog_ERR(TAG, "[%s]: GetProcAddress failed while loading %s", __FUNCTION__, module_path);
goto error;
}
- module = (proxyModule*)calloc(1, sizeof(proxyModule));
-
- if (module == NULL)
+ if (!pEntryPoint(&plugins_manager))
{
- WLog_ERR(TAG, "pf_modules_register_new(): malloc failed");
+ WLog_ERR(TAG, "[%s]: module %s entry point failed!", __FUNCTION__, module_path);
goto error;
}
- ops = calloc(1, sizeof(moduleOperations));
+ /* save module handle for freeing the module later */
+ if (ArrayList_Add(handles_list, handle) < 0)
+ {
+ WLog_ERR(TAG, "ArrayList_Add failed!");
+ return FALSE;
+ }
+
+ return TRUE;
- if (ops == NULL)
+error:
+ FreeLibrary(handle);
+ return FALSE;
+}
+
+BOOL pf_modules_init(const char* modules_directory)
+{
+ WIN32_FIND_DATA ffd;
+ HANDLE hFind = INVALID_HANDLE_VALUE;
+
+ WLog_DBG(TAG, "Searching plugins in directory %s", modules_directory);
+
+ hFind = FindFirstFile(modules_directory, &ffd);
+
+ if (INVALID_HANDLE_VALUE == hFind)
{
- WLog_ERR(TAG, "pf_modules_register_new(): calloc moduleOperations failed");
- goto error;
+ WLog_ERR(TAG, "FindFirstFile failed!");
+ return FALSE;
}
- ops->AbortConnect = pf_modules_abort_connect;
- ops->SetSessionData = pf_modules_set_session_data;
- ops->GetSessionData = pf_modules_get_session_data;
+ plugins_list = ArrayList_New(FALSE);
- if (!fn(ops))
+ if (plugins_list == NULL)
{
- WLog_ERR(TAG, "pf_modules_register_new(): failed to initialize module %s", module_path);
- goto error;
+ WLog_ERR(TAG, "[%s]: ArrayList_New failed!", __FUNCTION__);
+ return FALSE;
}
- module->name = _strdup(module_name);
- if (!module->name)
+ handles_list = ArrayList_New(FALSE);
+ if (handles_list == NULL)
{
- WLog_ERR(TAG, "pf_modules_register_new(): _strdup failed while loading %s", module_path);
- goto error;
+ ArrayList_Free(plugins_list);
+ plugins_list = NULL;
+
+ WLog_ERR(TAG, "[%s]: ArrayList_New failed!", __FUNCTION__);
+ return FALSE;
}
- module->handle = handle;
- module->ops = ops;
- module->enabled = TRUE;
+ do
+ {
+ if ((ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0)
+ {
+ char* fullpath = GetCombinedPath(modules_directory, ffd.cFileName);
+ char* dot = strrchr(ffd.cFileName, '.');
+
+ if (dot && strcmp(dot, FREERDP_SHARED_LIBRARY_SUFFIX) == 0)
+ pf_modules_load_module(fullpath);
+
+ free(fullpath);
+ }
+ } while (FindNextFile(hFind, &ffd) != 0);
+
+ FindClose(hFind);
+ return TRUE;
+}
+
+void pf_modules_free(void)
+{
+ size_t index;
- if (ArrayList_Add(proxy_modules, module) < 0)
+ if (plugins_list)
{
- WLog_ERR(TAG, "pf_modules_register_new(): failed adding module to list: %s", module_path);
- goto error;
+ proxyPlugin* plugin;
+
+ ArrayList_ForEach(plugins_list, proxyPlugin*, index, plugin)
+ {
+ if (!IFCALLRESULT(TRUE, plugin->PluginUnload))
+ WLog_WARN(TAG, "PluginUnload failed for plugin '%s'", plugin->name);
+ }
+
+ ArrayList_Free(plugins_list);
+ plugins_list = NULL;
}
- return TRUE;
+ if (handles_list)
+ {
+ HANDLE handle;
-error:
- pf_modules_module_free(module);
- return FALSE;
-}
\ No newline at end of file
+ ArrayList_ForEach(handles_list, HANDLE, index, handle)
+ {
+ if (handle)
+ FreeLibrary(handle);
+ };
+
+ ArrayList_Free(handles_list);
+ handles_list = NULL;
+ }
+}
#include "modules/modules_api.h"
-typedef wArrayList modules_list;
-typedef struct proxy_module proxyModule;
-
typedef enum _PF_FILTER_TYPE PF_FILTER_TYPE;
enum _PF_FILTER_TYPE
{
FILTER_TYPE_KEYBOARD,
- FILTER_TYPE_MOUSE
+ FILTER_TYPE_MOUSE,
+
+ FILTER_LAST
};
typedef enum _PF_HOOK_TYPE PF_HOOK_TYPE;
enum _PF_HOOK_TYPE
{
HOOK_TYPE_CLIENT_PRE_CONNECT,
+ HOOK_TYPE_CLIENT_LOGIN_FAILURE,
+
+ HOOK_TYPE_SERVER_POST_CONNECT,
HOOK_TYPE_SERVER_CHANNELS_INIT,
HOOK_TYPE_SERVER_CHANNELS_FREE,
-};
-
-struct proxy_module
-{
- /* Handle to the loaded library. Used for freeing the library */
- HMODULE handle;
- char* name;
- BOOL enabled;
- moduleOperations* ops;
+ HOOK_LAST
};
-BOOL pf_modules_init(void);
-BOOL pf_modules_register_new(const char* module_path, const char* module_name);
+BOOL pf_modules_init(const char* modules_directory_path);
+BOOL pf_modules_is_plugin_loaded(const char* plugin_name);
+void pf_modules_list_loaded_plugins(void);
-BOOL pf_modules_run_filter(PF_FILTER_TYPE type, rdpContext* server, void* param);
-BOOL pf_modules_run_hook(PF_HOOK_TYPE type, rdpContext* context);
+BOOL pf_modules_run_filter(PF_FILTER_TYPE type, proxyData* pdata, void* param);
+BOOL pf_modules_run_hook(PF_HOOK_TYPE type, proxyData* pdata);
void pf_modules_free(void);
#include "pf_disp.h"
#include "pf_rail.h"
#include "pf_channels.h"
+#include "pf_modules.h"
#define TAG PROXY_TAG("server")
pf_server_register_input_callbacks(peer->input);
pf_server_register_update_callbacks(peer->update);
- return TRUE;
+ return pf_modules_run_hook(HOOK_TYPE_SERVER_POST_CONNECT, pdata);
}
static BOOL pf_server_activate(freerdp_peer* peer)