diff --git a/server/proxy/CMakeLists.txt b/server/proxy/CMakeLists.txt index 557cdef1a..b8ccd5bed 100644 --- a/server/proxy/CMakeLists.txt +++ b/server/proxy/CMakeLists.txt @@ -3,7 +3,8 @@ # # Copyright 2019 Mati Shabtay # Copyright 2019 Kobi Mizrachi -# +# Copyright 2019 Idan Freiberg + # 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 @@ -43,11 +44,12 @@ set(${MODULE_PREFIX}_SRCS 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 @@ -79,4 +81,4 @@ endif() set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Server/proxy") -add_subdirectory("filters") +add_subdirectory("modules") diff --git a/server/proxy/config.ini b/server/proxy/config.ini index 4ac80a776..39b1bfdb9 100644 --- a/server/proxy/config.ini +++ b/server/proxy/config.ini @@ -32,6 +32,5 @@ Clipboard = 1 TextOnly = 1 MaxTextLength = 10 # 0 for no limit. -[Filters] -; FilterName = FilterPath -DemoFilter = "server/proxy/filters/libdemo_filter.so" +[Modules] +Demo = "modules/demo/libdemo.so" diff --git a/server/proxy/filters/CMakeLists.txt b/server/proxy/filters/CMakeLists.txt deleted file mode 100644 index 43f753469..000000000 --- a/server/proxy/filters/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -add_library(demo_filter SHARED - filter_demo.c -) diff --git a/server/proxy/filters/filters_api.h b/server/proxy/filters/filters_api.h deleted file mode 100644 index 047bbecb5..000000000 --- a/server/proxy/filters/filters_api.h +++ /dev/null @@ -1,72 +0,0 @@ -/** - * FreeRDP: A Remote Desktop Protocol Implementation - * FreeRDP Proxy Server - * - * Copyright 2019 Mati Shabtay - * Copyright 2019 Kobi Mizrachi - * Copyright 2019 Idan Freiberg - * - * 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 -#include - -#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 */ diff --git a/server/proxy/freerdp_proxy.c b/server/proxy/freerdp_proxy.c index 97e714db2..a5b36ab10 100644 --- a/server/proxy/freerdp_proxy.c +++ b/server/proxy/freerdp_proxy.c @@ -22,12 +22,13 @@ #include "pf_server.h" #include "pf_config.h" #include "pf_log.h" -#include "pf_filters.h" +#include "pf_modules.h" #include #define TAG PROXY_TAG("server") + int main(int argc, char* argv[]) { const char* cfg = "config.ini"; @@ -40,12 +41,16 @@ int main(int argc, char* argv[]) 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; } diff --git a/server/proxy/modules/CMakeLists.txt b/server/proxy/modules/CMakeLists.txt new file mode 100644 index 000000000..ed8ec43df --- /dev/null +++ b/server/proxy/modules/CMakeLists.txt @@ -0,0 +1,33 @@ +# Copyright 2019 Kobi Mizrachi +# +# 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) diff --git a/server/proxy/filters/README.md b/server/proxy/modules/README.md similarity index 100% rename from server/proxy/filters/README.md rename to server/proxy/modules/README.md diff --git a/server/proxy/modules/demo/CMakeLists.txt b/server/proxy/modules/demo/CMakeLists.txt new file mode 100644 index 000000000..849461e5a --- /dev/null +++ b/server/proxy/modules/demo/CMakeLists.txt @@ -0,0 +1,4 @@ + +add_library(demo SHARED + demo.c +) diff --git a/server/proxy/filters/filter_demo.c b/server/proxy/modules/demo/demo.c similarity index 66% rename from server/proxy/filters/filter_demo.c rename to server/proxy/modules/demo/demo.c index e2018adff..4a147a5be 100644 --- a/server/proxy/filters/filter_demo.c +++ b/server/proxy/modules/demo/demo.c @@ -19,33 +19,40 @@ #include -#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; -} - -BOOL filter_init(proxyEvents* events) -{ - events->KeyboardEvent = demo_filter_keyboard_event; - events->MouseEvent = demo_filter_mouse_event; - + 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; } diff --git a/server/proxy/modules/modules_api.h b/server/proxy/modules/modules_api.h new file mode 100644 index 000000000..375956297 --- /dev/null +++ b/server/proxy/modules/modules_api.h @@ -0,0 +1,101 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * FreeRDP Proxy Server + * + * Copyright 2019 Kobi Mizrachi + * Copyright 2019 Idan Freiberg + * + * 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 +#include + +#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 +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 + +/* + * 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 */ diff --git a/server/proxy/pf_client.c b/server/proxy/pf_client.c index 97b744f76..f4653112c 100644 --- a/server/proxy/pf_client.c +++ b/server/proxy/pf_client.c @@ -168,12 +168,6 @@ static BOOL pf_client_post_connect(freerdp* instance) 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; diff --git a/server/proxy/pf_config.c b/server/proxy/pf_config.c index 82db1fa50..2de2f21e5 100644 --- a/server/proxy/pf_config.c +++ b/server/proxy/pf_config.c @@ -2,7 +2,6 @@ * FreeRDP: A Remote Desktop Protocol Implementation * FreeRDP Proxy Server * - * Copyright 2019 Mati Shabtay * Copyright 2019 Kobi Mizrachi * Copyright 2019 Idan Freiberg * @@ -27,6 +26,7 @@ #include "pf_log.h" #include "pf_server.h" #include "pf_config.h" +#include "pf_modules.h" #define TAG PROXY_TAG("config") @@ -125,29 +125,26 @@ static BOOL pf_config_load_clipboard(wIniFile* ini, proxyConfig* 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; + module_names = IniFile_GetSectionKeyNames(ini, "Modules", &modules_count); - filters_names = IniFile_GetSectionKeyNames(ini, "Filters", &filters_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; @@ -185,7 +182,7 @@ BOOL pf_server_config_load(const char* path, proxyConfig* config) 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)) @@ -234,7 +231,6 @@ void pf_server_config_print(proxyConfig* config) void pf_server_config_free(proxyConfig* config) { - pf_filters_unregister_all(config->Filters); free(config->TargetHost); free(config->Host); free(config); diff --git a/server/proxy/pf_config.h b/server/proxy/pf_config.h index e7026dbf4..44c523293 100644 --- a/server/proxy/pf_config.h +++ b/server/proxy/pf_config.h @@ -2,7 +2,6 @@ * FreeRDP: A Remote Desktop Protocol Implementation * FreeRDP Proxy Server * - * Copyright 2019 Mati Shabtay * Copyright 2019 Kobi Mizrachi * Copyright 2019 Idan Freiberg * @@ -24,7 +23,10 @@ #include -#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 { @@ -52,9 +54,6 @@ struct proxy_config BOOL DisplayControl; BOOL Clipboard; - /* filters */ - filters_list* Filters; - /* clipboard specific settings*/ BOOL TextOnly; UINT32 MaxTextLength; diff --git a/server/proxy/pf_context.c b/server/proxy/pf_context.c index 1f4a42e79..361b1f152 100644 --- a/server/proxy/pf_context.c +++ b/server/proxy/pf_context.c @@ -26,6 +26,10 @@ 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) @@ -33,6 +37,7 @@ static BOOL client_to_proxy_context_new(freerdp_peer* client, return TRUE; fail_open_server: + HashTable_Free(context->modules_info); context->vcm = NULL; return FALSE; } @@ -53,6 +58,8 @@ static void client_to_proxy_context_free(freerdp_peer* client, CloseHandle(context->dynvcReady); context->dynvcReady = NULL; } + + HashTable_Free(context->modules_info); } BOOL init_p_server_context(freerdp_peer* client) @@ -141,15 +148,7 @@ error: 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)); @@ -158,14 +157,6 @@ proxyData* proxy_data_new() 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); @@ -175,27 +166,8 @@ proxyData* proxy_data_new() 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); diff --git a/server/proxy/pf_context.h b/server/proxy/pf_context.h index c6e11731a..227d2a214 100644 --- a/server/proxy/pf_context.h +++ b/server/proxy/pf_context.h @@ -33,7 +33,7 @@ #include "pf_config.h" #include "pf_server.h" -#include "pf_filters.h" +#include "pf_modules.h" typedef struct proxy_data proxyData; @@ -52,6 +52,9 @@ struct p_server_context RdpgfxServerContext* gfx; DispServerContext* disp; CliprdrServerContext* cliprdr; + + /* used to external modules to store per-session info */ + wHashTable* modules_info; }; typedef struct p_server_context pServerContext; @@ -93,18 +96,15 @@ struct proxy_data 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); diff --git a/server/proxy/pf_filters.c b/server/proxy/pf_filters.c deleted file mode 100644 index 121a839df..000000000 --- a/server/proxy/pf_filters.c +++ /dev/null @@ -1,218 +0,0 @@ -/** - * FreeRDP: A Remote Desktop Protocol Implementation - * FreeRDP Proxy Server - * - * Copyright 2019 Mati Shabtay - * Copyright 2019 Kobi Mizrachi - * Copyright 2019 Idan Freiberg - * - * 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 - -#include -#include -#include - -#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; -} diff --git a/server/proxy/pf_filters.h b/server/proxy/pf_filters.h deleted file mode 100644 index d88cf72d3..000000000 --- a/server/proxy/pf_filters.h +++ /dev/null @@ -1,74 +0,0 @@ -/** - * FreeRDP: A Remote Desktop Protocol Implementation - * FreeRDP Proxy Server - * - * Copyright 2019 Mati Shabtay - * Copyright 2019 Kobi Mizrachi - * Copyright 2019 Idan Freiberg - * - * 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 -#include - -#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 */ diff --git a/server/proxy/pf_modules.c b/server/proxy/pf_modules.c new file mode 100644 index 000000000..4a68a596a --- /dev/null +++ b/server/proxy/pf_modules.c @@ -0,0 +1,350 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * FreeRDP Proxy Server modules API + * + * Copyright 2019 Kobi Mizrachi + * Copyright 2019 Idan Freiberg + * + * 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 + +#include +#include +#include + +#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 diff --git a/server/proxy/pf_modules.h b/server/proxy/pf_modules.h new file mode 100644 index 000000000..e6b6911c4 --- /dev/null +++ b/server/proxy/pf_modules.h @@ -0,0 +1,65 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * FreeRDP Proxy Server + * + * Copyright 2019 Kobi Mizrachi + * Copyright 2019 Idan Freiberg + * + * 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 +#include + +#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 */