pf_config.h
pf_graphics.c
pf_graphics.h
+ pf_filters.c
+ pf_filters.h
pf_log.h)
# On windows create dll version information.
WhitelistMode = 0
AllowedChannels = "cliprdr,Microsoft::Windows::RDS::Video::Control"
DeniedChannels = "Microsoft::Windows::RDS::Geometry"
+
+[Filters]
+; FilterName = FilterPath
+DemoFilter = "server/proxy/demo.so"
--- /dev/null
+#include "filters_api.h"
+
+static PF_FILTER_RESULT demo_filter_keyboard_event(connectionInfo* info, void* param)
+{
+ proxyKeyboardEventInfo* event_data = (proxyKeyboardEventInfo*) param;
+ 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;
+}
--- /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 <inttypes.h>
+#include <stdbool.h>
+
+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_t flags;
+ uint16_t rdp_scan_code;
+};
+
+struct proxy_mouse_event_info {
+ uint16_t flags;
+ uint16_t x;
+ uint16_t y;
+};
+#pragma pack(pop)
+
+
+/* implement this method */
+bool filter_init(proxyEvents* events);
+
+#endif /* FREERDP_SERVER_PROXY_FILTERS_API_H */
#include <stdio.h>
#include <string.h>
#include <winpr/crt.h>
+#include <winpr/collections.h>
+
#include "pf_log.h"
#include "pf_server.h"
#include "pf_config.h"
#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*));
- char* token;
+ wArrayList* list = ArrayList_New(FALSE);
- while ((token = StrSep(&s, CHANNELS_SEPERATOR)) != NULL)
+ if (list == NULL)
{
- if (tokens_count == tokens_alloc)
- {
- tokens_alloc *= 2;
- tokens = realloc(tokens, tokens_alloc * sizeof(char*));
- }
-
- tokens[tokens_count++] = strdup(token);
+ WLog_ERR(TAG, "parse_string_array_from_str(): ArrayList_New failed!");
+ return NULL;
}
- if ((tokens_count == 0) || (tokens_count > UINT32_MAX))
+ char* s = _strdup(str);
+ char* temp = s;
+ char* token;
+
+ if (s == NULL)
{
- free(tokens);
- tokens = NULL;
- tokens_count = 0;
+ WLog_ERR(TAG, "parse_string_array_from_str(): strdup failed!");
+ goto error;
}
- else
+
+ while ((token = StrSep(&temp, CHANNELS_SEPERATOR)) != NULL)
{
- tokens = realloc(tokens, tokens_count * sizeof(char*));
+ char* current_token = _strdup(token);
+
+ if (current_token == NULL)
+ {
+ WLog_ERR(TAG, "parse_string_array_from_str(): strdup failed!");
+ goto error;
+ }
+
+ if (ArrayList_Add(list, current_token) < 0)
+ {
+ free(current_token);
+ goto error;
+ }
}
- *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)
DWORD pf_server_load_config(const char* path, proxyConfig* config)
{
const char* input;
+ char** filters_names;
int rc;
+ int filters_count = 0;
DWORD result = CONFIG_PARSE_ERROR;
wIniFile* ini = IniFile_New();
/* 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;
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 (int i = 0; i < filters_count; i++)
+ {
+ char* filter_name = filters_names[i];
+ 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);
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);
#include <winpr/ini.h>
+#include "pf_filters.h"
+
struct proxy_config
{
/* server */
char** AllowedChannels;
UINT32 AllowedChannelsCount;
- char** BlockedChannels;
- UINT32 BlockedChannelsCount;
+ /* filters */
+ filters_list* Filters;
};
typedef struct proxy_config proxyConfig;
settings->RedirectClipboard = FALSE;
return context;
}
+
+static void pf_context_connection_info_free(connectionInfo* info)
+{
+ free(info->TargetHostname);
+ free(info->ClientHostname);
+ free(info->Username);
+ free(info);
+}
+
+proxyData* pf_context_proxy_data_new()
+{
+ proxyData* pdata = malloc(sizeof(proxyData));
+
+ if (pdata == NULL)
+ {
+ return NULL;
+ }
+
+ pdata->info = pf_context_connection_info_new();
+
+ if (pdata->info == NULL)
+ {
+ free(pdata);
+ return NULL;
+ }
+
+ return pdata;
+}
+
+void pf_context_proxy_data_free(proxyData* pdata)
+{
+ pf_context_connection_info_free(pdata->info);
+ free(pdata);
+}
#include <freerdp/server/rdpgfx.h>
#include "pf_config.h"
#include "pf_server.h"
+#include "pf_filters.h"
typedef struct proxy_data proxyData;
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);
+proxyData* pf_context_proxy_data_new();
+void pf_context_proxy_data_free(proxyData* pdata);
+connectionInfo* pf_context_connection_info_new();
+void pf_context_connection_info_free(connectionInfo* info);
#endif /* FREERDP_SERVER_PROXY_PFCONTEXT_H */
--- /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_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);
+ FreeLibrary(filter->handle);
+ free(filter->name);
+ free(filter->events);
+ free(filter);
+}
+
+void pf_filters_unregister_all(filters_list* list)
+{
+ if (list == NULL)
+ return;
+
+ const size_t count = (size_t) ArrayList_Count(list);
+ size_t index;
+
+ 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;
+
+ if (list == NULL)
+ {
+ WLog_ERR(TAG, "pf_filters_register_new(): list == NULL");
+ goto error;
+ }
+
+ handle = LoadLibraryA(module_path);
+
+ if (handle == NULL)
+ {
+ WLog_ERR(TAG, "pf_filters_register_new(): failed loading external module: %s", module_path);
+ goto error;
+ }
+
+ 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 = malloc(sizeof(proxyEvents));
+
+ if (events == NULL)
+ {
+ WLog_ERR(TAG, "pf_filters_register_new(): failed loading external module: %s", module_path);
+ 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);
+
+ free(events);
+ 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,_cb,...) ({ \
+ ({ BOOL result; switch(pf_filters_run_by_type(_filters,_type,_conn_info,_event_info)) { \
+ case FILTER_PASS: \
+ result = _cb(__VA_ARGS__); \
+ break; \
+ case FILTER_IGNORE: \
+ result = TRUE; \
+ break; \
+ case FILTER_DROP: \
+ default: \
+ result = FALSE; \
+ }; result; \
+ }); \
+ })
+
+#endif /* FREERDP_SERVER_PROXY_FILTERS_H */
if (!config->Keyboard)
return TRUE;
- return freerdp_input_send_keyboard_event(context->input, flags, code);
+ proxyKeyboardEventInfo info =
+ {
+ .flags = flags,
+ .rdp_scan_code = code
+ };
+ return RUN_FILTER(config->Filters, FILTER_TYPE_KEYBOARD, ps->pdata->info, &info,
+ freerdp_input_send_keyboard_event, context->input, flags, code);
}
static BOOL pf_server_unicode_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code)
if (!config->Mouse)
return TRUE;
- return freerdp_input_send_mouse_event(context->input, flags, x, y);
+ proxyMouseEventInfo info =
+ {
+ .flags = flags,
+ .x = x, .y = y
+ };
+ return RUN_FILTER(config->Filters, FILTER_TYPE_MOUSE, ps->pdata->info, &info,
+ freerdp_input_send_mouse_event, context->input, flags, x, y);
}
static BOOL pf_server_extended_mouse_event(rdpInput* input, UINT16 flags, UINT16 x,
connectionClosedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
/* keep both sides of the connection in pdata */
pc->pdata = ps->pdata;
- pdata->pc = (pClientContext*) pc;
+ pdata->info->TargetHostname = _strdup(host);
+ pdata->info->Username = _strdup(client->settings->Username);
+ pdata->pc = pc;
pdata->ps = ps;
pdata->connectionClosed = connectionClosedEvent;
pf_server_rdpgfx_init(ps);
ps = (pServerContext*) client->context;
ps->dynvcReady = CreateEvent(NULL, TRUE, FALSE, NULL);
- pdata = calloc(1, sizeof(proxyData));
+ pdata = pf_context_proxy_data_new();
+
+ if (pdata == NULL)
+ {
+ WLog_ERR(TAG, "pf_context_proxy_data_new failed!");
+ return 0;
+ }
+
+ pdata->info->ClientHostname = _strdup(client->hostname);
ps->pdata = pdata;
/* keep configuration in proxyData */
pdata->config = client->ContextExtra;
pc = (rdpContext*) pdata->pc;
freerdp_client_stop(pc);
- free(pdata);
+ pf_context_proxy_data_free(pdata);
freerdp_client_context_free(pc);
client->Disconnect(client);
freerdp_peer_context_free(client);