server: proxy: improve modules api
authorkubistika <kmizrachi18@gmail.com>
Mon, 19 Aug 2019 13:05:59 +0000 (16:05 +0300)
committerakallabeth <akallabeth@users.noreply.github.com>
Fri, 23 Aug 2019 09:58:08 +0000 (11:58 +0200)
19 files changed:
server/proxy/CMakeLists.txt
server/proxy/config.ini
server/proxy/filters/CMakeLists.txt [deleted file]
server/proxy/filters/filters_api.h [deleted file]
server/proxy/freerdp_proxy.c
server/proxy/modules/CMakeLists.txt [new file with mode: 0644]
server/proxy/modules/README.md [moved from server/proxy/filters/README.md with 100% similarity]
server/proxy/modules/demo/CMakeLists.txt [new file with mode: 0644]
server/proxy/modules/demo/demo.c [moved from server/proxy/filters/filter_demo.c with 66% similarity]
server/proxy/modules/modules_api.h [new file with mode: 0644]
server/proxy/pf_client.c
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 [deleted file]
server/proxy/pf_filters.h [deleted file]
server/proxy/pf_modules.c [new file with mode: 0644]
server/proxy/pf_modules.h [new file with mode: 0644]

index 557cdef..b8ccd5b 100644 (file)
@@ -3,7 +3,8 @@
 #
 # 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
@@ -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")
index 4ac80a7..39b1bfd 100644 (file)
@@ -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 (file)
index 43f7534..0000000
+++ /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 (file)
index 047bbec..0000000
+++ /dev/null
@@ -1,72 +0,0 @@
-/**
- * FreeRDP: A Remote Desktop Protocol Implementation
- * FreeRDP Proxy Server
- *
- * Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
- * Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
- * Copyright 2019 Idan Freiberg <speidy@gmail.com>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef FREERDP_SERVER_PROXY_FILTERS_API_H
-#define FREERDP_SERVER_PROXY_FILTERS_API_H
-
-#include <freerdp/freerdp.h>
-#include <winpr/winpr.h>
-
-#define PROXY_API FREERDP_API
-
-enum pf_filter_result {
-    FILTER_PASS = 0,
-    FILTER_DROP,
-    FILTER_IGNORE
-};
-
-typedef enum pf_filter_result PF_FILTER_RESULT;
-typedef struct connection_info connectionInfo;
-typedef struct proxy_events proxyEvents;
-typedef struct proxy_keyboard_event_info proxyKeyboardEventInfo;
-typedef struct proxy_mouse_event_info proxyMouseEventInfo;
-typedef PF_FILTER_RESULT(*proxyEvent)(connectionInfo* info, void* param);
-
-struct connection_info {
-    char* TargetHostname;
-    char* ClientHostname;
-    char* Username;
-};
-
-struct proxy_events {
-    proxyEvent KeyboardEvent;
-    proxyEvent MouseEvent;
-};
-
-#pragma pack(push, 1)
-struct proxy_keyboard_event_info {
-    UINT16 flags;
-    UINT16 rdp_scan_code;
-};
-
-struct proxy_mouse_event_info {
-    UINT16 flags;
-    UINT16 x;
-    UINT16 y;
-};
-#pragma pack(pop)
-
-/* implement this method and register callbacks for proxy events
- * return TRUE if initialization succeeded, otherwise FALSE.
- **/
-PROXY_API BOOL filter_init(proxyEvents* events);
-
-#endif /* FREERDP_SERVER_PROXY_FILTERS_API_H */
index 97e714d..a5b36ab 100644 (file)
 #include "pf_server.h"
 #include "pf_config.h"
 #include "pf_log.h"
-#include "pf_filters.h"
+#include "pf_modules.h"
 
 #include <winpr/collections.h>
 
 #define TAG PROXY_TAG("server")
 
+
 int main(int argc, char* argv[])
 {
        const char* cfg = "config.ini";
@@ -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 (file)
index 0000000..ed8ec43
--- /dev/null
@@ -0,0 +1,33 @@
+# Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# The third-party directory is meant for third-party components to be built
+# as part of the main FreeRDP build system, making separate maintenance easier.
+# Subdirectories of the third-party directory are ignored by git, but are
+# automatically included by CMake when the -DWITH_THIRD_PARTY=on option is used.
+
+# include proxy header files for proxy modules
+include_directories("${CMAKE_SOURCE_DIR}/server/proxy")
+include_directories("${CMAKE_SOURCE_DIR}/server/proxy/modules")
+
+# taken from FreeRDP/third-party/CMakeLists.txt
+file(GLOB all_valid_subdirs RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*/CMakeLists.txt")
+
+foreach(dir ${all_valid_subdirs})
+       if(${dir} MATCHES "^([^/]*)/+CMakeLists.txt")
+               string(REGEX REPLACE "^([^/]*)/+CMakeLists.txt" "\\1" dir_trimmed ${dir})
+               message(STATUS "Adding proxy module ${dir_trimmed}")
+               add_subdirectory(${dir_trimmed})
+       endif()
+endforeach(dir)
diff --git a/server/proxy/modules/demo/CMakeLists.txt b/server/proxy/modules/demo/CMakeLists.txt
new file mode 100644 (file)
index 0000000..849461e
--- /dev/null
@@ -0,0 +1,4 @@
+
+add_library(demo SHARED
+       demo.c
+)
similarity index 66%
rename from server/proxy/filters/filter_demo.c
rename to server/proxy/modules/demo/demo.c
index e2018ad..4a147a5 100644 (file)
 
 #include <stdio.h>
 
-#include "filters_api.h"
+#include "modules_api.h"
 
-static PF_FILTER_RESULT demo_filter_keyboard_event(connectionInfo* info, void* param)
+static BOOL demo_filter_keyboard_event(moduleOperations* module, rdpContext* context, void* param)
 {
        proxyKeyboardEventInfo* event_data = (proxyKeyboardEventInfo*) param;
        WINPR_UNUSED(event_data);
 
-       return FILTER_PASS;
+       return TRUE;
 }
 
-static PF_FILTER_RESULT demo_filter_mouse_event(connectionInfo* info, void* param)
+static BOOL demo_filter_mouse_event(moduleOperations* module, rdpContext* context, void* param)
 {
        proxyMouseEventInfo* event_data = (proxyMouseEventInfo*) param;
 
        if (event_data->x % 100 == 0)
        {
+               module->AbortConnect(module, context);
                printf("filter_demo: mouse x is currently %"PRIu16"\n", event_data->x);
-               return FILTER_PASS;
        }
 
-       return FILTER_PASS;
+       return TRUE;
+}
+
+BOOL module_init(moduleOperations* module)
+{
+       module->KeyboardEvent = demo_filter_keyboard_event;
+       module->MouseEvent = demo_filter_mouse_event;
+
+       return TRUE;
 }
 
-BOOL filter_init(proxyEvents* events)
+BOOL module_exit(moduleOperations* module)
 {
-       events->KeyboardEvent = demo_filter_keyboard_event;
-       events->MouseEvent = demo_filter_mouse_event;
+       printf("bye bye\n");
 
        return TRUE;
 }
diff --git a/server/proxy/modules/modules_api.h b/server/proxy/modules/modules_api.h
new file mode 100644 (file)
index 0000000..3759562
--- /dev/null
@@ -0,0 +1,101 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * FreeRDP Proxy Server
+ *
+ * Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
+ * Copyright 2019 Idan Freiberg <speidy@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FREERDP_SERVER_PROXY_MODULES_API_H
+#define FREERDP_SERVER_PROXY_MODULES_API_H
+
+#include <freerdp/freerdp.h>
+#include <winpr/winpr.h>
+
+#define PROXY_API FREERDP_API
+
+typedef struct module_operations moduleOperations;
+
+/* used for filtering */
+typedef BOOL (*proxyFilterFn)(moduleOperations*, rdpContext*, void*);
+
+/* used for hooks */
+typedef BOOL (*proxyHookFn)(moduleOperations*, rdpContext*);
+
+/*
+ * used for per-session info.
+ *
+ * each module is allowed to store data per session.
+ * it is useful, for example, when a module wants to create a server channel in runtime,
+ * or to save information about a session (for example, the count of mouse clicks in the last
+ * minute), and then do something with this information (maybe abort the connection).
+ */
+typedef BOOL (*moduleSetSessionData)(moduleOperations*, rdpContext*, void*);
+typedef void* (*moduleGetSessionData)(moduleOperations*, rdpContext*);
+
+/*
+ * used for connection management. when a module wants to forcibly close a connection, it should
+ * call this method.
+ */
+typedef void (*moduleAbortConnect)(moduleOperations*, rdpContext*);
+
+typedef struct connection_info connectionInfo;
+typedef struct proxy_keyboard_event_info proxyKeyboardEventInfo;
+typedef struct proxy_mouse_event_info proxyMouseEventInfo;
+
+/* represents a set of operations that a module can do */
+struct module_operations
+{
+       /* per-session API. a module must not change these function pointers. */
+       moduleSetSessionData SetSessionData;
+       moduleGetSessionData GetSessionData;
+       moduleAbortConnect AbortConnect;
+
+       /* proxy hooks. a module can set these function pointers to register hooks. */
+       proxyHookFn ClientPreConnect;
+       proxyHookFn ServerChannelsInit;
+       proxyHookFn ServerChannelsFree;
+
+       /* proxy filters a module can set these function pointers to register filters. */
+       proxyFilterFn KeyboardEvent;
+       proxyFilterFn MouseEvent;
+};
+
+/* filter events parameters */
+#define WINPR_PACK_PUSH
+#include <winpr/pack.h>
+struct proxy_keyboard_event_info
+{
+       UINT16 flags;
+       UINT16 rdp_scan_code;
+};
+
+struct proxy_mouse_event_info
+{
+       UINT16 flags;
+       UINT16 x;
+       UINT16 y;
+};
+#define WINPR_PACK_POP
+#include <winpr/pack.h>
+
+/*
+ * these two functions must be implemented by any proxy module.
+ * module_init: used for module initialization, hooks and filters registration.
+ */
+PROXY_API BOOL module_init(moduleOperations* module);
+PROXY_API BOOL module_exit(moduleOperations* module);
+
+#endif /* FREERDP_SERVER_PROXY_MODULES_API_H */
index 97b744f..f465311 100644 (file)
@@ -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;
 
index 82db1fa..2de2f21 100644 (file)
@@ -2,7 +2,6 @@
  * 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>
  *
@@ -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;
-
-       filters_names = IniFile_GetSectionKeyNames(ini, "Filters", &filters_count);
+       module_names = IniFile_GetSectionKeyNames(ini, "Modules", &modules_count);
 
-       for (index = 0; index < filters_count; index++)
+       for (index = 0; index < modules_count; index++)
        {
-               char* filter_name = filters_names[index];
-               const char* path = CONFIG_GET_STR(ini, "Filters", filter_name);
+               char* module_name = module_names[index];
+               const char* path = CONFIG_GET_STR(ini, "Modules", module_name);
 
-               if (!pf_filters_register_new(config->Filters, path, filter_name))
+               if (!pf_modules_register_new(path, module_name))
                {
-                       WLog_DBG(TAG, "pf_config_load_filters(): failed to register %s (%s)", filter_name, path);
+                       WLog_DBG(TAG, "pf_config_load_modules(): failed to register %s (%s)", module_name, path);
                        continue;
                }
 
-               WLog_DBG(TAG, "pf_config_load_filters(): filter %s is registered", filter_name);
+               WLog_INFO(TAG, "module '%s' is loaded!", module_name);
        }
 
        return TRUE;
@@ -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);
index e7026db..44c5232 100644 (file)
@@ -2,7 +2,6 @@
  * FreeRDP: A Remote Desktop Protocol Implementation
  * FreeRDP Proxy Server
  *
- * Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
  * Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
  * Copyright 2019 Idan Freiberg <speidy@gmail.com>
  *
 
 #include <winpr/ini.h>
 
-#include "pf_filters.h"
+#define CONFIG_GET_STR(ini, section, key) IniFile_GetKeyValueString(ini, section, key)
+#define CONFIG_GET_BOOL(ini, section, key) IniFile_GetKeyValueInt(ini, section, key)
+
+typedef struct proxy_config proxyConfig;
 
 struct proxy_config
 {
@@ -52,9 +54,6 @@ struct proxy_config
        BOOL DisplayControl;
        BOOL Clipboard;
 
-       /* filters */
-       filters_list* Filters;
-
        /* clipboard specific settings*/
        BOOL TextOnly;
        UINT32 MaxTextLength;
index 1f4a42e..361b1f1 100644 (file)
 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);
index c6e1173..227d2a2 100644 (file)
@@ -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 (file)
index 121a839..0000000
+++ /dev/null
@@ -1,218 +0,0 @@
-/**
- * FreeRDP: A Remote Desktop Protocol Implementation
- * FreeRDP Proxy Server
- *
- * Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
- * Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
- * Copyright 2019 Idan Freiberg <speidy@gmail.com>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <assert.h>
-
-#include <winpr/wlog.h>
-#include <winpr/library.h>
-#include <freerdp/api.h>
-
-#include "pf_log.h"
-#include "pf_filters.h"
-
-#define TAG PROXY_TAG("filters")
-#define FILTER_INIT_METHOD "filter_init"
-
-static const char* FILTER_RESULT_STRINGS[] =
-{
-       "FILTER_PASS",
-       "FILTER_DROP",
-       "FILTER_IGNORE",
-};
-
-static const char* EVENT_TYPE_STRINGS[] =
-{
-       "KEYBOARD_EVENT",
-       "MOUSE_EVENT",
-};
-
-static const char* pf_filters_get_filter_result_string(PF_FILTER_RESULT result)
-{
-       if (result >= FILTER_PASS && result <= FILTER_IGNORE)
-               return FILTER_RESULT_STRINGS[result];
-       else
-               return "FILTER_UNKNOWN";
-}
-
-static const char* pf_filters_get_event_type_string(PF_FILTER_TYPE result)
-{
-       if (result >= FILTER_TYPE_KEYBOARD && result <= FILTER_TYPE_MOUSE)
-               return EVENT_TYPE_STRINGS[result];
-       else
-               return "EVENT_UNKNOWN";
-}
-
-BOOL pf_filters_init(filters_list** list)
-{
-       if (list == NULL)
-       {
-               WLog_ERR(TAG, "pf_filters_init(): list == NULL");
-               return FALSE;
-       }
-
-       *list = ArrayList_New(FALSE);
-
-       if (*list == NULL)
-       {
-               WLog_ERR(TAG, "pf_filters_init(): ArrayList_New failed!");
-               return FALSE;
-       }
-
-       return TRUE;
-}
-
-PF_FILTER_RESULT pf_filters_run_by_type(filters_list* list, PF_FILTER_TYPE type,
-                                        connectionInfo* info,
-                                        void* param)
-{
-       proxyFilter* filter;
-       proxyEvents* events;
-       PF_FILTER_RESULT result = FILTER_PASS;
-       const size_t count = (size_t) ArrayList_Count(list);
-       size_t index;
-
-       for (index = 0; index < count; index++)
-       {
-               filter = (proxyFilter*) ArrayList_GetItem(list, index);
-               events = filter->events;
-               WLog_VRB(TAG, "pf_filters_run_by_type(): Running filter: %s", filter->name);
-
-               switch (type)
-               {
-                       case FILTER_TYPE_KEYBOARD:
-                               IFCALLRET(events->KeyboardEvent, result, info, param);
-                               break;
-
-                       case FILTER_TYPE_MOUSE:
-                               IFCALLRET(events->MouseEvent, result, info, param);
-                               break;
-               }
-
-               if (result != FILTER_PASS)
-               {
-                       /* Filter returned FILTER_DROP or FILTER_IGNORE. There's no need to call next filters. */
-                       WLog_INFO(TAG, "Filter %s [%s] returned %s", filter->name,
-                                 pf_filters_get_event_type_string(type), pf_filters_get_filter_result_string(result));
-                       return result;
-               }
-       }
-
-       /* all filters returned FILTER_PASS */
-       return FILTER_PASS;
-}
-
-static void pf_filters_filter_free(proxyFilter* filter)
-{
-       if (!filter)
-               return;
-
-       if (filter->handle)
-               FreeLibrary(filter->handle);
-
-       free(filter->name);
-       free(filter->events);
-       free(filter);
-}
-
-void pf_filters_unregister_all(filters_list* list)
-{
-       size_t count;
-       size_t index;
-
-       if (list == NULL)
-               return;
-       
-       count = (size_t) ArrayList_Count(list);
-
-       for (index = 0; index < count; index++)
-       {
-               proxyFilter* filter = (proxyFilter*) ArrayList_GetItem(list, index);
-               WLog_DBG(TAG, "pf_filters_unregister_all(): freeing filter: %s", filter->name);
-               pf_filters_filter_free(filter);
-       }
-
-       ArrayList_Free(list);
-}
-
-BOOL pf_filters_register_new(filters_list* list, const char* module_path, const char* filter_name)
-{
-       proxyEvents* events = NULL;
-       proxyFilter* filter = NULL;
-       HMODULE handle = NULL;
-       filterInitFn fn;
-
-       assert(list != NULL);
-       handle = LoadLibraryA(module_path);
-
-       if (handle == NULL)
-       {
-               WLog_ERR(TAG, "pf_filters_register_new(): failed loading external module: %s", module_path);
-               return FALSE;
-       }
-
-       if (!(fn = (filterInitFn) GetProcAddress(handle, FILTER_INIT_METHOD)))
-       {
-               WLog_ERR(TAG, "pf_filters_register_new(): GetProcAddress failed while loading %s", module_path);
-               goto error;
-       }
-
-       filter = (proxyFilter*) malloc(sizeof(proxyFilter));
-
-       if (filter == NULL)
-       {
-               WLog_ERR(TAG, "pf_filters_register_new(): malloc failed");
-               goto error;
-       }
-
-       events = calloc(1, sizeof(proxyEvents));
-
-       if (events == NULL)
-       {
-               WLog_ERR(TAG, "pf_filters_register_new(): calloc proxyEvents failed");
-               goto error;
-       }
-
-       if (!fn(events))
-       {
-               WLog_ERR(TAG, "pf_filters_register_new(): failed calling external filter_init: %s", module_path);
-               goto error;
-       }
-
-       filter->handle = handle;
-       filter->name = _strdup(filter_name);
-       filter->events = events;
-       filter->enabled = TRUE;
-
-       if (ArrayList_Add(list, filter) < 0)
-       {
-               WLog_ERR(TAG, "pf_filters_register_new(): failed adding filter to list: %s", module_path);
-               goto error;
-       }
-
-       return TRUE;
-error:
-
-       if (handle)
-               FreeLibrary(handle);
-
-       pf_filters_filter_free(filter);
-       return FALSE;
-}
diff --git a/server/proxy/pf_filters.h b/server/proxy/pf_filters.h
deleted file mode 100644 (file)
index d88cf72..0000000
+++ /dev/null
@@ -1,74 +0,0 @@
-/**
- * FreeRDP: A Remote Desktop Protocol Implementation
- * FreeRDP Proxy Server
- *
- * Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
- * Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
- * Copyright 2019 Idan Freiberg <speidy@gmail.com>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef FREERDP_SERVER_PROXY_FILTERS_H
-#define FREERDP_SERVER_PROXY_FILTERS_H
-
-#include <winpr/wtypes.h>
-#include <winpr/collections.h>
-
-#include "filters/filters_api.h"
-
-/* filter init method */
-typedef BOOL (*filterInitFn)(proxyEvents* events);
-
-typedef wArrayList filters_list;
-typedef struct proxy_filter proxyFilter;
-
-typedef enum _PF_FILTER_TYPE PF_FILTER_TYPE;
-enum _PF_FILTER_TYPE
-{
-       FILTER_TYPE_KEYBOARD,
-       FILTER_TYPE_MOUSE
-};
-
-struct proxy_filter
-{
-       /* Handle to the loaded library. Used for freeing the library */
-       HMODULE handle;
-
-       char* name;
-       BOOL enabled;
-       proxyEvents* events;
-};
-
-BOOL pf_filters_init(filters_list** list);
-BOOL pf_filters_register_new(filters_list* list, const char* module_path, const char* filter_name);
-PF_FILTER_RESULT pf_filters_run_by_type(filters_list* list, PF_FILTER_TYPE type,
-                                        connectionInfo* info,
-                                        void* param);
-void pf_filters_unregister_all(filters_list* list);
-
-#define RUN_FILTER(_filters,_type,_conn_info,_event_info,_ret,_cb,...) do { \
-                       switch(pf_filters_run_by_type(_filters,_type,_conn_info,_event_info)) { \
-                               case FILTER_PASS:           \
-                                       _ret = _cb(__VA_ARGS__);       \
-                                       break; \
-                               case FILTER_IGNORE:       \
-                                       _ret = TRUE;   \
-                                       break; \
-                               case FILTER_DROP:       \
-                               default: \
-                                       _ret = FALSE;   \
-                       } \
-               } while(0)
-
-#endif /* FREERDP_SERVER_PROXY_FILTERS_H */
diff --git a/server/proxy/pf_modules.c b/server/proxy/pf_modules.c
new file mode 100644 (file)
index 0000000..4a68a59
--- /dev/null
@@ -0,0 +1,350 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * FreeRDP Proxy Server modules API
+ *
+ * Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
+ * Copyright 2019 Idan Freiberg <speidy@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+
+#include <winpr/wlog.h>
+#include <winpr/library.h>
+#include <freerdp/api.h>
+
+#include "pf_log.h"
+#include "pf_modules.h"
+#include "pf_context.h"
+
+#define TAG PROXY_TAG("modules")
+
+#define MODULE_INIT_METHOD "module_init"
+#define MODULE_EXIT_METHOD "module_exit"
+
+static modules_list* proxy_modules = NULL;
+
+/* module init/exit methods */
+typedef BOOL (*moduleInitFn)(moduleOperations* ops);
+typedef BOOL (*moduleExitFn)(moduleOperations* ops);
+
+static const char* FILTER_TYPE_STRINGS[] = {
+       "KEYBOARD_EVENT",
+       "MOUSE_EVENT",
+};
+
+static const char* HOOK_TYPE_STRINGS[] = {
+       "CLIENT_PRE_CONNECT",
+       "SERVER_CHANNELS_INIT",
+       "SERVER_CHANNELS_FREE",
+};
+
+static const char* pf_modules_get_filter_type_string(PF_FILTER_TYPE result)
+{
+       if (result >= FILTER_TYPE_KEYBOARD && result <= FILTER_TYPE_MOUSE)
+               return FILTER_TYPE_STRINGS[result];
+       else
+               return "FILTER_UNKNOWN";
+}
+
+static const char* pf_modules_get_hook_type_string(PF_HOOK_TYPE result)
+{
+       if (result >= HOOK_TYPE_CLIENT_PRE_CONNECT && result <= HOOK_TYPE_SERVER_CHANNELS_FREE)
+               return HOOK_TYPE_STRINGS[result];
+       else
+               return "HOOK_UNKNOWN";
+}
+
+BOOL pf_modules_init(void)
+{
+       proxy_modules = ArrayList_New(FALSE);
+
+       if (proxy_modules == NULL)
+       {
+               WLog_ERR(TAG, "pf_modules_init(): ArrayList_New failed!");
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+/*
+ * runs all hooks of type `type`.
+ *
+ * @type: hook type to run.
+ * @server: pointer of server's rdpContext struct of the current session.
+ */
+BOOL pf_modules_run_hook(PF_HOOK_TYPE type, rdpContext* context)
+{
+
+       proxyModule* module;
+       moduleOperations* ops;
+       BOOL ok = TRUE;
+       const size_t count = (size_t)ArrayList_Count(proxy_modules);
+       size_t index;
+
+       for (index = 0; index < count; index++)
+       {
+               module = (proxyModule*)ArrayList_GetItem(proxy_modules, index);
+               ops = module->ops;
+               WLog_VRB(TAG, "[%s]: Running module %s, hook %s", __FUNCTION__, module->name,
+                        pf_modules_get_hook_type_string(type));
+
+               switch (type)
+               {
+               case HOOK_TYPE_CLIENT_PRE_CONNECT:
+                       IFCALLRET(ops->ClientPreConnect, ok, ops, context);
+                       break;
+
+               case HOOK_TYPE_SERVER_CHANNELS_INIT:
+                       IFCALLRET(ops->ServerChannelsInit, ok, ops, context);
+                       break;
+
+               case HOOK_TYPE_SERVER_CHANNELS_FREE:
+                       IFCALLRET(ops->ServerChannelsFree, ok, ops, context);
+                       break;
+               }
+
+               if (!ok)
+               {
+                       WLog_INFO(TAG, "Module %s, hook %s failed!", module->name,
+                                 pf_modules_get_hook_type_string(type));
+                       return FALSE;
+               }
+       }
+
+       return TRUE;
+}
+
+/*
+ * runs all filters of type `type`.
+ *
+ * @type: filter type to run.
+ * @server: pointer of server's rdpContext struct of the current session.
+ */
+BOOL pf_modules_run_filter(PF_FILTER_TYPE type, rdpContext* server, void* param)
+{
+       proxyModule* module;
+       moduleOperations* ops;
+       BOOL result = TRUE;
+       const size_t count = (size_t)ArrayList_Count(proxy_modules);
+       size_t index;
+
+       for (index = 0; index < count; index++)
+       {
+               module = (proxyModule*)ArrayList_GetItem(proxy_modules, index);
+               ops = module->ops;
+               WLog_VRB(TAG, "[%s]: running filter: %s", __FUNCTION__, module->name);
+
+               switch (type)
+               {
+               case FILTER_TYPE_KEYBOARD:
+                       IFCALLRET(ops->KeyboardEvent, result, ops, server, param);
+                       break;
+
+               case FILTER_TYPE_MOUSE:
+                       IFCALLRET(ops->MouseEvent, result, ops, server, param);
+                       break;
+               }
+
+               if (!result)
+               {
+                       /* current filter return FALSE, no need to run other filters. */
+                       WLog_INFO(TAG, "module %s, filter type [%s] returned FALSE", module->name,
+                                 pf_modules_get_filter_type_string(type));
+                       return result;
+               }
+       }
+
+       /* all filters returned TRUE */
+       return TRUE;
+}
+
+static void pf_modules_module_free(proxyModule* module)
+{
+       moduleExitFn exitFn;
+
+       assert(module);
+       assert(module->handle);
+
+       exitFn = (moduleExitFn)GetProcAddress(module->handle, MODULE_EXIT_METHOD);
+
+       if (!exitFn)
+       {
+               WLog_ERR(TAG, "[%s]: GetProcAddress module_exit for %s failed!", __FUNCTION__,
+                        module->name);
+       }
+       else
+       {
+               if (!exitFn(module->ops))
+               {
+                       WLog_ERR(TAG, "[%s]: module_exit failed for %s!", __FUNCTION__, module->name);
+               }
+       }
+
+       FreeLibrary(module->handle);
+       module->handle = NULL;
+
+       free(module->name);
+       free(module->ops);
+       free(module);
+}
+
+void pf_modules_free(void)
+{
+       size_t index, count;
+
+       if (proxy_modules == NULL)
+               return;
+
+       count = (size_t)ArrayList_Count(proxy_modules);
+
+       for (index = 0; index < count; index++)
+       {
+               proxyModule* module = (proxyModule*)ArrayList_GetItem(proxy_modules, index);
+               WLog_INFO(TAG, "[%s]: freeing module: %s", __FUNCTION__, module->name);
+               pf_modules_module_free(module);
+       }
+
+       ArrayList_Free(proxy_modules);
+}
+
+/*
+ * stores per-session data needed by module.
+ *
+ * @context: current session server's rdpContext instance.
+ * @info: pointer to per-session data.
+ */
+static BOOL pf_modules_set_session_data(moduleOperations* module, rdpContext* context, void* data)
+{
+       pServerContext* ps;
+
+       assert(module);
+       assert(context);
+
+       if (data == NULL) /* no need to store anything */
+               return FALSE;
+
+       ps = (pServerContext*) context;
+       if (HashTable_Add(ps->modules_info, (void*)module, data) < 0)
+       {
+               WLog_ERR(TAG, "[%s]: HashTable_Add failed!");
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+/*
+ * returns per-session data needed by module.
+ *
+ * @context: current session server's rdpContext instance.
+ * if there's no data related to `module` in `context` (current session), a NULL will be returned.
+ */
+static void* pf_modules_get_session_data(moduleOperations* module, rdpContext* context)
+{
+       pServerContext* ps;
+
+       assert(module);
+       assert(context);
+
+       ps = (pServerContext*)context;
+       return HashTable_GetItemValue(ps->modules_info, module);
+}
+
+static void pf_modules_abort_connect(moduleOperations* module, rdpContext* context)
+{
+       pServerContext* ps;
+
+       assert(module);
+       assert(context);
+
+       WLog_INFO(TAG, "%s is called!", __FUNCTION__);
+
+       ps = (pServerContext*)context;
+       proxy_data_abort_connect(ps->pdata);
+}
+
+BOOL pf_modules_register_new(const char* module_path, const char* module_name)
+{
+       moduleOperations* ops = NULL;
+       proxyModule* module = NULL;
+       HMODULE handle = NULL;
+       moduleInitFn fn;
+
+       assert(proxy_modules != NULL);
+       handle = LoadLibraryA(module_path);
+
+       if (handle == NULL)
+       {
+               WLog_ERR(TAG, "pf_modules_register_new(): failed loading external module: %s", module_path);
+               return FALSE;
+       }
+
+       if (!(fn = (moduleInitFn)GetProcAddress(handle, MODULE_INIT_METHOD)))
+       {
+               WLog_ERR(TAG, "pf_modules_register_new(): GetProcAddress failed while loading %s",
+                        module_path);
+               goto error;
+       }
+
+       module = (proxyModule*)calloc(1, sizeof(proxyModule));
+
+       if (module == NULL)
+       {
+               WLog_ERR(TAG, "pf_modules_register_new(): malloc failed");
+               goto error;
+       }
+
+       ops = calloc(1, sizeof(moduleOperations));
+
+       if (ops == NULL)
+       {
+               WLog_ERR(TAG, "pf_modules_register_new(): calloc moduleOperations failed");
+               goto error;
+       }
+
+       ops->AbortConnect = pf_modules_abort_connect;
+       ops->SetSessionData = pf_modules_set_session_data;
+       ops->GetSessionData = pf_modules_get_session_data;
+
+       if (!fn(ops))
+       {
+               WLog_ERR(TAG, "pf_modules_register_new(): failed to initialize module %s", module_path);
+               goto error;
+       }
+
+       module->name = _strdup(module_name);
+       if (!module->name)
+       {
+               WLog_ERR(TAG, "pf_modules_register_new(): _strdup failed while loading %s", module_path);
+               goto error;
+       }
+
+       module->handle = handle;
+       module->ops = ops;
+       module->enabled = TRUE;
+
+       if (ArrayList_Add(proxy_modules, module) < 0)
+       {
+               WLog_ERR(TAG, "pf_modules_register_new(): failed adding module to list: %s", module_path);
+               goto error;
+       }
+
+       return TRUE;
+
+error:
+       pf_modules_module_free(module);
+       return FALSE;
+}
\ No newline at end of file
diff --git a/server/proxy/pf_modules.h b/server/proxy/pf_modules.h
new file mode 100644 (file)
index 0000000..e6b6911
--- /dev/null
@@ -0,0 +1,65 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ * FreeRDP Proxy Server
+ *
+ * Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
+ * Copyright 2019 Idan Freiberg <speidy@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef FREERDP_SERVER_PROXY_MODULES_H
+#define FREERDP_SERVER_PROXY_MODULES_H
+
+#include <winpr/wtypes.h>
+#include <winpr/collections.h>
+
+#include "modules/modules_api.h"
+
+typedef wArrayList modules_list;
+typedef struct proxy_module proxyModule;
+
+typedef enum _PF_FILTER_TYPE PF_FILTER_TYPE;
+enum _PF_FILTER_TYPE
+{
+       FILTER_TYPE_KEYBOARD,
+       FILTER_TYPE_MOUSE
+};
+
+typedef enum _PF_HOOK_TYPE PF_HOOK_TYPE;
+enum _PF_HOOK_TYPE
+{
+       HOOK_TYPE_CLIENT_PRE_CONNECT,
+       HOOK_TYPE_SERVER_CHANNELS_INIT,
+       HOOK_TYPE_SERVER_CHANNELS_FREE,
+};
+
+struct proxy_module
+{
+       /* Handle to the loaded library. Used for freeing the library */
+       HMODULE handle;
+
+       char* name;
+       BOOL enabled;
+       moduleOperations* ops;
+};
+
+BOOL pf_modules_init(void);
+BOOL pf_modules_register_new(const char* module_path, const char* module_name);
+
+BOOL pf_modules_run_filter(PF_FILTER_TYPE type, rdpContext* server, void* param);
+BOOL pf_modules_run_hook(PF_HOOK_TYPE type, rdpContext* context);
+
+void pf_modules_free(void);
+
+#endif /* FREERDP_SERVER_PROXY_MODULES_H */