diff --git a/server/proxy/CMakeLists.txt b/server/proxy/CMakeLists.txt index 6d749f577..a944f005e 100644 --- a/server/proxy/CMakeLists.txt +++ b/server/proxy/CMakeLists.txt @@ -43,6 +43,8 @@ set(${MODULE_PREFIX}_SRCS pf_config.h pf_graphics.c pf_graphics.h + pf_filters.c + pf_filters.h pf_log.h) # On windows create dll version information. @@ -74,3 +76,5 @@ if (WITH_DEBUG_SYMBOLS AND MSVC) endif() set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Server/proxy") + +add_subdirectory("filters") diff --git a/server/proxy/config.ini b/server/proxy/config.ini index 3a04dc6d1..fe29861e4 100644 --- a/server/proxy/config.ini +++ b/server/proxy/config.ini @@ -31,3 +31,7 @@ RdpSecurity = 1 WhitelistMode = 0 AllowedChannels = "cliprdr,Microsoft::Windows::RDS::Video::Control" DeniedChannels = "Microsoft::Windows::RDS::Geometry" + +[Filters] +; FilterName = FilterPath +DemoFilter = "server/proxy/filters/libdemo_filter.so" diff --git a/server/proxy/filters/CMakeLists.txt b/server/proxy/filters/CMakeLists.txt new file mode 100644 index 000000000..43f753469 --- /dev/null +++ b/server/proxy/filters/CMakeLists.txt @@ -0,0 +1,3 @@ +add_library(demo_filter SHARED + filter_demo.c +) diff --git a/server/proxy/filters/README.md b/server/proxy/filters/README.md new file mode 100644 index 000000000..c6b3b81c1 --- /dev/null +++ b/server/proxy/filters/README.md @@ -0,0 +1,27 @@ +# Proxy filter API + +`freerdp-proxy` has an API for filtering certain messages. A filter can register callbacks to events, allowing to record the data and control whether to pass/ignore the message, or right out drop the connection. + +During startup, the proxy loads its filters from the configuration: +```ini +[Filters] +; FilterName = FilterPath +DemoFilter = "server/proxy/demo.so" +``` + +## Currently supported events +* Mouse event +* Keyboard event + +## Developing a new filter +* Create a new file that includes `filters_api.h`. +* Implement the `filter_init` function and register the callbacks you are interested in. +* Each callback receives two parameters: + * `connectionInfo* info` holds connection info of the raised event. + * `void* param` holds the actual event data. It should be casted by the filter to the suitable struct from `filters_api.h`. +* Each callback must return a `PF_FILTER_RESULT`: + * `FILTER_IGNORE`: The event will not be proxied. + * `FILTER_PASS`: The event will be proxied. + * `FILTER_DROP`: The entire connection will be dropped. + +A demo can be found in `filter_demo.c`. \ No newline at end of file diff --git a/server/proxy/filters/filter_demo.c b/server/proxy/filters/filter_demo.c new file mode 100644 index 000000000..55255f21f --- /dev/null +++ b/server/proxy/filters/filter_demo.c @@ -0,0 +1,48 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * FreeRDP Proxy Server + * + * 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. + */ + +#include "filters_api.h" + +static PF_FILTER_RESULT demo_filter_keyboard_event(connectionInfo* info, void* param) +{ + proxyKeyboardEventInfo* event_data = (proxyKeyboardEventInfo*) param; + WINPR_UNUSED(event_data); + + return FILTER_PASS; +} + +static PF_FILTER_RESULT demo_filter_mouse_event(connectionInfo* info, void* param) +{ + proxyMouseEventInfo* event_data = (proxyMouseEventInfo*) param; + + if (event_data->x % 100 == 0) + { + return FILTER_DROP; + } + + return FILTER_PASS; +} + +BOOL filter_init(proxyEvents* events) +{ + events->KeyboardEvent = demo_filter_keyboard_event; + events->MouseEvent = demo_filter_mouse_event; + + return TRUE; +} diff --git a/server/proxy/filters/filters_api.h b/server/proxy/filters/filters_api.h new file mode 100644 index 000000000..644ebc962 --- /dev/null +++ b/server/proxy/filters/filters_api.h @@ -0,0 +1,69 @@ +/** + * 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 + +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. + **/ +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 d85afd44a..80fa85adf 100644 --- a/server/proxy/freerdp_proxy.c +++ b/server/proxy/freerdp_proxy.c @@ -22,6 +22,9 @@ #include "pf_server.h" #include "pf_config.h" #include "pf_log.h" +#include "pf_filters.h" + +#include #define TAG PROXY_TAG("server") @@ -31,6 +34,7 @@ int main(int argc, char* argv[]) int status = 0; DWORD ld; UINT32 i; + UINT32 count; proxyConfig* config = calloc(1, sizeof(proxyConfig)); if (!config) @@ -58,16 +62,18 @@ int main(int argc, char* argv[]) if (config->WhitelistMode) { WLog_INFO(TAG, "Channels mode: WHITELIST"); + count = ArrayList_Count(config->AllowedChannels); - for (i = 0; i < config->AllowedChannelsCount; i++) - WLog_INFO(TAG, "Allowing %s", config->AllowedChannels[i]); + for (i = 0; i < count; i++) + WLog_INFO(TAG, "Allowing %s", (char*) ArrayList_GetItem(config->AllowedChannels, i)); } else { WLog_INFO(TAG, "Channels mode: BLACKLIST"); + count = ArrayList_Count(config->BlockedChannels); - for (i = 0; i < config->BlockedChannelsCount; i++) - WLog_INFO(TAG, "Blocking %s", config->BlockedChannels[i]); + for (i = 0; i < count; i++) + WLog_INFO(TAG, "Blocking %s", (char*) ArrayList_GetItem(config->BlockedChannels, i)); } status = pf_server_start(config); diff --git a/server/proxy/pf_config.c b/server/proxy/pf_config.c index 388a1e5bb..4802631b3 100644 --- a/server/proxy/pf_config.c +++ b/server/proxy/pf_config.c @@ -22,6 +22,8 @@ #include #include #include +#include + #include "pf_log.h" #include "pf_server.h" #include "pf_config.h" @@ -30,39 +32,56 @@ #define CHANNELS_SEPERATOR "," -static char** parse_channels_from_str(const char* str, UINT32* length) +wArrayList* parse_string_array_from_str(const char* str) { - char* s = strdup(str); - size_t tokens_alloc = 1; - size_t tokens_count = 0; - char** tokens = calloc(tokens_alloc, sizeof(char*)); + wArrayList* list = ArrayList_New(FALSE); + char* s; + char* temp; char* token; - while ((token = StrSep(&s, CHANNELS_SEPERATOR)) != NULL) + if (list == NULL) { - if (tokens_count == tokens_alloc) + WLog_ERR(TAG, "parse_string_array_from_str(): ArrayList_New failed!"); + return NULL; + } + + + temp = s = _strdup(str); + if (!s) + { + WLog_ERR(TAG, "parse_string_array_from_str(): strdup failed!"); + return NULL; + } + + if (s == NULL) + { + WLog_ERR(TAG, "parse_string_array_from_str(): strdup failed!"); + goto error; + } + + while ((token = StrSep(&temp, CHANNELS_SEPERATOR)) != NULL) + { + char* current_token = _strdup(token); + + if (current_token == NULL) { - tokens_alloc *= 2; - tokens = realloc(tokens, tokens_alloc * sizeof(char*)); + WLog_ERR(TAG, "parse_string_array_from_str(): strdup failed!"); + goto error; } - tokens[tokens_count++] = strdup(token); + if (ArrayList_Add(list, current_token) < 0) + { + free(current_token); + goto error; + } } - if ((tokens_count == 0) || (tokens_count > UINT32_MAX)) - { - free(tokens); - tokens = NULL; - tokens_count = 0; - } - else - { - tokens = realloc(tokens, tokens_count * sizeof(char*)); - } - - *length = (DWORD)tokens_count; free(s); - return tokens; + return list; +error: + free(s); + ArrayList_Free(list); + return NULL; } static BOOL pf_server_is_config_valid(proxyConfig* config) @@ -100,7 +119,10 @@ static BOOL pf_server_is_config_valid(proxyConfig* config) DWORD pf_server_load_config(const char* path, proxyConfig* config) { const char* input; + char** filters_names; int rc; + int filters_count = 0; + UINT32 index; DWORD result = CONFIG_PARSE_ERROR; wIniFile* ini = IniFile_New(); @@ -141,10 +163,11 @@ DWORD pf_server_load_config(const char* path, proxyConfig* config) /* channels filtering */ config->WhitelistMode = IniFile_GetKeyValueInt(ini, "Channels", "WhitelistMode"); input = IniFile_GetKeyValueString(ini, "Channels", "AllowedChannels"); + /* filters api */ if (input) { - config->AllowedChannels = parse_channels_from_str(input, &config->AllowedChannelsCount); + config->AllowedChannels = parse_string_array_from_str(input); if (config->AllowedChannels == NULL) goto out; @@ -154,13 +177,34 @@ DWORD pf_server_load_config(const char* path, proxyConfig* config) if (input) { - config->BlockedChannels = parse_channels_from_str(input, &config->BlockedChannelsCount); + config->BlockedChannels = parse_string_array_from_str(input); if (config->BlockedChannels == NULL) goto out; } result = CONFIG_PARSE_SUCCESS; + + if (!pf_filters_init(&config->Filters)) + goto out; + + filters_names = IniFile_GetSectionKeyNames(ini, "Filters", &filters_count); + + for (index = 0; index < filters_count; index++) + { + char* filter_name = filters_names[index]; + const char* path = IniFile_GetKeyValueString(ini, "Filters", filter_name); + + if (!pf_filters_register_new(config->Filters, path, filter_name)) + { + WLog_DBG(TAG, "pf_server_load_config(): failed to register %s (%s)", filter_name, path); + } + else + { + WLog_DBG(TAG, "pf_server_load_config(): registered filter %s (%s) successfully", filter_name, path); + } + } + out: IniFile_Free(ini); @@ -172,16 +216,9 @@ out: void pf_server_config_free(proxyConfig* config) { - UINT32 i; - - for (i = 0; i < config->AllowedChannelsCount; i++) - free(config->AllowedChannels[i]); - - for (i = 0; i < config->BlockedChannelsCount; i++) - free(config->BlockedChannels[i]); - - free(config->AllowedChannels); - free(config->BlockedChannels); + pf_filters_unregister_all(config->Filters); + ArrayList_Free(config->AllowedChannels); + ArrayList_Free(config->BlockedChannels); free(config->TargetHost); free(config->Host); free(config); diff --git a/server/proxy/pf_config.h b/server/proxy/pf_config.h index 3ae229977..a432cff63 100644 --- a/server/proxy/pf_config.h +++ b/server/proxy/pf_config.h @@ -28,6 +28,8 @@ #include +#include "pf_filters.h" + struct proxy_config { /* server */ @@ -56,11 +58,11 @@ struct proxy_config /* channels */ BOOL WhitelistMode; - char** AllowedChannels; - UINT32 AllowedChannelsCount; + wArrayList* AllowedChannels; + wArrayList* BlockedChannels; - char** BlockedChannels; - UINT32 BlockedChannelsCount; + /* filters */ + filters_list* Filters; }; typedef struct proxy_config proxyConfig; diff --git a/server/proxy/pf_context.c b/server/proxy/pf_context.c index aea666194..1169e777d 100644 --- a/server/proxy/pf_context.c +++ b/server/proxy/pf_context.c @@ -56,8 +56,7 @@ BOOL init_p_server_context(freerdp_peer* client) return freerdp_peer_context_new(client); } -rdpContext* p_client_context_create(rdpSettings* clientSettings, - char* host, DWORD port) +rdpContext* p_client_context_create(rdpSettings* clientSettings) { RDP_CLIENT_ENTRY_POINTS clientEntryPoints; rdpContext* context; @@ -73,8 +72,6 @@ rdpContext* p_client_context_create(rdpSettings* clientSettings, settings->Username = _strdup(clientSettings->Username); settings->Password = _strdup(clientSettings->Password); settings->Domain = _strdup(clientSettings->Domain); - settings->ServerHostname = host; - settings->ServerPort = port; settings->SoftwareGdi = FALSE; settings->RedirectClipboard = FALSE; /* Client Monitor Data */ @@ -119,3 +116,61 @@ error: freerdp_client_context_free(context); 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* pdata = calloc(1, sizeof(proxyData)); + + if (pdata == NULL) + { + return NULL; + } + + pdata->info = calloc(1, sizeof(connectionInfo)); + + if (pdata->info == NULL) + { + free(pdata); + return NULL; + } + + if (!(pdata->connectionClosed = CreateEvent(NULL, TRUE, FALSE, NULL))) + { + proxy_data_free(pdata); + return NULL; + } + + return pdata; +} + +BOOL proxy_data_set_connection_info(proxyData* pdata, rdpSettings* clientSettings, + const char* target) +{ + if (!(pdata->info->TargetHostname = _strdup(target))) + goto out_fail; + + if (!(pdata->info->ClientHostname = _strdup(clientSettings->ClientHostname))) + goto out_fail; + + if (!(pdata->info->Username = _strdup(clientSettings->Username))) + goto out_fail; + + return TRUE; +out_fail: + proxy_data_free(pdata); + return FALSE; +} + +void proxy_data_free(proxyData* pdata) +{ + connection_info_free(pdata->info); + free(pdata); +} diff --git a/server/proxy/pf_context.h b/server/proxy/pf_context.h index fa92168de..b63da7d8b 100644 --- a/server/proxy/pf_context.h +++ b/server/proxy/pf_context.h @@ -29,7 +29,7 @@ #include #include "pf_config.h" #include "pf_server.h" - +#include "pf_filters.h" typedef struct proxy_data proxyData; @@ -75,9 +75,16 @@ struct proxy_data pClientContext* pc; HANDLE connectionClosed; + + connectionInfo* info; + filters_list* filters; }; BOOL init_p_server_context(freerdp_peer* client); -rdpContext* p_client_context_create(rdpSettings* clientSettings, char* host, DWORD port); +rdpContext* p_client_context_create(rdpSettings* clientSettings); +proxyData* proxy_data_new(); +BOOL proxy_data_set_connection_info(proxyData* pdata, rdpSettings* clientSettings, + const char* target); +void proxy_data_free(proxyData* pdata); #endif /* FREERDP_SERVER_PROXY_PFCONTEXT_H */ diff --git a/server/proxy/pf_filters.c b/server/proxy/pf_filters.c new file mode 100644 index 000000000..21c120f2c --- /dev/null +++ b/server/proxy/pf_filters.c @@ -0,0 +1,216 @@ +/** + * 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_DBG(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) +{ + assert(filter != NULL); + 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 new file mode 100644 index 000000000..d88cf72d3 --- /dev/null +++ b/server/proxy/pf_filters.h @@ -0,0 +1,74 @@ +/** + * 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_input.c b/server/proxy/pf_input.c index 6babb2b5d..01b8862c3 100644 --- a/server/proxy/pf_input.c +++ b/server/proxy/pf_input.c @@ -32,15 +32,22 @@ static BOOL pf_server_synchronize_event(rdpInput* input, UINT32 flags) static BOOL pf_server_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code) { + BOOL result = FALSE; pServerContext* ps = (pServerContext*)input->context; pClientContext* pc = ps->pdata->pc; rdpContext* context = (rdpContext*) pc; proxyConfig* config = ps->pdata->config; + proxyKeyboardEventInfo event; if (!config->Keyboard) return TRUE; - return freerdp_input_send_keyboard_event(context->input, flags, code); + event.flags = flags; + event.rdp_scan_code = code; + + RUN_FILTER(config->Filters, FILTER_TYPE_KEYBOARD, ps->pdata->info, &event, result, + freerdp_input_send_keyboard_event, context->input, flags, code); + return result; } static BOOL pf_server_unicode_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code) @@ -58,15 +65,23 @@ static BOOL pf_server_unicode_keyboard_event(rdpInput* input, UINT16 flags, UINT static BOOL pf_server_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y) { + BOOL result = FALSE; pServerContext* ps = (pServerContext*)input->context; pClientContext* pc = ps->pdata->pc; rdpContext* context = (rdpContext*) pc; proxyConfig* config = ps->pdata->config; + proxyMouseEventInfo event; if (!config->Mouse) return TRUE; - return freerdp_input_send_mouse_event(context->input, flags, x, y); + event.flags = flags; + event.x = x; + event.y = y; + + RUN_FILTER(config->Filters, FILTER_TYPE_MOUSE, ps->pdata->info, &event, result, + freerdp_input_send_mouse_event, context->input, flags, x, y); + return result; } static BOOL pf_server_extended_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, diff --git a/server/proxy/pf_server.c b/server/proxy/pf_server.c index 77d4451e0..9be46a977 100644 --- a/server/proxy/pf_server.c +++ b/server/proxy/pf_server.c @@ -71,7 +71,7 @@ static void pf_server_handle_client_disconnection(freerdp_peer* client) CloseHandle(ps->thread); } -static BOOL pf_server_parse_target_from_routing_token(freerdp_peer* client, +static BOOL pf_server_parse_target_from_routing_token(rdpContext* context, char** target, DWORD* port) { #define TARGET_MAX (100) @@ -80,39 +80,70 @@ static BOOL pf_server_parse_target_from_routing_token(freerdp_peer* client, size_t len; const size_t prefix_len = strlen(ROUTING_TOKEN_PREFIX); DWORD routing_token_length; - const char* routing_token = freerdp_nego_get_routing_token(client->context, &routing_token_length); + const char* routing_token = freerdp_nego_get_routing_token(context, &routing_token_length); - if (routing_token && - (routing_token_length > prefix_len) && (routing_token_length < TARGET_MAX)) + if (routing_token == NULL) { - len = routing_token_length - prefix_len; - *target = malloc(len + 1); - - if (!(*target)) - return FALSE; - - CopyMemory(*target, routing_token + prefix_len, len); - *(*target + len) = '\0'; - colon = strchr(*target, ':'); - WLog_INFO(TAG, "Target [parsed from routing token]: %s", *target); - - if (colon) - { - /* port is specified */ - unsigned long p = strtoul(colon + 1, NULL, 10); - - if (p > USHRT_MAX) - return FALSE; - - *port = (DWORD)p; - *colon = '\0'; - } - - return TRUE; + /* no routing token */ + return FALSE; } - /* no routing token */ - return FALSE; + if ((routing_token_length <= prefix_len) || (routing_token_length >= TARGET_MAX)) + { + WLog_ERR(TAG, "pf_server_parse_target_from_routing_token: bad routing token length: %i", + routing_token_length); + return FALSE; + } + + len = routing_token_length - prefix_len; + *target = malloc(len + 1); + + if (!(*target)) + return FALSE; + + CopyMemory(*target, routing_token + prefix_len, len); + *(*target + len) = '\0'; + colon = strchr(*target, ':'); + WLog_INFO(TAG, "Target [parsed from routing token]: %s", *target); + + if (colon) + { + /* port is specified */ + unsigned long p = strtoul(colon + 1, NULL, 10); + + if (p > USHRT_MAX) + { + free(*target); + return FALSE; + } + + *port = (DWORD)p; + *colon = '\0'; + } + + return TRUE; +} + +static BOOL pf_server_get_target_info(rdpContext* context, rdpSettings* settings, + proxyConfig* config) +{ + WLog_INFO(TAG, "pf_server_get_target_info: UseLoadBalanceInfo = %d", config->UseLoadBalanceInfo); + + if (config->UseLoadBalanceInfo) + return pf_server_parse_target_from_routing_token(context, &settings->ServerHostname, + &settings->ServerPort); + + /* use hardcoded target info from configuration */ + if (!(settings->ServerHostname = _strdup(config->TargetHost))) + { + WLog_DBG(TAG, "pf_server_get_target_info(): strdup failed!"); + return FALSE; + } + + settings->ServerPort = config->TargetPort > 0 ? 3389 : settings->ServerPort; + WLog_INFO(TAG, "Using target host from config: %s:%i", settings->ServerHostname, + settings->ServerPort); + return TRUE; } /* Event callbacks */ @@ -125,47 +156,35 @@ static BOOL pf_server_parse_target_from_routing_token(freerdp_peer* client, */ static BOOL pf_server_post_connect(freerdp_peer* client) { - proxyConfig* config; pServerContext* ps; - pClientContext* pc; - HANDLE connectionClosedEvent; + rdpContext* pc; proxyData* pdata; - char* host = NULL; - DWORD port = 3389; /* default port */ + ps = (pServerContext*)client->context; pdata = ps->pdata; - config = pdata->config; - if (config->UseLoadBalanceInfo) - { - if (!pf_server_parse_target_from_routing_token(client, &host, &port)) - { - WLog_ERR(TAG, "pf_server_parse_target_from_routing_token failed!"); - return FALSE; - } + pc = p_client_context_create(client->settings); - WLog_DBG(TAG, "Parsed target from load-balance-info: %s:%i", host, port); - } - else - { - /* use hardcoded target info from configuration */ - host = _strdup(config->TargetHost); - port = config->TargetPort > 0 ? config->TargetPort : port; - WLog_DBG(TAG, "Using hardcoded target host: %s:%i", host, port); - } - - pc = (pClientContext*) p_client_context_create(client->settings, host, port); - connectionClosedEvent = CreateEvent(NULL, TRUE, FALSE, NULL); /* keep both sides of the connection in pdata */ - pc->pdata = ps->pdata; - pdata->pc = (pClientContext*) pc; - pdata->ps = ps; - pdata->connectionClosed = connectionClosedEvent; + ((pClientContext*)pc)->pdata = ps->pdata; + pdata->pc = (pClientContext*)pc; + + if (!pf_server_get_target_info(client->context, pc->settings, pdata->config)) + { + WLog_ERR(TAG, "pf_server_post_connect(): pf_server_get_target_info failed!"); + return FALSE; + } + + if (!proxy_data_set_connection_info(pdata, client->settings, pc->settings->ServerHostname)) + { + WLog_ERR(TAG, "proxy_data_set_connection_info failed!"); + return FALSE; + } + pf_server_rdpgfx_init(ps); /* Start a proxy's client in it's own thread */ - if (!(ps->thread = CreateThread(NULL, 0, pf_client_start, (rdpContext*) pc, 0, - NULL))) + if (!(ps->thread = CreateThread(NULL, 0, pf_client_start, pc, 0, NULL))) { WLog_ERR(TAG, "CreateThread failed!"); return FALSE; @@ -202,18 +221,25 @@ static DWORD WINAPI pf_server_handle_client(LPVOID arg) rdpContext* pc; proxyData* pdata; proxyConfig* config; - freerdp_peer* client = (freerdp_peer*) arg; + freerdp_peer* client = (freerdp_peer*)arg; if (!init_p_server_context(client)) + goto out_free_peer; + + ps = (pServerContext*)client->context; + if (!(ps->dynvcReady = CreateEvent(NULL, TRUE, FALSE, NULL))) { - freerdp_peer_free(client); - return 0; + WLog_ERR(TAG, "pf_server_post_connect(): CreateEvent failed!"); + goto out_free_peer; } - ps = (pServerContext*) client->context; - ps->dynvcReady = CreateEvent(NULL, TRUE, FALSE, NULL); - pdata = calloc(1, sizeof(proxyData)); - ps->pdata = pdata; + if (!(pdata = ps->pdata = proxy_data_new())) + { + WLog_ERR(TAG, "pf_server_post_connect(): proxy_data_new failed!"); + goto out_free_peer; + } + + pdata->ps = ps; /* keep configuration in proxyData */ pdata->config = client->ContextExtra; config = pdata->config; @@ -223,12 +249,11 @@ static DWORD WINAPI pf_server_handle_client(LPVOID arg) client->settings->PrivateKeyFile = _strdup("server.key"); client->settings->RdpKeyFile = _strdup("server.key"); - if (!client->settings->CertificateFile || !client->settings->PrivateKeyFile - || !client->settings->RdpKeyFile) + if (!client->settings->CertificateFile || !client->settings->PrivateKeyFile || + !client->settings->RdpKeyFile) { WLog_ERR(TAG, "Memory allocation failed (strdup)"); - freerdp_peer_free(client); - return 0; + goto out_free_peer; } client->settings->RdpSecurity = config->RdpSecurity; @@ -247,8 +272,7 @@ static DWORD WINAPI pf_server_handle_client(LPVOID arg) pf_server_register_update_callbacks(client->update); client->settings->MultifragMaxRequestSize = 0xFFFFFF; /* FIXME */ client->Initialize(client); - WLog_INFO(TAG, "Client connected: %s", - client->local ? "(local)" : client->hostname); + WLog_INFO(TAG, "Client connected: %s", client->local ? "(local)" : client->hostname); /* Main client event handling loop */ ChannelEvent = WTSVirtualChannelManagerGetEventHandle(ps->vcm); @@ -256,8 +280,7 @@ static DWORD WINAPI pf_server_handle_client(LPVOID arg) { eventCount = 0; { - tmp = client->GetEventHandles(client, &eventHandles[eventCount], - 32 - eventCount); + tmp = client->GetEventHandles(client, &eventHandles[eventCount], 32 - eventCount); if (tmp == 0) { @@ -295,28 +318,28 @@ static DWORD WINAPI pf_server_handle_client(LPVOID arg) switch (WTSVirtualChannelManagerGetDrdynvcState(ps->vcm)) { - /* Dynamic channel status may have been changed after processing */ - case DRDYNVC_STATE_NONE: + /* Dynamic channel status may have been changed after processing */ + case DRDYNVC_STATE_NONE: - /* Initialize drdynvc channel */ - if (!WTSVirtualChannelManagerCheckFileDescriptor(ps->vcm)) - { - WLog_ERR(TAG, "Failed to initialize drdynvc channel"); - goto fail; - } + /* Initialize drdynvc channel */ + if (!WTSVirtualChannelManagerCheckFileDescriptor(ps->vcm)) + { + WLog_ERR(TAG, "Failed to initialize drdynvc channel"); + goto fail; + } - break; + break; - case DRDYNVC_STATE_READY: - if (WaitForSingleObject(ps->dynvcReady, 0) == WAIT_TIMEOUT) - { - SetEvent(ps->dynvcReady); - } + case DRDYNVC_STATE_READY: + if (WaitForSingleObject(ps->dynvcReady, 0) == WAIT_TIMEOUT) + { + SetEvent(ps->dynvcReady); + } - break; + break; - default: - break; + default: + break; } } @@ -327,24 +350,23 @@ fail: pf_server_handle_client_disconnection(client); } - pc = (rdpContext*) pdata->pc; + pc = (rdpContext*)pdata->pc; freerdp_client_stop(pc); - free(pdata); + proxy_data_free(pdata); freerdp_client_context_free(pc); client->Disconnect(client); +out_free_peer: freerdp_peer_context_free(client); freerdp_peer_free(client); return 0; } -static BOOL pf_server_client_connected(freerdp_listener* listener, - freerdp_peer* client) +static BOOL pf_server_client_connected(freerdp_listener* listener, freerdp_peer* client) { HANDLE hThread; client->ContextExtra = listener->info; - if (!(hThread = CreateThread(NULL, 0, pf_server_handle_client, - (void*) client, 0, NULL))) + if (!(hThread = CreateThread(NULL, 0, pf_server_handle_client, (void*)client, 0, NULL))) return FALSE; CloseHandle(hThread); @@ -408,7 +430,7 @@ int pf_server_start(proxyConfig* config) } /* Determine filepath for local socket */ - sprintf_s(localSockName, sizeof(localSockName), "proxy.%"PRIu16"", config->Port); + sprintf_s(localSockName, sizeof(localSockName), "proxy.%" PRIu16 "", config->Port); localSockPath = GetKnownSubPath(KNOWN_PATH_TEMP, localSockName); if (!localSockPath)