#
# Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
# Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
-#
+# Copyright 2019 Idan Freiberg <speidy@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
pf_config.h
pf_graphics.c
pf_graphics.h
- pf_filters.c
- pf_filters.h
- pf_log.h
+ pf_modules.c
+ pf_modules.h
pf_cliprdr.c
- pf_cliprdr.h)
+ pf_cliprdr.h
+ pf_log.h
+ )
# On windows create dll version information.
# Vendor, product and year are already set in top level CMakeLists.txt
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Server/proxy")
-add_subdirectory("filters")
+add_subdirectory("modules")
TextOnly = 1
MaxTextLength = 10 # 0 for no limit.
-[Filters]
-; FilterName = FilterPath
-DemoFilter = "server/proxy/filters/libdemo_filter.so"
+[Modules]
+Demo = "modules/demo/libdemo.so"
+++ /dev/null
-add_library(demo_filter SHARED
- filter_demo.c
-)
+++ /dev/null
-/**
- * FreeRDP: A Remote Desktop Protocol Implementation
- * FreeRDP Proxy Server
- *
- * Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
- * Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
- * Copyright 2019 Idan Freiberg <speidy@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.
- */
-
-#ifndef FREERDP_SERVER_PROXY_FILTERS_API_H
-#define FREERDP_SERVER_PROXY_FILTERS_API_H
-
-#include <freerdp/freerdp.h>
-#include <winpr/winpr.h>
-
-#define PROXY_API FREERDP_API
-
-enum pf_filter_result {
- FILTER_PASS = 0,
- FILTER_DROP,
- FILTER_IGNORE
-};
-
-typedef enum pf_filter_result PF_FILTER_RESULT;
-typedef struct connection_info connectionInfo;
-typedef struct proxy_events proxyEvents;
-typedef struct proxy_keyboard_event_info proxyKeyboardEventInfo;
-typedef struct proxy_mouse_event_info proxyMouseEventInfo;
-typedef PF_FILTER_RESULT(*proxyEvent)(connectionInfo* info, void* param);
-
-struct connection_info {
- char* TargetHostname;
- char* ClientHostname;
- char* Username;
-};
-
-struct proxy_events {
- proxyEvent KeyboardEvent;
- proxyEvent MouseEvent;
-};
-
-#pragma pack(push, 1)
-struct proxy_keyboard_event_info {
- UINT16 flags;
- UINT16 rdp_scan_code;
-};
-
-struct proxy_mouse_event_info {
- UINT16 flags;
- UINT16 x;
- UINT16 y;
-};
-#pragma pack(pop)
-
-/* implement this method and register callbacks for proxy events
- * return TRUE if initialization succeeded, otherwise FALSE.
- **/
-PROXY_API BOOL filter_init(proxyEvents* events);
-
-#endif /* FREERDP_SERVER_PROXY_FILTERS_API_H */
#include "pf_server.h"
#include "pf_config.h"
#include "pf_log.h"
-#include "pf_filters.h"
+#include "pf_modules.h"
#include <winpr/collections.h>
#define TAG PROXY_TAG("server")
+
int main(int argc, char* argv[])
{
const char* cfg = "config.ini";
if (argc > 1)
cfg = argv[1];
+ if (!pf_modules_init())
+ return FALSE;
+
if (!pf_server_config_load(cfg, config))
goto fail;
pf_server_config_print(config);
status = pf_server_start(config);
fail:
+ pf_modules_free();
pf_server_config_free(config);
return status;
}
--- /dev/null
+# 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.
+
+# The third-party directory is meant for third-party components to be built
+# as part of the main FreeRDP build system, making separate maintenance easier.
+# Subdirectories of the third-party directory are ignored by git, but are
+# automatically included by CMake when the -DWITH_THIRD_PARTY=on option is used.
+
+# include proxy header files for proxy modules
+include_directories("${CMAKE_SOURCE_DIR}/server/proxy")
+include_directories("${CMAKE_SOURCE_DIR}/server/proxy/modules")
+
+# taken from FreeRDP/third-party/CMakeLists.txt
+file(GLOB all_valid_subdirs RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*/CMakeLists.txt")
+
+foreach(dir ${all_valid_subdirs})
+ if(${dir} MATCHES "^([^/]*)/+CMakeLists.txt")
+ string(REGEX REPLACE "^([^/]*)/+CMakeLists.txt" "\\1" dir_trimmed ${dir})
+ message(STATUS "Adding proxy module ${dir_trimmed}")
+ add_subdirectory(${dir_trimmed})
+ endif()
+endforeach(dir)
--- /dev/null
+
+add_library(demo SHARED
+ demo.c
+)
#include <stdio.h>
-#include "filters_api.h"
+#include "modules_api.h"
-static PF_FILTER_RESULT demo_filter_keyboard_event(connectionInfo* info, void* param)
+static BOOL demo_filter_keyboard_event(moduleOperations* module, rdpContext* context, void* param)
{
proxyKeyboardEventInfo* event_data = (proxyKeyboardEventInfo*) param;
WINPR_UNUSED(event_data);
- return FILTER_PASS;
+ return TRUE;
}
-static PF_FILTER_RESULT demo_filter_mouse_event(connectionInfo* info, void* param)
+static BOOL demo_filter_mouse_event(moduleOperations* module, rdpContext* context, void* param)
{
proxyMouseEventInfo* event_data = (proxyMouseEventInfo*) param;
if (event_data->x % 100 == 0)
{
+ module->AbortConnect(module, context);
printf("filter_demo: mouse x is currently %"PRIu16"\n", event_data->x);
- return FILTER_PASS;
}
- return FILTER_PASS;
+ return TRUE;
+}
+
+BOOL module_init(moduleOperations* module)
+{
+ module->KeyboardEvent = demo_filter_keyboard_event;
+ module->MouseEvent = demo_filter_mouse_event;
+
+ return TRUE;
}
-BOOL filter_init(proxyEvents* events)
+BOOL module_exit(moduleOperations* module)
{
- events->KeyboardEvent = demo_filter_keyboard_event;
- events->MouseEvent = demo_filter_mouse_event;
+ printf("bye bye\n");
return TRUE;
}
--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * FreeRDP Proxy Server
+ *
+ * Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
+ * Copyright 2019 Idan Freiberg <speidy@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.
+ */
+
+#ifndef FREERDP_SERVER_PROXY_MODULES_API_H
+#define FREERDP_SERVER_PROXY_MODULES_API_H
+
+#include <freerdp/freerdp.h>
+#include <winpr/winpr.h>
+
+#define PROXY_API FREERDP_API
+
+typedef struct module_operations moduleOperations;
+
+/* 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*);
+
+/*
+ * used for connection management. when a module wants to forcibly close a connection, it should
+ * call this method.
+ */
+typedef void (*moduleAbortConnect)(moduleOperations*, rdpContext*);
+
+typedef struct connection_info connectionInfo;
+typedef struct proxy_keyboard_event_info proxyKeyboardEventInfo;
+typedef struct proxy_mouse_event_info proxyMouseEventInfo;
+
+/* represents a set of operations that a module can do */
+struct module_operations
+{
+ /* per-session API. a module must not change these function pointers. */
+ moduleSetSessionData SetSessionData;
+ moduleGetSessionData GetSessionData;
+ moduleAbortConnect AbortConnect;
+
+ /* proxy hooks. a module can set these function pointers to register hooks. */
+ proxyHookFn ClientPreConnect;
+ proxyHookFn ServerChannelsInit;
+ proxyHookFn ServerChannelsFree;
+
+ /* proxy filters a module can set these function pointers to register filters. */
+ proxyFilterFn KeyboardEvent;
+ proxyFilterFn MouseEvent;
+};
+
+/* filter events parameters */
+#define WINPR_PACK_PUSH
+#include <winpr/pack.h>
+struct proxy_keyboard_event_info
+{
+ UINT16 flags;
+ UINT16 rdp_scan_code;
+};
+
+struct proxy_mouse_event_info
+{
+ UINT16 flags;
+ UINT16 x;
+ UINT16 y;
+};
+#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);
+
+#endif /* FREERDP_SERVER_PROXY_MODULES_API_H */
pc = (pClientContext*) context;
ps = (rdpContext*) pc->pdata->ps;
- if (!proxy_data_set_connection_info(pc->pdata, ps->settings, settings))
- {
- WLog_ERR(TAG, "proxy_data_set_connection_info failed!");
- return FALSE;
- }
-
if (!gdi_init(instance, PIXEL_FORMAT_XRGB32))
return FALSE;
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Proxy Server
*
- * Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
*
#include "pf_log.h"
#include "pf_server.h"
#include "pf_config.h"
+#include "pf_modules.h"
#define TAG PROXY_TAG("config")
return TRUE;
}
-static BOOL pf_config_load_filters(wIniFile* ini, proxyConfig* config)
+static BOOL pf_config_load_modules(wIniFile* ini, proxyConfig* config)
{
UINT32 index;
- int filters_count;
- char** filters_names;
+ int modules_count = 0;
+ char** module_names;
- if (!pf_filters_init(&config->Filters))
- return FALSE;
-
- filters_names = IniFile_GetSectionKeyNames(ini, "Filters", &filters_count);
+ module_names = IniFile_GetSectionKeyNames(ini, "Modules", &modules_count);
- for (index = 0; index < filters_count; index++)
+ for (index = 0; index < modules_count; index++)
{
- char* filter_name = filters_names[index];
- const char* path = CONFIG_GET_STR(ini, "Filters", filter_name);
+ char* module_name = module_names[index];
+ const char* path = CONFIG_GET_STR(ini, "Modules", module_name);
- if (!pf_filters_register_new(config->Filters, path, filter_name))
+ if (!pf_modules_register_new(path, module_name))
{
- WLog_DBG(TAG, "pf_config_load_filters(): failed to register %s (%s)", filter_name, path);
+ WLog_DBG(TAG, "pf_config_load_modules(): failed to register %s (%s)", module_name, path);
continue;
}
- WLog_DBG(TAG, "pf_config_load_filters(): filter %s is registered", filter_name);
+ WLog_INFO(TAG, "module '%s' is loaded!", module_name);
}
return TRUE;
if (!pf_config_load_security(ini, config))
goto out;
- if (!pf_config_load_filters(ini, config))
+ if (!pf_config_load_modules(ini, config))
goto out;
if (!pf_config_load_clipboard(ini, config))
void pf_server_config_free(proxyConfig* config)
{
- pf_filters_unregister_all(config->Filters);
free(config->TargetHost);
free(config->Host);
free(config);
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Proxy Server
*
- * Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
*
#include <winpr/ini.h>
-#include "pf_filters.h"
+#define CONFIG_GET_STR(ini, section, key) IniFile_GetKeyValueString(ini, section, key)
+#define CONFIG_GET_BOOL(ini, section, key) IniFile_GetKeyValueInt(ini, section, key)
+
+typedef struct proxy_config proxyConfig;
struct proxy_config
{
BOOL DisplayControl;
BOOL Clipboard;
- /* filters */
- filters_list* Filters;
-
/* clipboard specific settings*/
BOOL TextOnly;
UINT32 MaxTextLength;
static BOOL client_to_proxy_context_new(freerdp_peer* client,
pServerContext* context)
{
+ context->modules_info = HashTable_New(TRUE);
+ if (!context->modules_info)
+ return FALSE;
+
context->vcm = WTSOpenServerA((LPSTR) client->context);
if (!context->vcm || context->vcm == INVALID_HANDLE_VALUE)
return TRUE;
fail_open_server:
+ HashTable_Free(context->modules_info);
context->vcm = NULL;
return FALSE;
}
CloseHandle(context->dynvcReady);
context->dynvcReady = NULL;
}
+
+ HashTable_Free(context->modules_info);
}
BOOL init_p_server_context(freerdp_peer* client)
return NULL;
}
-static void connection_info_free(connectionInfo* info)
-{
- free(info->TargetHostname);
- free(info->ClientHostname);
- free(info->Username);
- free(info);
-}
-
-proxyData* proxy_data_new()
+proxyData* proxy_data_new(void)
{
proxyData* pdata = calloc(1, sizeof(proxyData));
return NULL;
}
- pdata->info = calloc(1, sizeof(connectionInfo));
-
- if (pdata->info == NULL)
- {
- free(pdata);
- return NULL;
- }
-
if (!(pdata->abort_event = CreateEvent(NULL, TRUE, FALSE, NULL)))
{
proxy_data_free(pdata);
return pdata;
}
-/* sets connection info values using the settings of both server & client */
-BOOL proxy_data_set_connection_info(proxyData* pdata, rdpSettings* ps, rdpSettings* pc)
-{
- if (!(pdata->info->TargetHostname = _strdup(pc->ServerHostname)))
- goto out_fail;
-
- if (!(pdata->info->Username = _strdup(pc->Username)))
- goto out_fail;
-
- if (!(pdata->info->ClientHostname = _strdup(ps->ClientHostname)))
- goto out_fail;
-
- return TRUE;
-out_fail:
- proxy_data_free(pdata);
- return FALSE;
-}
-
void proxy_data_free(proxyData* pdata)
{
- connection_info_free(pdata->info);
if (pdata->abort_event)
{
CloseHandle(pdata->abort_event);
#include "pf_config.h"
#include "pf_server.h"
-#include "pf_filters.h"
+#include "pf_modules.h"
typedef struct proxy_data proxyData;
RdpgfxServerContext* gfx;
DispServerContext* disp;
CliprdrServerContext* cliprdr;
+
+ /* used to external modules to store per-session info */
+ wHashTable* modules_info;
};
typedef struct p_server_context pServerContext;
HANDLE abort_event;
HANDLE client_thread;
-
- connectionInfo* info;
- filters_list* filters;
};
/* client */
rdpContext* p_client_context_create(rdpSettings* clientSettings);
/* pdata */
-proxyData* proxy_data_new();
-BOOL proxy_data_set_connection_info(proxyData* pdata, rdpSettings* ps, rdpSettings* pc);
+proxyData* proxy_data_new(void);
void proxy_data_free(proxyData* pdata);
+
BOOL pf_context_copy_settings(rdpSettings* dst, const rdpSettings* src, BOOL is_dst_server);
void proxy_data_abort_connect(proxyData* pdata);
BOOL proxy_data_shall_disconnect(proxyData* pdata);
+++ /dev/null
-/**
- * FreeRDP: A Remote Desktop Protocol Implementation
- * FreeRDP Proxy Server
- *
- * Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
- * Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
- * Copyright 2019 Idan Freiberg <speidy@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 <assert.h>
-
-#include <winpr/wlog.h>
-#include <winpr/library.h>
-#include <freerdp/api.h>
-
-#include "pf_log.h"
-#include "pf_filters.h"
-
-#define TAG PROXY_TAG("filters")
-#define FILTER_INIT_METHOD "filter_init"
-
-static const char* FILTER_RESULT_STRINGS[] =
-{
- "FILTER_PASS",
- "FILTER_DROP",
- "FILTER_IGNORE",
-};
-
-static const char* EVENT_TYPE_STRINGS[] =
-{
- "KEYBOARD_EVENT",
- "MOUSE_EVENT",
-};
-
-static const char* pf_filters_get_filter_result_string(PF_FILTER_RESULT result)
-{
- if (result >= FILTER_PASS && result <= FILTER_IGNORE)
- return FILTER_RESULT_STRINGS[result];
- else
- return "FILTER_UNKNOWN";
-}
-
-static const char* pf_filters_get_event_type_string(PF_FILTER_TYPE result)
-{
- if (result >= FILTER_TYPE_KEYBOARD && result <= FILTER_TYPE_MOUSE)
- return EVENT_TYPE_STRINGS[result];
- else
- return "EVENT_UNKNOWN";
-}
-
-BOOL pf_filters_init(filters_list** list)
-{
- if (list == NULL)
- {
- WLog_ERR(TAG, "pf_filters_init(): list == NULL");
- return FALSE;
- }
-
- *list = ArrayList_New(FALSE);
-
- if (*list == NULL)
- {
- WLog_ERR(TAG, "pf_filters_init(): ArrayList_New failed!");
- return FALSE;
- }
-
- return TRUE;
-}
-
-PF_FILTER_RESULT pf_filters_run_by_type(filters_list* list, PF_FILTER_TYPE type,
- connectionInfo* info,
- void* param)
-{
- proxyFilter* filter;
- proxyEvents* events;
- PF_FILTER_RESULT result = FILTER_PASS;
- const size_t count = (size_t) ArrayList_Count(list);
- size_t index;
-
- for (index = 0; index < count; index++)
- {
- filter = (proxyFilter*) ArrayList_GetItem(list, index);
- events = filter->events;
- WLog_VRB(TAG, "pf_filters_run_by_type(): Running filter: %s", filter->name);
-
- switch (type)
- {
- case FILTER_TYPE_KEYBOARD:
- IFCALLRET(events->KeyboardEvent, result, info, param);
- break;
-
- case FILTER_TYPE_MOUSE:
- IFCALLRET(events->MouseEvent, result, info, param);
- break;
- }
-
- if (result != FILTER_PASS)
- {
- /* Filter returned FILTER_DROP or FILTER_IGNORE. There's no need to call next filters. */
- WLog_INFO(TAG, "Filter %s [%s] returned %s", filter->name,
- pf_filters_get_event_type_string(type), pf_filters_get_filter_result_string(result));
- return result;
- }
- }
-
- /* all filters returned FILTER_PASS */
- return FILTER_PASS;
-}
-
-static void pf_filters_filter_free(proxyFilter* filter)
-{
- if (!filter)
- return;
-
- if (filter->handle)
- FreeLibrary(filter->handle);
-
- free(filter->name);
- free(filter->events);
- free(filter);
-}
-
-void pf_filters_unregister_all(filters_list* list)
-{
- size_t count;
- size_t index;
-
- if (list == NULL)
- return;
-
- count = (size_t) ArrayList_Count(list);
-
- for (index = 0; index < count; index++)
- {
- proxyFilter* filter = (proxyFilter*) ArrayList_GetItem(list, index);
- WLog_DBG(TAG, "pf_filters_unregister_all(): freeing filter: %s", filter->name);
- pf_filters_filter_free(filter);
- }
-
- ArrayList_Free(list);
-}
-
-BOOL pf_filters_register_new(filters_list* list, const char* module_path, const char* filter_name)
-{
- proxyEvents* events = NULL;
- proxyFilter* filter = NULL;
- HMODULE handle = NULL;
- filterInitFn fn;
-
- assert(list != NULL);
- handle = LoadLibraryA(module_path);
-
- if (handle == NULL)
- {
- WLog_ERR(TAG, "pf_filters_register_new(): failed loading external module: %s", module_path);
- return FALSE;
- }
-
- if (!(fn = (filterInitFn) GetProcAddress(handle, FILTER_INIT_METHOD)))
- {
- WLog_ERR(TAG, "pf_filters_register_new(): GetProcAddress failed while loading %s", module_path);
- goto error;
- }
-
- filter = (proxyFilter*) malloc(sizeof(proxyFilter));
-
- if (filter == NULL)
- {
- WLog_ERR(TAG, "pf_filters_register_new(): malloc failed");
- goto error;
- }
-
- events = calloc(1, sizeof(proxyEvents));
-
- if (events == NULL)
- {
- WLog_ERR(TAG, "pf_filters_register_new(): calloc proxyEvents failed");
- goto error;
- }
-
- if (!fn(events))
- {
- WLog_ERR(TAG, "pf_filters_register_new(): failed calling external filter_init: %s", module_path);
- goto error;
- }
-
- filter->handle = handle;
- filter->name = _strdup(filter_name);
- filter->events = events;
- filter->enabled = TRUE;
-
- if (ArrayList_Add(list, filter) < 0)
- {
- WLog_ERR(TAG, "pf_filters_register_new(): failed adding filter to list: %s", module_path);
- goto error;
- }
-
- return TRUE;
-error:
-
- if (handle)
- FreeLibrary(handle);
-
- pf_filters_filter_free(filter);
- return FALSE;
-}
+++ /dev/null
-/**
- * FreeRDP: A Remote Desktop Protocol Implementation
- * FreeRDP Proxy Server
- *
- * Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
- * Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
- * Copyright 2019 Idan Freiberg <speidy@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.
- */
-
-#ifndef FREERDP_SERVER_PROXY_FILTERS_H
-#define FREERDP_SERVER_PROXY_FILTERS_H
-
-#include <winpr/wtypes.h>
-#include <winpr/collections.h>
-
-#include "filters/filters_api.h"
-
-/* filter init method */
-typedef BOOL (*filterInitFn)(proxyEvents* events);
-
-typedef wArrayList filters_list;
-typedef struct proxy_filter proxyFilter;
-
-typedef enum _PF_FILTER_TYPE PF_FILTER_TYPE;
-enum _PF_FILTER_TYPE
-{
- FILTER_TYPE_KEYBOARD,
- FILTER_TYPE_MOUSE
-};
-
-struct proxy_filter
-{
- /* Handle to the loaded library. Used for freeing the library */
- HMODULE handle;
-
- char* name;
- BOOL enabled;
- proxyEvents* events;
-};
-
-BOOL pf_filters_init(filters_list** list);
-BOOL pf_filters_register_new(filters_list* list, const char* module_path, const char* filter_name);
-PF_FILTER_RESULT pf_filters_run_by_type(filters_list* list, PF_FILTER_TYPE type,
- connectionInfo* info,
- void* param);
-void pf_filters_unregister_all(filters_list* list);
-
-#define RUN_FILTER(_filters,_type,_conn_info,_event_info,_ret,_cb,...) do { \
- switch(pf_filters_run_by_type(_filters,_type,_conn_info,_event_info)) { \
- case FILTER_PASS: \
- _ret = _cb(__VA_ARGS__); \
- break; \
- case FILTER_IGNORE: \
- _ret = TRUE; \
- break; \
- case FILTER_DROP: \
- default: \
- _ret = FALSE; \
- } \
- } while(0)
-
-#endif /* FREERDP_SERVER_PROXY_FILTERS_H */
--- /dev/null
+/**
+ * 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>
+ *
+ * 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 <assert.h>
+
+#include <winpr/wlog.h>
+#include <winpr/library.h>
+#include <freerdp/api.h>
+
+#include "pf_log.h"
+#include "pf_modules.h"
+#include "pf_context.h"
+
+#define TAG PROXY_TAG("modules")
+
+#define MODULE_INIT_METHOD "module_init"
+#define MODULE_EXIT_METHOD "module_exit"
+
+static modules_list* proxy_modules = NULL;
+
+/* module init/exit methods */
+typedef BOOL (*moduleInitFn)(moduleOperations* ops);
+typedef BOOL (*moduleExitFn)(moduleOperations* ops);
+
+static const char* FILTER_TYPE_STRINGS[] = {
+ "KEYBOARD_EVENT",
+ "MOUSE_EVENT",
+};
+
+static const char* HOOK_TYPE_STRINGS[] = {
+ "CLIENT_PRE_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)
+ 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)
+ 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)
+{
+
+ proxyModule* module;
+ moduleOperations* ops;
+ BOOL ok = TRUE;
+ const size_t count = (size_t)ArrayList_Count(proxy_modules);
+ size_t index;
+
+ for (index = 0; index < count; index++)
+ {
+ 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));
+
+ switch (type)
+ {
+ case HOOK_TYPE_CLIENT_PRE_CONNECT:
+ IFCALLRET(ops->ClientPreConnect, ok, ops, context);
+ break;
+
+ case HOOK_TYPE_SERVER_CHANNELS_INIT:
+ IFCALLRET(ops->ServerChannelsInit, ok, ops, context);
+ break;
+
+ case HOOK_TYPE_SERVER_CHANNELS_FREE:
+ IFCALLRET(ops->ServerChannelsFree, ok, ops, context);
+ break;
+ }
+
+ if (!ok)
+ {
+ WLog_INFO(TAG, "Module %s, hook %s failed!", module->name,
+ pf_modules_get_hook_type_string(type));
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+/*
+ * 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(PF_FILTER_TYPE type, rdpContext* server, void* param)
+{
+ proxyModule* module;
+ moduleOperations* ops;
+ BOOL result = TRUE;
+ const size_t count = (size_t)ArrayList_Count(proxy_modules);
+ size_t index;
+
+ for (index = 0; index < count; index++)
+ {
+ module = (proxyModule*)ArrayList_GetItem(proxy_modules, index);
+ ops = module->ops;
+ WLog_VRB(TAG, "[%s]: running filter: %s", __FUNCTION__, module->name);
+
+ switch (type)
+ {
+ case FILTER_TYPE_KEYBOARD:
+ IFCALLRET(ops->KeyboardEvent, result, ops, server, param);
+ break;
+
+ case FILTER_TYPE_MOUSE:
+ IFCALLRET(ops->MouseEvent, result, ops, server, param);
+ break;
+ }
+
+ if (!result)
+ {
+ /* current filter return FALSE, no need to run other filters. */
+ WLog_INFO(TAG, "module %s, filter type [%s] returned FALSE", module->name,
+ pf_modules_get_filter_type_string(type));
+ return result;
+ }
+ }
+
+ /* all filters returned TRUE */
+ return TRUE;
+}
+
+static void pf_modules_module_free(proxyModule* module)
+{
+ moduleExitFn exitFn;
+
+ assert(module);
+ assert(module->handle);
+
+ exitFn = (moduleExitFn)GetProcAddress(module->handle, MODULE_EXIT_METHOD);
+
+ if (!exitFn)
+ {
+ 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);
+ }
+ }
+
+ FreeLibrary(module->handle);
+ module->handle = NULL;
+
+ free(module->name);
+ free(module->ops);
+ free(module);
+}
+
+void pf_modules_free(void)
+{
+ size_t index, count;
+
+ 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);
+ }
+
+ ArrayList_Free(proxy_modules);
+}
+
+/*
+ * 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)
+{
+ pServerContext* ps;
+
+ assert(module);
+ assert(context);
+
+ if (data == NULL) /* no need to store anything */
+ return FALSE;
+
+ ps = (pServerContext*) context;
+ if (HashTable_Add(ps->modules_info, (void*)module, data) < 0)
+ {
+ WLog_ERR(TAG, "[%s]: HashTable_Add failed!");
+ 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)
+{
+ pServerContext* ps;
+
+ assert(module);
+ assert(context);
+
+ ps = (pServerContext*)context;
+ return HashTable_GetItemValue(ps->modules_info, module);
+}
+
+static void pf_modules_abort_connect(moduleOperations* module, rdpContext* context)
+{
+ pServerContext* ps;
+
+ assert(module);
+ assert(context);
+
+ WLog_INFO(TAG, "%s is called!", __FUNCTION__);
+
+ ps = (pServerContext*)context;
+ proxy_data_abort_connect(ps->pdata);
+}
+
+BOOL pf_modules_register_new(const char* module_path, const char* module_name)
+{
+ moduleOperations* ops = NULL;
+ proxyModule* module = NULL;
+ HMODULE handle = NULL;
+ moduleInitFn fn;
+
+ assert(proxy_modules != NULL);
+ handle = LoadLibraryA(module_path);
+
+ if (handle == NULL)
+ {
+ WLog_ERR(TAG, "pf_modules_register_new(): failed loading external module: %s", module_path);
+ return FALSE;
+ }
+
+ if (!(fn = (moduleInitFn)GetProcAddress(handle, MODULE_INIT_METHOD)))
+ {
+ WLog_ERR(TAG, "pf_modules_register_new(): GetProcAddress failed while loading %s",
+ module_path);
+ goto error;
+ }
+
+ module = (proxyModule*)calloc(1, sizeof(proxyModule));
+
+ if (module == NULL)
+ {
+ WLog_ERR(TAG, "pf_modules_register_new(): malloc failed");
+ goto error;
+ }
+
+ ops = calloc(1, sizeof(moduleOperations));
+
+ if (ops == NULL)
+ {
+ WLog_ERR(TAG, "pf_modules_register_new(): calloc moduleOperations failed");
+ goto error;
+ }
+
+ ops->AbortConnect = pf_modules_abort_connect;
+ ops->SetSessionData = pf_modules_set_session_data;
+ ops->GetSessionData = pf_modules_get_session_data;
+
+ if (!fn(ops))
+ {
+ WLog_ERR(TAG, "pf_modules_register_new(): failed to initialize module %s", module_path);
+ goto error;
+ }
+
+ module->name = _strdup(module_name);
+ if (!module->name)
+ {
+ WLog_ERR(TAG, "pf_modules_register_new(): _strdup failed while loading %s", module_path);
+ goto error;
+ }
+
+ module->handle = handle;
+ module->ops = ops;
+ module->enabled = TRUE;
+
+ if (ArrayList_Add(proxy_modules, module) < 0)
+ {
+ WLog_ERR(TAG, "pf_modules_register_new(): failed adding module to list: %s", module_path);
+ goto error;
+ }
+
+ return TRUE;
+
+error:
+ pf_modules_module_free(module);
+ return FALSE;
+}
\ No newline at end of file
--- /dev/null
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * FreeRDP Proxy Server
+ *
+ * Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
+ * Copyright 2019 Idan Freiberg <speidy@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.
+ */
+
+#ifndef FREERDP_SERVER_PROXY_MODULES_H
+#define FREERDP_SERVER_PROXY_MODULES_H
+
+#include <winpr/wtypes.h>
+#include <winpr/collections.h>
+
+#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
+};
+
+typedef enum _PF_HOOK_TYPE PF_HOOK_TYPE;
+enum _PF_HOOK_TYPE
+{
+ HOOK_TYPE_CLIENT_PRE_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;
+};
+
+BOOL pf_modules_init(void);
+BOOL pf_modules_register_new(const char* module_path, const char* module_name);
+
+BOOL pf_modules_run_filter(PF_FILTER_TYPE type, rdpContext* server, void* param);
+BOOL pf_modules_run_hook(PF_HOOK_TYPE type, rdpContext* context);
+
+void pf_modules_free(void);
+
+#endif /* FREERDP_SERVER_PROXY_MODULES_H */