server/proxy: Add external filters support
authorkubistika <kmizrachi18@gmail.com>
Sun, 12 May 2019 17:48:51 +0000 (20:48 +0300)
committerkubistika <kmizrachi18@gmail.com>
Sun, 26 May 2019 07:48:10 +0000 (10:48 +0300)
12 files changed:
server/proxy/CMakeLists.txt
server/proxy/config.ini
server/proxy/filters/filter_demo.c [new file with mode: 0644]
server/proxy/filters/filters_api.h [new file with mode: 0644]
server/proxy/pf_config.c
server/proxy/pf_config.h
server/proxy/pf_context.c
server/proxy/pf_context.h
server/proxy/pf_filters.c [new file with mode: 0644]
server/proxy/pf_filters.h [new file with mode: 0644]
server/proxy/pf_input.c
server/proxy/pf_server.c

index 6d749f5..46dc120 100644 (file)
@@ -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.
index 3a04dc6..c528041 100644 (file)
@@ -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/demo.so"
diff --git a/server/proxy/filters/filter_demo.c b/server/proxy/filters/filter_demo.c
new file mode 100644 (file)
index 0000000..985dbcf
--- /dev/null
@@ -0,0 +1,25 @@
+#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;
+}
diff --git a/server/proxy/filters/filters_api.h b/server/proxy/filters/filters_api.h
new file mode 100644 (file)
index 0000000..c77386d
--- /dev/null
@@ -0,0 +1,69 @@
+/**
+ * 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 */
index 388a1e5..88841e6 100644 (file)
@@ -22,6 +22,8 @@
 #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)
@@ -100,7 +112,9 @@ 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();
 
@@ -141,10 +155,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 +169,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 (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);
 
@@ -172,16 +208,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);
index 3ae2299..26429a1 100644 (file)
@@ -28,6 +28,8 @@
 
 #include <winpr/ini.h>
 
+#include "pf_filters.h"
+
 struct proxy_config
 {
        /* server */
@@ -59,8 +61,8 @@ struct proxy_config
        char** AllowedChannels;
        UINT32 AllowedChannelsCount;
 
-       char** BlockedChannels;
-       UINT32 BlockedChannelsCount;
+       /* filters */
+       filters_list* Filters;
 };
 
 typedef struct proxy_config proxyConfig;
index 0e00d40..b8c516b 100644 (file)
@@ -79,3 +79,37 @@ rdpContext* p_client_context_create(rdpSettings* clientSettings,
        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);
+}
index fa92168..ae9856f 100644 (file)
@@ -29,6 +29,7 @@
 #include <freerdp/server/rdpgfx.h>
 #include "pf_config.h"
 #include "pf_server.h"
+#include "pf_filters.h"
 
 
 typedef struct proxy_data proxyData;
@@ -75,9 +76,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);
+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 */
diff --git a/server/proxy/pf_filters.c b/server/proxy/pf_filters.c
new file mode 100644 (file)
index 0000000..171cb4a
--- /dev/null
@@ -0,0 +1,218 @@
+/**
+ * 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;
+}
diff --git a/server/proxy/pf_filters.h b/server/proxy/pf_filters.h
new file mode 100644 (file)
index 0000000..8924bca
--- /dev/null
@@ -0,0 +1,75 @@
+/**
+ * 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 */
index 6babb2b..3961292 100644 (file)
@@ -40,7 +40,13 @@ static BOOL pf_server_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code)
        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)
@@ -66,7 +72,13 @@ static BOOL pf_server_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT1
        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,
index c9e933f..e85b4da 100644 (file)
@@ -158,7 +158,9 @@ static BOOL pf_server_post_connect(freerdp_peer* client)
        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);
@@ -206,7 +208,15 @@ static DWORD WINAPI pf_server_handle_client(LPVOID arg)
 
        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;
@@ -320,7 +330,7 @@ fail:
 
        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);