From b907324009b0af6c9fee449e61e6fbcf5d5d865e Mon Sep 17 00:00:00 2001 From: Mati Shabtay <35010736+m4ntis@users.noreply.github.com> Date: Fri, 17 May 2019 15:32:54 +0300 Subject: [PATCH] First version of an RDP proxy (#5372) * server: Add proxy dir with barebones server * sever/proxy: Remove licensing * server/proxy: Add client files * server/proxy: rm binary * server/proxy: Formatting * server/proxy: Fixed includes and added basic client creation functionality * server/proxy: Remove licensing and fix ifndef * proxy/server: Fix cmake indentation * server/proxy: Fix licensing * server/proxy: Forward connection on peer_post_connect * server/proxy: Fix function signature * server/proxy: Changed function signature of proxy_client_start * server/proxy: Now peer_post_connect calls proxy_client_start in a new thread * pfreerdp.c: Clean up useless comments and logs * server/proxy: Fix license * server/proxy: Remove all non-connection related data from proxy_context * server/proxy: Move Log Tag definition to pf_log.h * server/proxy: Move context definition to pf_context * server/proxy: Delete pfreerdp.h * pfreerdp.c: Move context callbacks to pf_context.c * server/proxy: Update CMakeLists.txt * pf_channels: Use new proxy context API * pf_client: Move context to pf_context * pf_client.c: Remove unnessecary event handling * server/proxy: Formatting * proxy/server: Move server logic to pf_server.c * server/proxy: Handle client disconnection * Merge stash * pf_server.c: Open GFX Connection to client * server: CMakeLists: build proxy along with other servers * server: proxy: get target server from rdpNego->RoutingToken Iv'e omitted a check from which im not sure is right. Should check in docs * server/proxy: Handle remote server -> client disconnection * server/proxy: Move common function to pf_common.c * server/proxy: Move common function to pf_common.c * rdpgfx.h: Add reference to freerdp.h for rdpContext * pf_channels: Pipe GFX on channel connection * server/proxy: Add pf_rdpgfx for proxy gfx callbacks * pf_client: Declare dynvc and gfx capabilities on connection * server/proxy: Add graphics callbacks * server/proxy: Add graphics callbacks * pf_server: Listen to channel events * Pass user settings to server * pf_server: Proxy mouse events * fixup! server/proxy: Add graphics callbacks * pf_client: Fix setting initialization * Merge feat/proxy-gfx to feat/proxy * pf_server: Fix double freed credentials * server/proxy: Remove unnecessary call to freerdp_client_settings_parse_command_line * server/proxy: Refactor re-activation code * server/proxy: Run format scripts * server/proxy: Fix segfault when post_disconnect return FALSE * server/proxy: Refactor proxy_settings_mirror * server/proxy: Redirect credentials * server/proxy: move proxy_settings_mirror to pf_common.c * server/proxy: Redirect desktop_resize event * pf_client: Remove interactive CLI auth methods * fixup! server/proxy: Redirect credentials * server/proxy: Rename proxy_mirror_settings to pf_common_copy_settings * pf_server.c: Fixed non-freed context When the disconnection is forced by the target server, the function `pf_server_handle_client_disconnection` isn't called. Therefore, the context of the connection between the proxy to tagrget isn't freed. * fixup! pf_server.c: Fixed non-freed context * pf_client: Prefix all client methods with pf_client * pf_context: Add init client to proxy context method * pf_server: Confirm all GFX caps regardless of settings * pf_server: Prefix all methods with pf_server * pf_server: Move variable decleration to start of method * pf_server: Fix client setting * pf_server: Fix GFX init method * pf_server: Move variable decleration to start of methods * server/proxy: Formatting * Merge feat/proxy * pf_server: Proxy synchronize event * pf_server: Proxy refresh rect update events * pf_server: Proxy suppress output messages * server/proxy: Fix licensing * server/proxy: Move client input callbacks to pf_input * server/proxy: Move client update callbacks to pf_update * server/proxy: Fix non-terminated target host string * Feat/proxy config (#2) * server/proxy: Add config loading support * server/proxy: Add config file * server/proxy: Format code * server/proxy: Code refactor, rename update_register_callbacks and input_register_callbacks * server/proxy: Update config file * server/proxy: Remove config.ini from root directory * Remove comment from config file * server/proxy: Fix leak in pf_server_load_config * server/proxy: Add rdpServerProxy struct and embed it in proxyContext * server/proxy: Load configuration and pass it inside every proxyContext instance * server/proxy: Move rdpProxyServer to proxy.h * server/proxy: Use configuration while proxying input events * server/proxy: Update CMakeLists * server/proxy: Refactor pf_input.c * server/proxy: Add AllowedChannels, DeniedChannels in configuration * server/proxy: Remove unnecessary variable from parse_channels_from_str * server/proxy: Update config file * server/proxy: config: Rename to * server/proxy: config: Add mode - blacklist/whitelist * server/proxy: Refactor, fix NULL deref * server/proxy: Add license to proxy.h * server/proxy: Fix newline in pf_config.c * server/proxy: config: Rename Mode to WhitelistMode * Add target in config. Add checks for configuration validity (#3) * Add target in config. Add checks for configuration validity * Update config file * libfreerdp: nego: revert commented out check of routingToken length * pf_server: Fix target host info from RoutingToken * pf_server: Remove hardcoded lenght of routing token prefix * Feat/refactor context (#8) * Refactor main structs * Update CMakeLists.txt * pf_server.c: Free pdata at the end of the connection * Run format scripts * Rename tf to pc * Fix licenses * pf_server: Refactor names of structs and functions * proxy: gfx: sync caps (#4) * proxy: gfx: sync caps * proxy: gfx: sync caps, hook gfx client's OnClose() call and close server resources * fixup! Feat/refactor context (#8) * fixup! fixup! Feat/refactor context (#8) * rdpgfx/client: Fix rdpgfx_recv_caps_confirm_pdu caps set length parsing * Run format scripts * proxy config.ini: Change default port to 3389 * pf_rdpgfx: Limit caps version to freerdp's supported versions * Gfx OnOpen() wait for dynvc ready (#10) * proxy/gfx: Wait for dynvc ready state before open * pf_channels: Initialize pc->gfx * pf_rdpgfx: Add log and fix comments * rdpgfx: Fix GFX v10.6 PDUs parsing and naming according to the spec * pf_rdpgfx: Proxy rdpgfx v10.6 PDUs * gfx client: Publish FrameAck sending and add auto ack flag * proxy/gfx: Forward frame ack messages * pf_context: Forward domain on connection * pf_rdpgfx: Change max supported caps to 10.6 * proxy: Update config * server/proxy: Use configuration in pf_server_handle_client * rdpgfx/client: Fix size of surface_to_scaled_window, surface_to_window * pf_rdpgfx: Fix formatting * pf_server.c: Fix comments * Move pf_server_rdpgfx_init to pf_rdpgfx * server/proxy/CMakeLists.txt: Fix formatting * pf_client.c: Add comment in proxy_server_reactivate * Fixed const correctness of gfx function pointer Signed-off-by: Mati Shabtay * server: proxy: update copyright * server: proxy: wrap rdpNego and add a getter for routing token * Refactor routing token getter (#14) * Refactor routing token getter * pf_server_parse_target_from_routing_token change routing_token_length to be DWORD * libfreerdp/core/nego.c: Run format script * pf_server: Run format script * server/proxy: Fix os msbuild tests * pf_channels.c: Remove unused channels * pf_client: Remove unused callbacks * proxy: Remove encomsp callbacks from proxy's client * client/rdpgfx_main.c: Fix msbuild test * pf_config.c: Use StrSep instead of strsep for Windows builds * Removed nego struct from direct access. Signed-off-by: Mati Shabtay * proxy: Rename binary to freerdp-proxy * rdpgfx_main.c: Revert unwanted double change to send_supported_caps * Cleaned up proxy server code. * All internal functions static * Added simple command line argument to supply a config file * Silence compiler warnings Signed-off-by: kubistika --- .gitignore | 1 + include/freerdp/freerdp.h | 2 + include/freerdp/server/rdpgfx.h | 1 + libfreerdp/core/freerdp.c | 8 + libfreerdp/core/nego.c | 9 + libfreerdp/core/nego.h | 1 + server/.gitignore | 1 + server/CMakeLists.txt | 1 + server/proxy/CMakeLists.txt | 76 ++++++ server/proxy/config.ini | 33 +++ server/proxy/freerdp_proxy.c | 77 ++++++ server/proxy/pf_channels.c | 80 ++++++ server/proxy/pf_channels.h | 33 +++ server/proxy/pf_client.c | 415 ++++++++++++++++++++++++++++++ server/proxy/pf_client.h | 31 +++ server/proxy/pf_common.c | 107 ++++++++ server/proxy/pf_common.h | 31 +++ server/proxy/pf_config.c | 188 ++++++++++++++ server/proxy/pf_config.h | 71 ++++++ server/proxy/pf_context.c | 81 ++++++ server/proxy/pf_context.h | 83 ++++++ server/proxy/pf_gdi.c | 151 +++++++++++ server/proxy/pf_gdi.h | 29 +++ server/proxy/pf_graphics.c | 171 +++++++++++++ server/proxy/pf_graphics.h | 31 +++ server/proxy/pf_input.c | 93 +++++++ server/proxy/pf_input.h | 29 +++ server/proxy/pf_log.h | 29 +++ server/proxy/pf_rdpgfx.c | 319 +++++++++++++++++++++++ server/proxy/pf_rdpgfx.h | 34 +++ server/proxy/pf_server.c | 430 ++++++++++++++++++++++++++++++++ server/proxy/pf_server.h | 29 +++ server/proxy/pf_update.c | 45 ++++ server/proxy/pf_update.h | 29 +++ server/proxy/server.crt | 17 ++ server/proxy/server.key | 28 +++ 36 files changed, 2794 insertions(+) create mode 100644 server/proxy/CMakeLists.txt create mode 100644 server/proxy/config.ini create mode 100644 server/proxy/freerdp_proxy.c create mode 100644 server/proxy/pf_channels.c create mode 100644 server/proxy/pf_channels.h create mode 100644 server/proxy/pf_client.c create mode 100644 server/proxy/pf_client.h create mode 100644 server/proxy/pf_common.c create mode 100644 server/proxy/pf_common.h create mode 100644 server/proxy/pf_config.c create mode 100644 server/proxy/pf_config.h create mode 100644 server/proxy/pf_context.c create mode 100644 server/proxy/pf_context.h create mode 100644 server/proxy/pf_gdi.c create mode 100644 server/proxy/pf_gdi.h create mode 100644 server/proxy/pf_graphics.c create mode 100644 server/proxy/pf_graphics.h create mode 100644 server/proxy/pf_input.c create mode 100644 server/proxy/pf_input.h create mode 100644 server/proxy/pf_log.h create mode 100644 server/proxy/pf_rdpgfx.c create mode 100644 server/proxy/pf_rdpgfx.h create mode 100644 server/proxy/pf_server.c create mode 100644 server/proxy/pf_server.h create mode 100644 server/proxy/pf_update.c create mode 100644 server/proxy/pf_update.h create mode 100644 server/proxy/server.crt create mode 100644 server/proxy/server.key diff --git a/.gitignore b/.gitignore index affdc42f8..c844922f6 100644 --- a/.gitignore +++ b/.gitignore @@ -104,6 +104,7 @@ client/Sample/sfreerdp client/Wayland/wlfreerdp server/Sample/sfreerdp-server server/X11/xfreerdp-server +server/proxy/freerdp-proxy xcode libfreerdp/codec/test/TestOpenH264ASM diff --git a/include/freerdp/freerdp.h b/include/freerdp/freerdp.h index 8b76c9e72..a0c34c149 100644 --- a/include/freerdp/freerdp.h +++ b/include/freerdp/freerdp.h @@ -490,6 +490,8 @@ FREERDP_API void setChannelError(rdpContext* context, UINT errorNum, char* description); FREERDP_API BOOL checkChannelErrorEvent(rdpContext* context); +FREERDP_API const char* freerdp_nego_get_routing_token(rdpContext* context, DWORD* length); + #ifdef __cplusplus } #endif diff --git a/include/freerdp/server/rdpgfx.h b/include/freerdp/server/rdpgfx.h index e9e7f6277..c8a2bf53b 100644 --- a/include/freerdp/server/rdpgfx.h +++ b/include/freerdp/server/rdpgfx.h @@ -21,6 +21,7 @@ #define FREERDP_CHANNEL_RDPGFX_SERVER_RDPGFX_H #include +#include typedef struct _rdpgfx_server_context RdpgfxServerContext; typedef struct _rdpgfx_server_private RdpgfxServerPrivate; diff --git a/libfreerdp/core/freerdp.c b/libfreerdp/core/freerdp.c index cf8654818..a2de6c8f0 100644 --- a/libfreerdp/core/freerdp.c +++ b/libfreerdp/core/freerdp.c @@ -1084,3 +1084,11 @@ void setChannelError(rdpContext* context, UINT errorNum, char* description) strncpy(context->errorDescription, description, 499); SetEvent(context->channelErrorEvent); } + +const char* freerdp_nego_get_routing_token(rdpContext* context, DWORD* length) +{ + if (!context || !context->rdp) + return NULL; + + return (const char*)nego_get_routing_token(context->rdp->nego, length); +} diff --git a/libfreerdp/core/nego.c b/libfreerdp/core/nego.c index 723881806..9abd9a9e6 100644 --- a/libfreerdp/core/nego.c +++ b/libfreerdp/core/nego.c @@ -1440,3 +1440,12 @@ void nego_free_nla(rdpNego* nego) nla_free(nego->transport->nla); nego->transport->nla = NULL; } + +const BYTE* nego_get_routing_token(rdpNego* nego, DWORD* RoutingTokenLength) +{ + if (!nego) + return NULL; + if (RoutingTokenLength) + *RoutingTokenLength = nego->RoutingTokenLength; + return nego->RoutingToken; +} diff --git a/libfreerdp/core/nego.h b/libfreerdp/core/nego.h index 4153098bc..22f9d71d1 100644 --- a/libfreerdp/core/nego.h +++ b/libfreerdp/core/nego.h @@ -119,6 +119,7 @@ FREERDP_LOCAL void nego_enable_rdp(rdpNego* nego, BOOL enable_rdp); FREERDP_LOCAL void nego_enable_tls(rdpNego* nego, BOOL enable_tls); FREERDP_LOCAL void nego_enable_nla(rdpNego* nego, BOOL enable_nla); FREERDP_LOCAL void nego_enable_ext(rdpNego* nego, BOOL enable_ext); +FREERDP_LOCAL const BYTE* nego_get_routing_token(rdpNego* nego, DWORD* RoutingTokenLength); FREERDP_LOCAL BOOL nego_set_routing_token(rdpNego* nego, BYTE* RoutingToken, DWORD RoutingTokenLength); FREERDP_LOCAL BOOL nego_set_cookie(rdpNego* nego, char* cookie); diff --git a/server/.gitignore b/server/.gitignore index 5685908a6..8c5ddfda1 100644 --- a/server/.gitignore +++ b/server/.gitignore @@ -1,4 +1,5 @@ /* +!/proxy !/common !/Mac !/Sample diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index feb979981..327b52297 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -19,6 +19,7 @@ add_subdirectory(common) add_subdirectory(shadow) +add_subdirectory(proxy) if(FREERDP_VENDOR) if(WITH_SAMPLE) diff --git a/server/proxy/CMakeLists.txt b/server/proxy/CMakeLists.txt new file mode 100644 index 000000000..6d749f577 --- /dev/null +++ b/server/proxy/CMakeLists.txt @@ -0,0 +1,76 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP Proxy Server +# +# Copyright 2019 Mati Shabtay +# 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. + +set(MODULE_NAME "freerdp-proxy") +set(MODULE_PREFIX "FREERDP_SERVER_PROXY") + +set(${MODULE_PREFIX}_SRCS + freerdp_proxy.c + pf_context.c + pf_context.h + pf_channels.c + pf_channels.h + pf_client.c + pf_client.h + pf_input.c + pf_input.h + pf_update.c + pf_update.h + pf_rdpgfx.c + pf_rdpgfx.h + pf_server.c + pf_server.h + pf_common.c + pf_common.h + pf_gdi.c + pf_gdi.h + pf_config.c + pf_config.h + pf_graphics.c + pf_graphics.h + pf_log.h) + +# On windows create dll version information. +# Vendor, product and year are already set in top level CMakeLists.txt +if (WIN32) + set (RC_VERSION_MAJOR ${FREERDP_VERSION_MAJOR}) + set (RC_VERSION_MINOR ${FREERDP_VERSION_MINOR}) + set (RC_VERSION_BUILD ${FREERDP_VERSION_REVISION}) + set (RC_VERSION_FILE "${MODULE_NAME}${CMAKE_EXECUTABLE_SUFFIX}" ) + + configure_file( + ${CMAKE_SOURCE_DIR}/cmake/WindowsDLLVersion.rc.in + ${CMAKE_CURRENT_BINARY_DIR}/version.rc + @ONLY) + + set ( ${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS} ${CMAKE_CURRENT_BINARY_DIR}/version.rc) +endif() + +add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS}) + +set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} freerdp-server) +set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} freerdp-client) +set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} winpr freerdp) + +target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS}) +install(TARGETS ${MODULE_NAME} DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT server) +if (WITH_DEBUG_SYMBOLS AND MSVC) + install(FILES ${CMAKE_PDB_BINARY_DIR}/${MODULE_NAME}.pdb DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT symbols) +endif() + +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Server/proxy") diff --git a/server/proxy/config.ini b/server/proxy/config.ini new file mode 100644 index 000000000..3a04dc6d1 --- /dev/null +++ b/server/proxy/config.ini @@ -0,0 +1,33 @@ +[Server] +Host = "0.0.0.0" +Port = 3389 +LocalOnly = 0 + +[Target] +; If this value is set to TRUE, the target server info will be parsed using the +; load balance info setting at runtime. The format is +; "Cookie: msts=", and can be set in an rdp file for windows/mac, +; and the /load-balance-info: CLI option for xfreerdp. Otherwise, the server +; will always connect to the same target, using the configured values of `Host` +; and `Port`. +UseLoadBalanceInfo = 1 +Host = "Target server" +Port = 3389 + +[Input] +Mouse = 1 +Keyboard = 1 + +[Graphics] +GFX = 1 +BitmapUpdate = 1 + +[Security] +NlaSecurity = 0 +TlsSecurity = 1 +RdpSecurity = 1 + +[Channels] +WhitelistMode = 0 +AllowedChannels = "cliprdr,Microsoft::Windows::RDS::Video::Control" +DeniedChannels = "Microsoft::Windows::RDS::Geometry" diff --git a/server/proxy/freerdp_proxy.c b/server/proxy/freerdp_proxy.c new file mode 100644 index 000000000..d85afd44a --- /dev/null +++ b/server/proxy/freerdp_proxy.c @@ -0,0 +1,77 @@ +/** + * 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 "pf_server.h" +#include "pf_config.h" +#include "pf_log.h" + +#define TAG PROXY_TAG("server") + +int main(int argc, char* argv[]) +{ + const char* cfg = "config.ini"; + int status = 0; + DWORD ld; + UINT32 i; + proxyConfig* config = calloc(1, sizeof(proxyConfig)); + + if (!config) + return -1; + + if (argc > 1) + cfg = argv[1]; + + ld = pf_server_load_config(cfg, config); + + switch (ld) + { + case CONFIG_PARSE_SUCCESS: + WLog_DBG(TAG, "Configuration parsed successfully"); + break; + + case CONFIG_PARSE_ERROR: + WLog_ERR(TAG, "An error occured while parsing configuration file, exiting..."); + goto fail; + + case CONFIG_INVALID: + goto fail; + } + + if (config->WhitelistMode) + { + WLog_INFO(TAG, "Channels mode: WHITELIST"); + + for (i = 0; i < config->AllowedChannelsCount; i++) + WLog_INFO(TAG, "Allowing %s", config->AllowedChannels[i]); + } + else + { + WLog_INFO(TAG, "Channels mode: BLACKLIST"); + + for (i = 0; i < config->BlockedChannelsCount; i++) + WLog_INFO(TAG, "Blocking %s", config->BlockedChannels[i]); + } + + status = pf_server_start(config); +fail: + pf_server_config_free(config); + return status; +} diff --git a/server/proxy/pf_channels.c b/server/proxy/pf_channels.c new file mode 100644 index 000000000..36e99caad --- /dev/null +++ b/server/proxy/pf_channels.c @@ -0,0 +1,80 @@ +/** + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include +#include +#include +#include +#include + +#include "pf_channels.h" +#include "pf_client.h" +#include "pf_context.h" +#include "pf_rdpgfx.h" +#include "pf_log.h" + +#define TAG PROXY_TAG("channels") + +void pf_OnChannelConnectedEventHandler(void* context, + ChannelConnectedEventArgs* e) +{ + pClientContext* pc = (pClientContext*) context; + pServerContext* ps = pc->pdata->ps; + RdpgfxClientContext* gfx; + RdpgfxServerContext* server; + WLog_DBG(TAG, "Channel connected: %s", e->name); + + if (strcmp(e->name, RDPEI_DVC_CHANNEL_NAME) == 0) + { + pc->rdpei = (RdpeiClientContext*) e->pInterface; + } + else if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0) + { + gfx = (RdpgfxClientContext*) e->pInterface; + pc->gfx = gfx; + server = ps->gfx; + pf_rdpgfx_pipeline_init(gfx, server, pc->pdata); + } +} + +void pf_OnChannelDisconnectedEventHandler(void* context, + ChannelDisconnectedEventArgs* e) +{ + pClientContext* pc = (pClientContext*) context; + rdpSettings* settings; + settings = ((rdpContext*)pc)->settings; + + if (strcmp(e->name, RDPEI_DVC_CHANNEL_NAME) == 0) + { + pc->rdpei = NULL; + } + else if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0) + { + gdi_graphics_pipeline_uninit(((rdpContext*)pc)->gdi, + (RdpgfxClientContext*) e->pInterface); + } +} diff --git a/server/proxy/pf_channels.h b/server/proxy/pf_channels.h new file mode 100644 index 000000000..023ab0006 --- /dev/null +++ b/server/proxy/pf_channels.h @@ -0,0 +1,33 @@ +/** + * 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_PFCHANNELS_H +#define FREERDP_SERVER_PROXY_PFCHANNELS_H + +#include +#include + +void pf_OnChannelConnectedEventHandler(void* context, + ChannelConnectedEventArgs* e); +void pf_OnChannelDisconnectedEventHandler(void* context, + ChannelDisconnectedEventArgs* e); + +#endif /* FREERDP_SERVER_PROXY_PFCHANNELS_H */ diff --git a/server/proxy/pf_client.c b/server/proxy/pf_client.c new file mode 100644 index 000000000..d03c7b043 --- /dev/null +++ b/server/proxy/pf_client.c @@ -0,0 +1,415 @@ +/** + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "pf_channels.h" +#include "pf_gdi.h" +#include "pf_graphics.h" +#include "pf_common.h" +#include "pf_client.h" +#include "pf_context.h" +#include "pf_log.h" + +#define TAG PROXY_TAG("client") + +/** + * Re-negociate with original client after negociation between the proxy + * and the target has finished. + */ +static void proxy_server_reactivate(rdpContext* client, rdpContext* target) +{ + pf_common_copy_settings(client->settings, target->settings); + /* DesktopResize causes internal function rdp_server_reactivate to be called, + * which causes the reactivation. + */ + client->update->DesktopResize(client); +} + +/** + * This function is called whenever a new frame starts. + * It can be used to reset invalidated areas. + */ +static BOOL pf_client_begin_paint(rdpContext* context) +{ + pClientContext* pc = (pClientContext*) context; + proxyData* pdata = pc->pdata; + rdpContext* ps = (rdpContext*)pdata->ps; + return ps->update->BeginPaint(ps); +} + +/** + * This function is called when the library completed composing a new + * frame. Read out the changed areas and blit them to your output device. + * The image buffer will have the format specified by gdi_init + */ +static BOOL pf_client_end_paint(rdpContext* context) +{ + pClientContext* pc = (pClientContext*) context; + proxyData* pdata = pc->pdata; + rdpContext* ps = (rdpContext*)pdata->ps; + return ps->update->EndPaint(ps); +} + +/** + * Called before a connection is established. + * + * TODO: Take client to proxy settings and use channel whitelist to filter out + * unwanted channels. + */ +static BOOL pf_client_pre_connect(freerdp* instance) +{ + rdpSettings* settings = instance->settings; + settings->OsMajorType = OSMAJORTYPE_UNIX; + settings->OsMinorType = OSMINORTYPE_NATIVE_XSERVER; + /** + * settings->OrderSupport is initialized at this point. + * Only override it if you plan to implement custom order + * callbacks or deactiveate certain features. + */ + /** + * Register the channel listeners. + * They are required to set up / tear down channels if they are loaded. + */ + PubSub_SubscribeChannelConnected(instance->context->pubSub, + pf_OnChannelConnectedEventHandler); + PubSub_SubscribeChannelDisconnected(instance->context->pubSub, + pf_OnChannelDisconnectedEventHandler); + /** + * Load all required plugins / channels / libraries specified by current + * settings. + */ + WLog_INFO(TAG, "Loading addins"); + + if (!freerdp_client_load_addins(instance->context->channels, + instance->settings)) + { + WLog_ERR(TAG, "Failed to load addins"); + return FALSE; + } + + return TRUE; +} + + +static BOOL pf_client_bitmap_update(rdpContext* context, const BITMAP_UPDATE* bitmap) +{ + pClientContext* pc = (pClientContext*) context; + proxyData* pdata = pc->pdata; + rdpContext* ps = (rdpContext*)pdata->ps; + return ps->update->BitmapUpdate(ps, bitmap); +} + +static BOOL pf_client_desktop_resize(rdpContext* context) +{ + pClientContext* pc = (pClientContext*) context; + proxyData* pdata = pc->pdata; + rdpContext* ps = (rdpContext*)pdata->ps; + return ps->update->DesktopResize(ps); +} + +/** + * Called after a RDP connection was successfully established. + * Settings might have changed during negociation of client / server feature + * support. + * + * Set up local framebuffers and painting callbacks. + * If required, register pointer callbacks to change the local mouse cursor + * when hovering over the RDP window + */ +static BOOL pf_client_post_connect(freerdp* instance) +{ + rdpContext* context; + rdpSettings* settings; + rdpUpdate* update; + pClientContext* pc; + rdpContext* ps; + + if (!gdi_init(instance, PIXEL_FORMAT_XRGB32)) + return FALSE; + + context = instance->context; + settings = instance->settings; + update = instance->update; + pc = (pClientContext*) context; + + if (!pf_register_pointer(context->graphics)) + return FALSE; + + if (!settings->SoftwareGdi) + { + if (!pf_register_graphics(context->graphics)) + { + WLog_ERR(TAG, "failed to register graphics"); + return FALSE; + } + + pf_gdi_register_update_callbacks(update); + brush_cache_register_callbacks(instance->update); + glyph_cache_register_callbacks(instance->update); + bitmap_cache_register_callbacks(instance->update); + offscreen_cache_register_callbacks(instance->update); + palette_cache_register_callbacks(instance->update); + } + + update->BeginPaint = pf_client_begin_paint; + update->EndPaint = pf_client_end_paint; + update->BitmapUpdate = pf_client_bitmap_update; + update->DesktopResize = pf_client_desktop_resize; + ps = (rdpContext*) pc->pdata->ps; + proxy_server_reactivate(ps, context); + return TRUE; +} + + +/* This function is called whether a session ends by failure or success. + * Clean up everything allocated by pre_connect and post_connect. + */ +static void pf_client_post_disconnect(freerdp* instance) +{ + pClientContext* context; + proxyData* pdata; + rdpContext* ps; + freerdp_peer* peer; + + if (!instance) + return; + + if (!instance->context) + return; + + context = (pClientContext*) instance->context; + pdata = context->pdata; + PubSub_UnsubscribeChannelConnected(instance->context->pubSub, + pf_OnChannelConnectedEventHandler); + PubSub_UnsubscribeChannelDisconnected(instance->context->pubSub, + pf_OnChannelDisconnectedEventHandler); + gdi_free(instance); + ps = (rdpContext*) pdata->ps; + + if (!pf_common_connection_aborted_by_peer(pdata)) + { + SetEvent(pdata->connectionClosed); + WLog_INFO(TAG, "connectionClosed event is not set; closing connection with client"); + peer = ps->peer; + peer->Disconnect(peer); + } + + /* It's important to avoid calling `freerdp_peer_context_free` and `freerdp_peer_free` here, + * in order to avoid double-free. Those objects will be freed by the server when needed. + */ +} + +/** + * RDP main loop. + * Connects RDP, loops while running and handles event and dispatch, cleans up + * after the connection ends. + */ +static DWORD WINAPI pf_client_thread_proc(LPVOID arg) +{ + freerdp* instance = (freerdp*)arg; + DWORD nCount; + DWORD status; + HANDLE handles[64]; + + if (!freerdp_connect(instance)) + { + WLog_ERR(TAG, "connection failure"); + return 0; + } + + while (!freerdp_shall_disconnect(instance)) + { + nCount = freerdp_get_event_handles(instance->context, &handles[0], 64); + + if (nCount == 0) + { + WLog_ERR(TAG, "%s: freerdp_get_event_handles failed", __FUNCTION__); + break; + } + + status = WaitForMultipleObjects(nCount, handles, FALSE, 100); + + if (status == WAIT_FAILED) + { + WLog_ERR(TAG, "%s: WaitForMultipleObjects failed with %"PRIu32"", __FUNCTION__, + status); + break; + } + + if (freerdp_shall_disconnect(instance)) + break; + + if (!freerdp_check_event_handles(instance->context)) + { + if (freerdp_get_last_error(instance->context) == FREERDP_ERROR_SUCCESS) + WLog_ERR(TAG, "Failed to check FreeRDP event handles"); + + break; + } + } + + freerdp_disconnect(instance); + return 0; +} + +/** + * Optional global initializer. + * Here we just register a signal handler to print out stack traces + * if available. + * */ +static BOOL pf_client_global_init(void) +{ + if (freerdp_handle_signals() != 0) + return FALSE; + + return TRUE; +} + +static int pf_logon_error_info(freerdp* instance, UINT32 data, UINT32 type) +{ + pClientContext* pc; + const char* str_data = freerdp_get_logon_error_info_data(data); + const char* str_type = freerdp_get_logon_error_info_type(type); + + if (!instance || !instance->context) + return -1; + + pc = (pClientContext*) instance->context; + WLog_INFO(TAG, "Logon Error Info %s [%s]", str_data, str_type); + return 1; +} + +/** + * Callback set in the rdp_freerdp structure, and used to make a certificate validation + * when the connection requires it. + * This function will actually be called by tls_verify_certificate(). + * @see rdp_client_connect() and tls_connect() + * @param instance pointer to the rdp_freerdp structure that contains the connection settings + * @param host The host currently connecting to + * @param port The port currently connecting to + * @param common_name The common name of the certificate, should match host or an alias of it + * @param subject The subject of the certificate + * @param issuer The certificate issuer name + * @param fingerprint The fingerprint of the certificate + * @param flags See VERIFY_CERT_FLAG_* for possible values. + * + * @return 1 if the certificate is trusted, 2 if temporary trusted, 0 otherwise. + */ +static DWORD pf_client_verify_certificate_ex(freerdp* instance, const char* host, UINT16 port, + const char* common_name, + const char* subject, const char* issuer, + const char* fingerprint, DWORD flags) +{ + /* TODO: Add trust level to proxy configurable settings */ + return 1; +} + +/** + * Callback set in the rdp_freerdp structure, and used to make a certificate validation + * when a stored certificate does not match the remote counterpart. + * This function will actually be called by tls_verify_certificate(). + * @see rdp_client_connect() and tls_connect() + * @param instance pointer to the rdp_freerdp structure that contains the connection settings + * @param host The host currently connecting to + * @param port The port currently connecting to + * @param common_name The common name of the certificate, should match host or an alias of it + * @param subject The subject of the certificate + * @param issuer The certificate issuer name + * @param fingerprint The fingerprint of the certificate + * @param old_subject The subject of the previous certificate + * @param old_issuer The previous certificate issuer name + * @param old_fingerprint The fingerprint of the previous certificate + * @param flags See VERIFY_CERT_FLAG_* for possible values. + * + * @return 1 if the certificate is trusted, 2 if temporary trusted, 0 otherwise. + */ +static DWORD pf_client_verify_changed_certificate_ex(freerdp* instance, + const char* host, UINT16 port, + const char* common_name, + const char* subject, const char* issuer, + const char* fingerprint, + const char* old_subject, const char* old_issuer, + const char* old_fingerprint, DWORD flags) +{ + /* TODO: Add trust level to proxy configurable settings */ + return 1; +} + +static BOOL pf_client_client_new(freerdp* instance, rdpContext* context) +{ + if (!instance || !context) + return FALSE; + + instance->PreConnect = pf_client_pre_connect; + instance->PostConnect = pf_client_post_connect; + instance->PostDisconnect = pf_client_post_disconnect; + instance->VerifyCertificateEx = pf_client_verify_certificate_ex; + instance->VerifyChangedCertificateEx = pf_client_verify_changed_certificate_ex; + instance->LogonErrorInfo = pf_logon_error_info; + return TRUE; +} + +int RdpClientEntry(RDP_CLIENT_ENTRY_POINTS* pEntryPoints) +{ + ZeroMemory(pEntryPoints, sizeof(RDP_CLIENT_ENTRY_POINTS)); + pEntryPoints->Version = RDP_CLIENT_INTERFACE_VERSION; + pEntryPoints->Size = sizeof(RDP_CLIENT_ENTRY_POINTS_V1); + pEntryPoints->GlobalInit = pf_client_global_init; + pEntryPoints->ContextSize = sizeof(pClientContext); + /* Client init and finish */ + pEntryPoints->ClientNew = pf_client_client_new; + return 0; +} + +/** + * Starts running a client connection towards target server. + */ +DWORD WINAPI pf_client_start(LPVOID arg) +{ + rdpContext* context = (rdpContext*)arg; + + if (freerdp_client_start(context) != 0) + return 1; + + return pf_client_thread_proc(context->instance); +} diff --git a/server/proxy/pf_client.h b/server/proxy/pf_client.h new file mode 100644 index 000000000..cda87a8f3 --- /dev/null +++ b/server/proxy/pf_client.h @@ -0,0 +1,31 @@ +/** + * 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_PFCLIENT_H +#define FREERDP_SERVER_PROXY_PFCLIENT_H + +#include +#include + +int RdpClientEntry(RDP_CLIENT_ENTRY_POINTS* pEntryPoints); +DWORD WINAPI pf_client_start(LPVOID arg); + +#endif /* FREERDP_SERVER_PROXY_PFCLIENT_H */ diff --git a/server/proxy/pf_common.c b/server/proxy/pf_common.c new file mode 100644 index 000000000..89e89c74a --- /dev/null +++ b/server/proxy/pf_common.c @@ -0,0 +1,107 @@ +/** + * 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 "pf_common.h" + +BOOL pf_common_connection_aborted_by_peer(proxyData* pdata) +{ + return WaitForSingleObject(pdata->connectionClosed, 0) == WAIT_OBJECT_0; +} + +void pf_common_copy_settings(rdpSettings* dst, rdpSettings* src) +{ + /* Client/server CORE options */ + dst->RdpVersion = src->RdpVersion; + dst->DesktopWidth = src->DesktopWidth; + dst->DesktopHeight = src->DesktopHeight; + dst->ColorDepth = src->ColorDepth; + dst->ConnectionType = src->ConnectionType; + dst->ClientBuild = src->ClientBuild; + dst->ClientHostname = _strdup(src->ClientHostname); + dst->ClientProductId = _strdup(src->ClientProductId); + dst->EarlyCapabilityFlags = src->EarlyCapabilityFlags; + dst->NetworkAutoDetect = src->NetworkAutoDetect; + dst->SupportAsymetricKeys = src->SupportAsymetricKeys; + dst->SupportErrorInfoPdu = src->SupportErrorInfoPdu; + dst->SupportStatusInfoPdu = src->SupportStatusInfoPdu; + dst->SupportMonitorLayoutPdu = src->SupportMonitorLayoutPdu; + dst->SupportGraphicsPipeline = src->SupportGraphicsPipeline; + dst->SupportDynamicTimeZone = src->SupportDynamicTimeZone; + dst->SupportHeartbeatPdu = src->SupportHeartbeatPdu; + dst->DesktopPhysicalWidth = src->DesktopPhysicalWidth; + dst->DesktopPhysicalHeight = src->DesktopPhysicalHeight; + dst->DesktopOrientation = src->DesktopOrientation; + dst->DesktopScaleFactor = src->DesktopScaleFactor; + dst->DeviceScaleFactor = src->DeviceScaleFactor; + /* client info */ + dst->AutoLogonEnabled = src->AutoLogonEnabled; + dst->CompressionEnabled = src->CompressionEnabled; + dst->DisableCtrlAltDel = src->DisableCtrlAltDel; + dst->EnableWindowsKey = src->EnableWindowsKey; + dst->MaximizeShell = src->MaximizeShell; + dst->LogonNotify = src->LogonNotify; + dst->LogonErrors = src->LogonErrors; + dst->MouseAttached = src->MouseAttached; + dst->MouseHasWheel = src->MouseHasWheel; + dst->RemoteConsoleAudio = src->RemoteConsoleAudio; + dst->AudioPlayback = src->AudioPlayback; + dst->AudioCapture = src->AudioCapture; + dst->VideoDisable = src->VideoDisable; + dst->PasswordIsSmartcardPin = src->PasswordIsSmartcardPin; + dst->UsingSavedCredentials = src->UsingSavedCredentials; + dst->ForceEncryptedCsPdu = src->ForceEncryptedCsPdu; + dst->HiDefRemoteApp = src->HiDefRemoteApp; + dst->CompressionLevel = src->CompressionLevel; + dst->PerformanceFlags = src->PerformanceFlags; + dst->AllowFontSmoothing = src->AllowFontSmoothing; + dst->DisableWallpaper = src->DisableWallpaper; + dst->DisableFullWindowDrag = src->DisableFullWindowDrag; + dst->DisableMenuAnims = src->DisableMenuAnims; + dst->DisableThemes = src->DisableThemes; + dst->DisableCursorShadow = src->DisableCursorShadow; + dst->DisableCursorBlinking = src->DisableCursorBlinking; + dst->AllowDesktopComposition = src->AllowDesktopComposition; + dst->DisableThemes = src->DisableThemes; + /* Remote App */ + dst->RemoteApplicationMode = src->RemoteApplicationMode; + dst->RemoteApplicationName = src->RemoteApplicationName; + dst->RemoteApplicationIcon = src->RemoteApplicationIcon; + dst->RemoteApplicationProgram = src->RemoteApplicationProgram; + dst->RemoteApplicationFile = src->RemoteApplicationFile; + dst->RemoteApplicationGuid = src->RemoteApplicationGuid; + dst->RemoteApplicationCmdLine = src->RemoteApplicationCmdLine; + dst->RemoteApplicationExpandCmdLine = src->RemoteApplicationExpandCmdLine; + dst->RemoteApplicationExpandWorkingDir = src->RemoteApplicationExpandWorkingDir; + dst->DisableRemoteAppCapsCheck = src->DisableRemoteAppCapsCheck; + dst->RemoteAppNumIconCaches = src->RemoteAppNumIconCaches; + dst->RemoteAppNumIconCacheEntries = src->RemoteAppNumIconCacheEntries; + dst->RemoteAppLanguageBarSupported = src->RemoteAppLanguageBarSupported; + dst->RemoteWndSupportLevel = src->RemoteWndSupportLevel; + /* GFX */ + dst->GfxThinClient = src->GfxThinClient; + dst->GfxSmallCache = src->GfxSmallCache; + dst->GfxProgressive = src->GfxProgressive; + dst->GfxProgressiveV2 = src->GfxProgressiveV2; + dst->GfxH264 = src->GfxH264; + dst->GfxAVC444 = src->GfxAVC444; + dst->GfxSendQoeAck = src->GfxSendQoeAck; + dst->GfxAVC444v2 = src->GfxAVC444v2; +} \ No newline at end of file diff --git a/server/proxy/pf_common.h b/server/proxy/pf_common.h new file mode 100644 index 000000000..9cca30ddb --- /dev/null +++ b/server/proxy/pf_common.h @@ -0,0 +1,31 @@ +/** + * 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_PFCOMMON_H +#define FREERDP_SERVER_PROXY_PFCOMMON_H + +#include +#include "pf_context.h" + +BOOL pf_common_connection_aborted_by_peer(proxyData* pdata); +void pf_common_copy_settings(rdpSettings* dst, rdpSettings* src); + +#endif /* FREERDP_SERVER_PROXY_PFCOMMON_H */ diff --git a/server/proxy/pf_config.c b/server/proxy/pf_config.c new file mode 100644 index 000000000..388a1e5bb --- /dev/null +++ b/server/proxy/pf_config.c @@ -0,0 +1,188 @@ +/** + * 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 "pf_log.h" +#include "pf_server.h" +#include "pf_config.h" + +#define TAG PROXY_TAG("config") + +#define CHANNELS_SEPERATOR "," + +static char** parse_channels_from_str(const char* str, UINT32* length) +{ + char* s = strdup(str); + size_t tokens_alloc = 1; + size_t tokens_count = 0; + char** tokens = calloc(tokens_alloc, sizeof(char*)); + char* token; + + while ((token = StrSep(&s, CHANNELS_SEPERATOR)) != NULL) + { + if (tokens_count == tokens_alloc) + { + tokens_alloc *= 2; + tokens = realloc(tokens, tokens_alloc * sizeof(char*)); + } + + tokens[tokens_count++] = strdup(token); + } + + 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; +} + +static BOOL pf_server_is_config_valid(proxyConfig* config) +{ + if (config->Host == NULL) + { + WLog_ERR(TAG, "Configuration value for `Server.Host` is not valid"); + return FALSE; + } + + if (config->Port <= 0) + { + WLog_ERR(TAG, "Configuration value for `Server.Port` is not valid"); + return FALSE; + } + + if (!config->UseLoadBalanceInfo) + { + if (config->TargetHost == NULL) + { + WLog_ERR(TAG, "Configuration value for `Target.Host` is not valid"); + return FALSE; + } + + if (config->TargetPort <= 0) + { + WLog_ERR(TAG, "Configuration value for `Target.Port` is not valid"); + return FALSE; + } + } + + return TRUE; +} + +DWORD pf_server_load_config(const char* path, proxyConfig* config) +{ + const char* input; + int rc; + DWORD result = CONFIG_PARSE_ERROR; + wIniFile* ini = IniFile_New(); + + if (!ini) + return CONFIG_PARSE_ERROR; + + if (IniFile_ReadFile(ini, path) < 0) + goto out; + + /* server */ + config->Host = _strdup(IniFile_GetKeyValueString(ini, "Server", "Host")); + config->LocalOnly = IniFile_GetKeyValueInt(ini, "Server", "LocalOnly"); + rc = IniFile_GetKeyValueInt(ini, "Server", "Port"); + + if ((rc < 0) || (rc > UINT16_MAX)) + goto out; + + config->Port = (UINT16)rc; + /* target */ + config->UseLoadBalanceInfo = IniFile_GetKeyValueInt(ini, "Target", "UseLoadBalanceInfo"); + config->TargetHost = _strdup(IniFile_GetKeyValueString(ini, "Target", "Host")); + rc = IniFile_GetKeyValueInt(ini, "Target", "Port"); + + if ((rc < 0) || (rc > UINT16_MAX)) + goto out; + + config->TargetPort = (UINT16)rc; + /* graphics */ + config->GFX = IniFile_GetKeyValueInt(ini, "Graphics", "GFX"); + config->BitmapUpdate = IniFile_GetKeyValueInt(ini, "Graphics", "BitmapUpdate"); + /* input */ + config->Keyboard = IniFile_GetKeyValueInt(ini, "Input", "Keyboard"); + config->Mouse = IniFile_GetKeyValueInt(ini, "Input", "Mouse"); + /* security */ + config->TlsSecurity = IniFile_GetKeyValueInt(ini, "Security", "TlsSecurity"); + config->NlaSecurity = IniFile_GetKeyValueInt(ini, "Security", "NlaSecurity"); + config->RdpSecurity = IniFile_GetKeyValueInt(ini, "Security", "RdpSecurity"); + /* channels filtering */ + config->WhitelistMode = IniFile_GetKeyValueInt(ini, "Channels", "WhitelistMode"); + input = IniFile_GetKeyValueString(ini, "Channels", "AllowedChannels"); + + if (input) + { + config->AllowedChannels = parse_channels_from_str(input, &config->AllowedChannelsCount); + + if (config->AllowedChannels == NULL) + goto out; + } + + input = IniFile_GetKeyValueString(ini, "Channels", "DeniedChannels"); + + if (input) + { + config->BlockedChannels = parse_channels_from_str(input, &config->BlockedChannelsCount); + + if (config->BlockedChannels == NULL) + goto out; + } + + result = CONFIG_PARSE_SUCCESS; +out: + IniFile_Free(ini); + + if (!pf_server_is_config_valid(config)) + return CONFIG_INVALID; + + return result; +} + +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); + free(config->TargetHost); + free(config->Host); + free(config); +} diff --git a/server/proxy/pf_config.h b/server/proxy/pf_config.h new file mode 100644 index 000000000..3ae229977 --- /dev/null +++ b/server/proxy/pf_config.h @@ -0,0 +1,71 @@ +/** + * 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_PFCONFIG_H +#define FREERDP_SERVER_PROXY_PFCONFIG_H + +#define CONFIG_PARSE_SUCCESS 0 +#define CONFIG_PARSE_ERROR 1 +#define CONFIG_INVALID 2 + +#include + +struct proxy_config +{ + /* server */ + char* Host; + UINT16 Port; + BOOL LocalOnly; + + /* target */ + BOOL UseLoadBalanceInfo; + char* TargetHost; + UINT16 TargetPort; + + /* graphics */ + BOOL GFX; + BOOL BitmapUpdate; + + /* input */ + BOOL Keyboard; + BOOL Mouse; + + /* security */ + BOOL NlaSecurity; + BOOL TlsSecurity; + BOOL RdpSecurity; + + /* channels */ + BOOL WhitelistMode; + + char** AllowedChannels; + UINT32 AllowedChannelsCount; + + char** BlockedChannels; + UINT32 BlockedChannelsCount; +}; + +typedef struct proxy_config proxyConfig; + +DWORD pf_server_load_config(const char* path, proxyConfig* config); +void pf_server_config_free(proxyConfig* config); + +#endif /* FREERDP_SERVER_PROXY_PFCONFIG_H */ diff --git a/server/proxy/pf_context.c b/server/proxy/pf_context.c new file mode 100644 index 000000000..0e00d4039 --- /dev/null +++ b/server/proxy/pf_context.c @@ -0,0 +1,81 @@ +/** +* 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 "pf_client.h" +#include "pf_context.h" +#include "pf_common.h" + +/* Proxy context initialization callback */ +static BOOL client_to_proxy_context_new(freerdp_peer* client, + pServerContext* context) +{ + context->vcm = WTSOpenServerA((LPSTR) client->context); + + if (!context->vcm || context->vcm == INVALID_HANDLE_VALUE) + goto fail_open_server; + + return TRUE; +fail_open_server: + context->vcm = NULL; + return FALSE; +} + +/* Proxy context free callback */ +static void client_to_proxy_context_free(freerdp_peer* client, + pServerContext* context) +{ + WINPR_UNUSED(client); + + if (context) + WTSCloseServer((HANDLE) context->vcm); +} + +BOOL init_p_server_context(freerdp_peer* client) +{ + client->ContextSize = sizeof(pServerContext); + client->ContextNew = (psPeerContextNew) client_to_proxy_context_new; + client->ContextFree = (psPeerContextFree) client_to_proxy_context_free; + return freerdp_peer_context_new(client); +} + +rdpContext* p_client_context_create(rdpSettings* clientSettings, + char* host, DWORD port) +{ + RDP_CLIENT_ENTRY_POINTS clientEntryPoints; + rdpContext* context; + rdpSettings* settings; + RdpClientEntry(&clientEntryPoints); + context = freerdp_client_context_new(&clientEntryPoints); + + if (!context) + return NULL; + + settings = context->settings; + pf_common_copy_settings(settings, 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; + return context; +} diff --git a/server/proxy/pf_context.h b/server/proxy/pf_context.h new file mode 100644 index 000000000..fa92168de --- /dev/null +++ b/server/proxy/pf_context.h @@ -0,0 +1,83 @@ +/** + * 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_PFCONTEXT_H +#define FREERDP_SERVER_PROXY_PFCONTEXT_H + +#include +#include +#include +#include +#include +#include "pf_config.h" +#include "pf_server.h" + + +typedef struct proxy_data proxyData; + +/** + * Wraps rdpContext and holds the state for the proxy's server. + */ +struct p_server_context +{ + rdpContext _context; + + proxyData* pdata; + + HANDLE vcm; + HANDLE thread; + HANDLE dynvcReady; + + RdpgfxServerContext* gfx; +}; +typedef struct p_server_context pServerContext; + +/** + * Wraps rdpContext and holds the state for the proxy's client. + */ +struct p_client_context +{ + rdpContext _context; + + proxyData* pdata; + + RdpeiClientContext* rdpei; + RdpgfxClientContext* gfx; +}; +typedef struct p_client_context pClientContext; + +/** + * Holds data common to both sides of a proxy's session. + */ +struct proxy_data +{ + proxyConfig* config; + + pServerContext* ps; + pClientContext* pc; + + HANDLE connectionClosed; +}; + +BOOL init_p_server_context(freerdp_peer* client); +rdpContext* p_client_context_create(rdpSettings* clientSettings, char* host, DWORD port); + +#endif /* FREERDP_SERVER_PROXY_PFCONTEXT_H */ diff --git a/server/proxy/pf_gdi.c b/server/proxy/pf_gdi.c new file mode 100644 index 000000000..67ddd76a2 --- /dev/null +++ b/server/proxy/pf_gdi.c @@ -0,0 +1,151 @@ +/** + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include "pf_gdi.h" +#include "pf_log.h" + +#include +#define TAG PROXY_TAG("gdi") + +/* TODO: Figure how to use functions decleared in update.c */ +static BOOL pf_gdi_set_bounds(rdpContext* context, + const rdpBounds* bounds) +{ + WLog_INFO(TAG, __FUNCTION__); + return TRUE; +} + +static BOOL pf_gdi_dstblt(rdpContext* context, const DSTBLT_ORDER* dstblt) +{ + WLog_INFO(TAG, __FUNCTION__); + return TRUE; +} + +static BOOL pf_gdi_patblt(rdpContext* context, PATBLT_ORDER* patblt) +{ + WLog_INFO(TAG, __FUNCTION__); + return TRUE; +} + +static BOOL pf_gdi_scrblt(rdpContext* context, const SCRBLT_ORDER* scrblt) +{ + WLog_INFO(TAG, __FUNCTION__); + return TRUE; +} + +static BOOL pf_gdi_opaque_rect(rdpContext* context, + const OPAQUE_RECT_ORDER* opaque_rect) +{ + WLog_INFO(TAG, __FUNCTION__); + return TRUE; +} + +static BOOL pf_gdi_multi_opaque_rect(rdpContext* context, + const MULTI_OPAQUE_RECT_ORDER* multi_opaque_rect) +{ + WLog_INFO(TAG, __FUNCTION__); + return TRUE; +} + +static BOOL pf_gdi_line_to(rdpContext* context, const LINE_TO_ORDER* line_to) +{ + WLog_INFO(TAG, __FUNCTION__); + return TRUE; +} + +static BOOL pf_gdi_polyline(rdpContext* context, + const POLYLINE_ORDER* polyline) +{ + WLog_INFO(TAG, __FUNCTION__); + return TRUE; +} + +static BOOL pf_gdi_memblt(rdpContext* context, MEMBLT_ORDER* memblt) +{ + WLog_INFO(TAG, __FUNCTION__); + return TRUE; +} + +static BOOL pf_gdi_mem3blt(rdpContext* context, MEM3BLT_ORDER* mem3blt) +{ + WLog_INFO(TAG, __FUNCTION__); + return TRUE; +} + + +static BOOL pf_gdi_polygon_sc(rdpContext* context, + const POLYGON_SC_ORDER* polygon_sc) +{ + WLog_INFO(TAG, __FUNCTION__); + return TRUE; +} + +static BOOL pf_gdi_polygon_cb(rdpContext* context, + POLYGON_CB_ORDER* polygon_cb) +{ + WLog_INFO(TAG, __FUNCTION__); + return TRUE; +} + +static BOOL pf_gdi_surface_frame_marker(rdpContext* context, + const SURFACE_FRAME_MARKER* surface_frame_marker) +{ + WLog_INFO(TAG, __FUNCTION__); + return TRUE; +} + +static BOOL pf_gdi_surface_bits(rdpContext* context, + const SURFACE_BITS_COMMAND* cmd) +{ + WLog_INFO(TAG, __FUNCTION__); + return TRUE; +} + +void pf_gdi_register_update_callbacks(rdpUpdate* update) +{ + rdpPrimaryUpdate* primary = update->primary; + update->SetBounds = pf_gdi_set_bounds; + primary->DstBlt = pf_gdi_dstblt; + primary->PatBlt = pf_gdi_patblt; + primary->ScrBlt = pf_gdi_scrblt; + primary->OpaqueRect = pf_gdi_opaque_rect; + primary->MultiOpaqueRect = pf_gdi_multi_opaque_rect; + primary->LineTo = pf_gdi_line_to; + primary->Polyline = pf_gdi_polyline; + primary->MemBlt = pf_gdi_memblt; + primary->Mem3Blt = pf_gdi_mem3blt; + primary->PolygonSC = pf_gdi_polygon_sc; + primary->PolygonCB = pf_gdi_polygon_cb; + update->SurfaceBits = pf_gdi_surface_bits; + update->SurfaceFrameMarker = pf_gdi_surface_frame_marker; +} + diff --git a/server/proxy/pf_gdi.h b/server/proxy/pf_gdi.h new file mode 100644 index 000000000..b419e5ee3 --- /dev/null +++ b/server/proxy/pf_gdi.h @@ -0,0 +1,29 @@ +/** + * 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_PFGDI_H +#define FREERDP_SERVER_PROXY_PFGDI_H + +#include + +void pf_gdi_register_update_callbacks(rdpUpdate* update); + +#endif /* FREERDP_SERVER_PROXY_PFGDI_H */ diff --git a/server/proxy/pf_graphics.c b/server/proxy/pf_graphics.c new file mode 100644 index 000000000..1a2a8b660 --- /dev/null +++ b/server/proxy/pf_graphics.c @@ -0,0 +1,171 @@ +/** + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include + +#include + +#include "pf_graphics.h" +#include "pf_log.h" +#include "pf_gdi.h" +#include "pf_context.h" + +#include +#include +#include +#include +#include +#include +#include +#define TAG PROXY_TAG("graphics") + +/* Bitmap Class */ +static BOOL pf_Bitmap_New(rdpContext* context, rdpBitmap* bitmap) +{ + return TRUE; +} + +static void pf_Bitmap_Free(rdpContext* context, rdpBitmap* bitmap) +{ +} + +static BOOL pf_Bitmap_Paint(rdpContext* context, rdpBitmap* bitmap) +{ + return TRUE; +} + +static BOOL pf_Bitmap_SetSurface(rdpContext* context, rdpBitmap* bitmap, + BOOL primary) +{ + return TRUE; +} + +/* Pointer Class */ +static BOOL pf_Pointer_New(rdpContext* context, rdpPointer* pointer) +{ + return TRUE; +} + +static void pf_Pointer_Free(rdpContext* context, rdpPointer* pointer) +{ +} + +static BOOL pf_Pointer_Set(rdpContext* context, + const rdpPointer* pointer) +{ + return TRUE; +} + +static BOOL pf_Pointer_SetNull(rdpContext* context) +{ + return TRUE; +} + +static BOOL pf_Pointer_SetDefault(rdpContext* context) +{ + return TRUE; +} + +static BOOL pf_Pointer_SetPosition(rdpContext* context, UINT32 x, UINT32 y) +{ + return TRUE; +} + +/* Glyph Class */ +static BOOL pf_Glyph_New(rdpContext* context, const rdpGlyph* glyph) +{ + return TRUE; +} + +static void pf_Glyph_Free(rdpContext* context, rdpGlyph* glyph) +{ +} + +static BOOL pf_Glyph_Draw(rdpContext* context, const rdpGlyph* glyph, INT32 x, + INT32 y, INT32 w, INT32 h, INT32 sx, INT32 sy, + BOOL fOpRedundant) +{ + return TRUE; +} + +static BOOL pf_Glyph_BeginDraw(rdpContext* context, INT32 x, INT32 y, + INT32 width, INT32 height, UINT32 bgcolor, + UINT32 fgcolor, BOOL fOpRedundant) +{ + return TRUE; +} + +static BOOL pf_Glyph_EndDraw(rdpContext* context, INT32 x, INT32 y, + INT32 width, INT32 height, + UINT32 bgcolor, UINT32 fgcolor) +{ + return TRUE; +} + +/* Graphics Module */ +BOOL pf_register_pointer(rdpGraphics* graphics) +{ + rdpPointer* pointer = NULL; + + if (!(pointer = (rdpPointer*) calloc(1, sizeof(rdpPointer)))) + return FALSE; + + pointer->size = sizeof(rdpPointer); + pointer->New = pf_Pointer_New; + pointer->Free = pf_Pointer_Free; + pointer->Set = pf_Pointer_Set; + pointer->SetNull = pf_Pointer_SetNull; + pointer->SetDefault = pf_Pointer_SetDefault; + pointer->SetPosition = pf_Pointer_SetPosition; + graphics_register_pointer(graphics, pointer); + free(pointer); + return TRUE; +} + +BOOL pf_register_graphics(rdpGraphics* graphics) +{ + rdpBitmap bitmap; + rdpGlyph glyph; + + if (!graphics || !graphics->Bitmap_Prototype || !graphics->Glyph_Prototype) + return FALSE; + + bitmap = *graphics->Bitmap_Prototype; + glyph = *graphics->Glyph_Prototype; + bitmap.size = sizeof(rdpBitmap); + bitmap.New = pf_Bitmap_New; + bitmap.Free = pf_Bitmap_Free; + bitmap.Paint = pf_Bitmap_Paint; + bitmap.SetSurface = pf_Bitmap_SetSurface; + graphics_register_bitmap(graphics, &bitmap); + glyph.size = sizeof(rdpGlyph); + glyph.New = pf_Glyph_New; + glyph.Free = pf_Glyph_Free; + glyph.Draw = pf_Glyph_Draw; + glyph.BeginDraw = pf_Glyph_BeginDraw; + glyph.EndDraw = pf_Glyph_EndDraw; + graphics_register_glyph(graphics, &glyph); + return TRUE; +} \ No newline at end of file diff --git a/server/proxy/pf_graphics.h b/server/proxy/pf_graphics.h new file mode 100644 index 000000000..08763a64f --- /dev/null +++ b/server/proxy/pf_graphics.h @@ -0,0 +1,31 @@ +/** + * 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_PFGRAPHICS_H +#define FREERDP_SERVER_PROXY_PFGRAPHICS_H + +#include +#include "pf_client.h" + +BOOL pf_register_pointer(rdpGraphics* graphics); +BOOL pf_register_graphics(rdpGraphics* graphics); + +#endif /* FREERDP_SERVER_PROXY_PFGRAPHICS_H */ diff --git a/server/proxy/pf_input.c b/server/proxy/pf_input.c new file mode 100644 index 000000000..6babb2b5d --- /dev/null +++ b/server/proxy/pf_input.c @@ -0,0 +1,93 @@ +/** + * 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 "pf_input.h" +#include "pf_context.h" + +static BOOL pf_server_synchronize_event(rdpInput* input, UINT32 flags) +{ + pServerContext* ps = (pServerContext*)input->context; + pClientContext* pc = ps->pdata->pc; + rdpContext* context = (rdpContext*) pc; + return freerdp_input_send_synchronize_event(context->input, flags); +} + +static BOOL pf_server_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code) +{ + pServerContext* ps = (pServerContext*)input->context; + pClientContext* pc = ps->pdata->pc; + rdpContext* context = (rdpContext*) pc; + proxyConfig* config = ps->pdata->config; + + if (!config->Keyboard) + return TRUE; + + return freerdp_input_send_keyboard_event(context->input, flags, code); +} + +static BOOL pf_server_unicode_keyboard_event(rdpInput* input, UINT16 flags, UINT16 code) +{ + pServerContext* ps = (pServerContext*)input->context; + pClientContext* pc = ps->pdata->pc; + rdpContext* context = (rdpContext*) pc; + proxyConfig* config = ps->pdata->config; + + if (!config->Keyboard) + return TRUE; + + return freerdp_input_send_unicode_keyboard_event(context->input, flags, code); +} + +static BOOL pf_server_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, UINT16 y) +{ + pServerContext* ps = (pServerContext*)input->context; + pClientContext* pc = ps->pdata->pc; + rdpContext* context = (rdpContext*) pc; + proxyConfig* config = ps->pdata->config; + + if (!config->Mouse) + return TRUE; + + return freerdp_input_send_mouse_event(context->input, flags, x, y); +} + +static BOOL pf_server_extended_mouse_event(rdpInput* input, UINT16 flags, UINT16 x, + UINT16 y) +{ + pServerContext* ps = (pServerContext*)input->context; + pClientContext* pc = ps->pdata->pc; + rdpContext* context = (rdpContext*) pc; + proxyConfig* config = ps->pdata->config; + + if (!config->Mouse) + return TRUE; + + return freerdp_input_send_extended_mouse_event(context->input, flags, x, y); +} + +void pf_server_register_input_callbacks(rdpInput* input) +{ + input->SynchronizeEvent = pf_server_synchronize_event; + input->KeyboardEvent = pf_server_keyboard_event; + input->UnicodeKeyboardEvent = pf_server_unicode_keyboard_event; + input->MouseEvent = pf_server_mouse_event; + input->ExtendedMouseEvent = pf_server_extended_mouse_event; +} diff --git a/server/proxy/pf_input.h b/server/proxy/pf_input.h new file mode 100644 index 000000000..ea9c542fa --- /dev/null +++ b/server/proxy/pf_input.h @@ -0,0 +1,29 @@ +/** + * 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_PFINPUT_H +#define FREERDP_SERVER_PROXY_PFINPUT_H + +#include + +void pf_server_register_input_callbacks(rdpInput* input); + +#endif /* FREERDP_SERVER_PROXY_PFINPUT_H */ diff --git a/server/proxy/pf_log.h b/server/proxy/pf_log.h new file mode 100644 index 000000000..4376a882f --- /dev/null +++ b/server/proxy/pf_log.h @@ -0,0 +1,29 @@ +/** + * 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_PFLOG_H +#define FREERDP_SERVER_PROXY_PFLOG_H + +#include + +#define PROXY_TAG(tag) "proxy." tag + +#endif /* FREERDP_SERVER_PROXY_PFLOG_H */ \ No newline at end of file diff --git a/server/proxy/pf_rdpgfx.c b/server/proxy/pf_rdpgfx.c new file mode 100644 index 000000000..d049e2748 --- /dev/null +++ b/server/proxy/pf_rdpgfx.c @@ -0,0 +1,319 @@ +/** + * 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 "pf_rdpgfx.h" +#include "pf_context.h" +#include "pf_log.h" + +#define TAG PROXY_TAG("gfx") + +BOOL pf_server_rdpgfx_init(pServerContext* ps) +{ + RdpgfxServerContext* gfx; + gfx = ps->gfx = rdpgfx_server_context_new(ps->vcm); + + if (!gfx) + { + return FALSE; + } + + gfx->rdpcontext = (rdpContext*)ps; + return TRUE; +} + +static UINT pf_rdpgfx_reset_graphics(RdpgfxClientContext* context, + const RDPGFX_RESET_GRAPHICS_PDU* resetGraphics) +{ + proxyData* pdata = (proxyData*) context->custom; + RdpgfxServerContext* server = (RdpgfxServerContext*) pdata->ps->gfx; + WLog_DBG(TAG, __FUNCTION__); + return server->ResetGraphics(server, resetGraphics); +} + +static UINT pf_rdpgfx_start_frame(RdpgfxClientContext* context, + const RDPGFX_START_FRAME_PDU* startFrame) +{ + proxyData* pdata = (proxyData*) context->custom; + RdpgfxServerContext* server = (RdpgfxServerContext*) pdata->ps->gfx; + WLog_DBG(TAG, __FUNCTION__); + return server->StartFrame(server, startFrame); +} + +static UINT pf_rdpgfx_end_frame(RdpgfxClientContext* context, + const RDPGFX_END_FRAME_PDU* endFrame) +{ + proxyData* pdata = (proxyData*) context->custom; + RdpgfxServerContext* server = (RdpgfxServerContext*) pdata->ps->gfx; + WLog_DBG(TAG, __FUNCTION__); + return server->EndFrame(server, endFrame); +} + +static UINT pf_rdpgfx_surface_command(RdpgfxClientContext* context, + const RDPGFX_SURFACE_COMMAND* cmd) +{ + proxyData* pdata = (proxyData*) context->custom; + RdpgfxServerContext* server = (RdpgfxServerContext*) pdata->ps->gfx; + WLog_DBG(TAG, __FUNCTION__); + return server->SurfaceCommand(server, cmd); +} + +static UINT pf_rdpgfx_delete_encoding_context(RdpgfxClientContext* context, + const RDPGFX_DELETE_ENCODING_CONTEXT_PDU* deleteEncodingContext) +{ + proxyData* pdata = (proxyData*) context->custom; + RdpgfxServerContext* server = (RdpgfxServerContext*) pdata->ps->gfx; + WLog_DBG(TAG, __FUNCTION__); + return server->DeleteEncodingContext(server, deleteEncodingContext); +} + +static UINT pf_rdpgfx_create_surface(RdpgfxClientContext* context, + const RDPGFX_CREATE_SURFACE_PDU* createSurface) +{ + proxyData* pdata = (proxyData*) context->custom; + RdpgfxServerContext* server = (RdpgfxServerContext*) pdata->ps->gfx; + WLog_DBG(TAG, __FUNCTION__); + return server->CreateSurface(server, createSurface); +} + +static UINT pf_rdpgfx_delete_surface(RdpgfxClientContext* context, + const RDPGFX_DELETE_SURFACE_PDU* deleteSurface) +{ + proxyData* pdata = (proxyData*) context->custom; + RdpgfxServerContext* server = (RdpgfxServerContext*) pdata->ps->gfx; + WLog_DBG(TAG, __FUNCTION__); + return server->DeleteSurface(server, deleteSurface); +} + +static UINT pf_rdpgfx_solid_fill(RdpgfxClientContext* context, + const RDPGFX_SOLID_FILL_PDU* solidFill) +{ + proxyData* pdata = (proxyData*) context->custom; + RdpgfxServerContext* server = (RdpgfxServerContext*) pdata->ps->gfx; + WLog_DBG(TAG, __FUNCTION__); + return server->SolidFill(server, solidFill); +} + +static UINT pf_rdpgfx_surface_to_surface(RdpgfxClientContext* context, + const RDPGFX_SURFACE_TO_SURFACE_PDU* surfaceToSurface) +{ + proxyData* pdata = (proxyData*) context->custom; + RdpgfxServerContext* server = (RdpgfxServerContext*) pdata->ps->gfx; + WLog_DBG(TAG, __FUNCTION__); + return server->SurfaceToSurface(server, surfaceToSurface); +} + +static UINT pf_rdpgfx_surface_to_cache(RdpgfxClientContext* context, + const RDPGFX_SURFACE_TO_CACHE_PDU* surfaceToCache) +{ + proxyData* pdata = (proxyData*) context->custom; + RdpgfxServerContext* server = (RdpgfxServerContext*) pdata->ps->gfx; + WLog_DBG(TAG, __FUNCTION__); + return server->SurfaceToCache(server, surfaceToCache); +} + +static UINT pf_rdpgfx_cache_to_surface(RdpgfxClientContext* context, + const RDPGFX_CACHE_TO_SURFACE_PDU* cacheToSurface) +{ + proxyData* pdata = (proxyData*) context->custom; + RdpgfxServerContext* server = (RdpgfxServerContext*) pdata->ps->gfx; + WLog_DBG(TAG, __FUNCTION__); + return server->CacheToSurface(server, cacheToSurface); +} + +static UINT pf_rdpgfx_cache_import_reply(RdpgfxClientContext* context, + const RDPGFX_CACHE_IMPORT_REPLY_PDU* cacheImportReply) +{ + proxyData* pdata = (proxyData*) context->custom; + RdpgfxServerContext* server = (RdpgfxServerContext*) pdata->ps->gfx; + WLog_DBG(TAG, __FUNCTION__); + return server->CacheImportReply(server, cacheImportReply); +} + +static UINT pf_rdpgfx_evict_cache_entry(RdpgfxClientContext* context, + const RDPGFX_EVICT_CACHE_ENTRY_PDU* evictCacheEntry) +{ + proxyData* pdata = (proxyData*) context->custom; + RdpgfxServerContext* server = (RdpgfxServerContext*) pdata->ps->gfx; + WLog_DBG(TAG, __FUNCTION__); + return server->EvictCacheEntry(server, evictCacheEntry); +} + +static UINT pf_rdpgfx_map_surface_to_output(RdpgfxClientContext* context, + const RDPGFX_MAP_SURFACE_TO_OUTPUT_PDU* surfaceToOutput) +{ + proxyData* pdata = (proxyData*) context->custom; + RdpgfxServerContext* server = (RdpgfxServerContext*) pdata->ps->gfx; + WLog_DBG(TAG, __FUNCTION__); + return server->MapSurfaceToOutput(server, surfaceToOutput); +} + +static UINT pf_rdpgfx_map_surface_to_window(RdpgfxClientContext* context, + const RDPGFX_MAP_SURFACE_TO_WINDOW_PDU* surfaceToWindow) +{ + proxyData* pdata = (proxyData*) context->custom; + RdpgfxServerContext* server = (RdpgfxServerContext*) pdata->ps->gfx; + WLog_DBG(TAG, __FUNCTION__); + return server->MapSurfaceToWindow(server, surfaceToWindow); +} + +static UINT pf_rdpgfx_map_surface_to_scaled_window(RdpgfxClientContext* context, + const RDPGFX_MAP_SURFACE_TO_SCALED_WINDOW_PDU* surfaceToScaledWindow) +{ + proxyData* pdata = (proxyData*) context->custom; + RdpgfxServerContext* server = (RdpgfxServerContext*) pdata->ps->gfx; + WLog_DBG(TAG, __FUNCTION__); + return server->MapSurfaceToScaledWindow(server, surfaceToScaledWindow); +} + + +static UINT pf_rdpgfx_map_surface_to_scaled_output(RdpgfxClientContext* context, + const RDPGFX_MAP_SURFACE_TO_SCALED_OUTPUT_PDU* surfaceToScaledOutput) +{ + proxyData* pdata = (proxyData*) context->custom; + RdpgfxServerContext* server = (RdpgfxServerContext*) pdata->ps->gfx; + WLog_DBG(TAG, __FUNCTION__); + return server->MapSurfaceToScaledOutput(server, surfaceToScaledOutput); +} + +static UINT pf_rdpgfx_on_open(RdpgfxClientContext* context, + BOOL* do_caps_advertise, BOOL* send_frame_acks) +{ + proxyData* pdata = (proxyData*) context->custom; + RdpgfxServerContext* server = (RdpgfxServerContext*) pdata->ps->gfx; + WLog_DBG(TAG, __FUNCTION__); + + if (NULL != do_caps_advertise) + *do_caps_advertise = FALSE; + + if (NULL != send_frame_acks) + *send_frame_acks = FALSE; + + /* Wait for the proxy's server's DYNVC to be in a ready state to safely open + * the GFX DYNVC. */ + WLog_DBG(TAG, "Waiting for proxy's server dynvc to be ready"); + WaitForSingleObject(pdata->ps->dynvcReady, INFINITE); + + /* Check for error since the server's API doesn't return WTSAPI error codes */ + if (server->Open(server)) + { + return CHANNEL_RC_OK; + } + + return CHANNEL_RC_INITIALIZATION_ERROR; +} + +static UINT pf_rdpgfx_on_close(RdpgfxClientContext* context) +{ + proxyData* pdata = (proxyData*) context->custom; + RdpgfxServerContext* server = (RdpgfxServerContext*) pdata->ps->gfx; + WLog_DBG(TAG, __FUNCTION__); + return server->Close(server) ? CHANNEL_RC_OK : ERROR_INTERNAL_ERROR; +} + +static UINT pf_rdpgfx_caps_confirm(RdpgfxClientContext* context, + const RDPGFX_CAPS_CONFIRM_PDU* capsConfirm) +{ + proxyData* pdata = (proxyData*) context->custom; + RdpgfxServerContext* server = (RdpgfxServerContext*) pdata->ps->gfx; + WLog_DBG(TAG, __FUNCTION__); + return server->CapsConfirm(server, capsConfirm); +} + +/* Proxy server side callbacks */ +static UINT pf_rdpgfx_caps_advertise(RdpgfxServerContext* context, + const RDPGFX_CAPS_ADVERTISE_PDU* capsAdvertise) +{ + proxyData* pdata = (proxyData*) context->custom; + RdpgfxClientContext* client = (RdpgfxClientContext*) pdata->pc->gfx; + UINT16 index; + UINT16 proxySupportedCapsSetCount = 0; + RDPGFX_CAPS_ADVERTISE_PDU supportedCapsAdvertise; + RDPGFX_CAPSET* proxySupportedCapsSet; + RDPGFX_CAPSET proxySupportedCapsSets[RDPGFX_NUMBER_CAPSETS] = { 0 }; + + for (index = 0; index < capsAdvertise->capsSetCount; index++) + { + const RDPGFX_CAPSET* currentCaps = &capsAdvertise->capsSets[index]; + + /* Add cap to supported caps list if supported by FreeRDP. + * TODO: Have a better way of expressing max supported GFX caps version + * by FreeRDP. + */ + if (currentCaps->version <= RDPGFX_CAPVERSION_106) + { + proxySupportedCapsSet = &proxySupportedCapsSets[proxySupportedCapsSetCount++]; + proxySupportedCapsSet->version = currentCaps->version; + proxySupportedCapsSet->length = currentCaps->length; + proxySupportedCapsSet->flags = currentCaps->flags; + } + } + + supportedCapsAdvertise.capsSetCount = proxySupportedCapsSetCount; + supportedCapsAdvertise.capsSets = proxySupportedCapsSets; + WLog_DBG(TAG, __FUNCTION__); + return client->CapsAdvertise(client, &supportedCapsAdvertise); +} + +static UINT pf_rdpgfx_frame_acknowledge(RdpgfxServerContext* context, + const RDPGFX_FRAME_ACKNOWLEDGE_PDU* frameAcknowledge) +{ + proxyData* pdata = (proxyData*) context->custom; + RdpgfxClientContext* client = (RdpgfxClientContext*) pdata->pc->gfx; + WLog_DBG(TAG, __FUNCTION__); + return client->FrameAcknowledge(client, frameAcknowledge); +} + +void pf_rdpgfx_pipeline_init(RdpgfxClientContext* gfx, RdpgfxServerContext* server, + proxyData* pdata) +{ + /* Set server and client side references to proxy data */ + gfx->custom = (void*) pdata; + server->custom = (void*) pdata; + /* Set client callbacks */ + gfx->ResetGraphics = pf_rdpgfx_reset_graphics; + gfx->StartFrame = pf_rdpgfx_start_frame; + gfx->EndFrame = pf_rdpgfx_end_frame; + gfx->SurfaceCommand = pf_rdpgfx_surface_command; + gfx->DeleteEncodingContext = pf_rdpgfx_delete_encoding_context; + gfx->CreateSurface = pf_rdpgfx_create_surface; + gfx->DeleteSurface = pf_rdpgfx_delete_surface; + gfx->SolidFill = pf_rdpgfx_solid_fill; + gfx->SurfaceToSurface = pf_rdpgfx_surface_to_surface; + gfx->SurfaceToCache = pf_rdpgfx_surface_to_cache; + gfx->CacheToSurface = pf_rdpgfx_cache_to_surface; + gfx->CacheImportReply = pf_rdpgfx_cache_import_reply; + gfx->EvictCacheEntry = pf_rdpgfx_evict_cache_entry; + gfx->MapSurfaceToOutput = pf_rdpgfx_map_surface_to_output; + gfx->MapSurfaceToWindow = pf_rdpgfx_map_surface_to_window; + gfx->MapSurfaceToScaledOutput = pf_rdpgfx_map_surface_to_scaled_output; + gfx->MapSurfaceToScaledWindow = pf_rdpgfx_map_surface_to_scaled_window; + gfx->OnOpen = pf_rdpgfx_on_open; + gfx->OnClose = pf_rdpgfx_on_close; + gfx->CapsConfirm = pf_rdpgfx_caps_confirm; + /* Set server callbacks */ + server->CapsAdvertise = pf_rdpgfx_caps_advertise; + server->FrameAcknowledge = pf_rdpgfx_frame_acknowledge; +} diff --git a/server/proxy/pf_rdpgfx.h b/server/proxy/pf_rdpgfx.h new file mode 100644 index 000000000..4ac1f20e7 --- /dev/null +++ b/server/proxy/pf_rdpgfx.h @@ -0,0 +1,34 @@ +/** + * 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_PFRDPGFX_H +#define FREERDP_SERVER_PROXY_PFRDPGFX_H + +#include +#include + +#include "pf_context.h" + +BOOL pf_server_rdpgfx_init(pServerContext* ps); +void pf_rdpgfx_pipeline_init(RdpgfxClientContext* gfx, RdpgfxServerContext* server, + proxyData* pdata); + +#endif /*FREERDP_SERVER_PROXY_PFRDPGFX_H*/ \ No newline at end of file diff --git a/server/proxy/pf_server.c b/server/proxy/pf_server.c new file mode 100644 index 000000000..c9e933fca --- /dev/null +++ b/server/proxy/pf_server.c @@ -0,0 +1,430 @@ +/** + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "pf_server.h" +#include "pf_common.h" +#include "pf_log.h" +#include "pf_config.h" +#include "pf_client.h" +#include "pf_context.h" +#include "pf_input.h" +#include "pf_update.h" +#include "pf_rdpgfx.h" + +#define TAG PROXY_TAG("server") + +static void pf_server_handle_client_disconnection(freerdp_peer* client) +{ + pServerContext* ps; + proxyData* pdata; + rdpContext* pc; + ps = (pServerContext*)client->context; + pc = (rdpContext*) ps->pdata->pc; + pdata = ps->pdata; + WLog_INFO(TAG, "Client %s disconnected; closing connection with server %s", + client->hostname, pc->settings->ServerHostname); + /* Mark connection closed for sContext */ + SetEvent(pdata->connectionClosed); + freerdp_abort_connect(pc->instance); + /* Close connection to remote host */ + WLog_DBG(TAG, "Waiting for proxy's client thread to finish"); + WaitForSingleObject(ps->thread, INFINITE); + CloseHandle(ps->thread); +} + +static BOOL pf_server_parse_target_from_routing_token(freerdp_peer* client, + char** target, DWORD* port) +{ +#define TARGET_MAX (100) +#define ROUTING_TOKEN_PREFIX "Cookie: msts=" + char* colon; + 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); + + if (routing_token && + (routing_token_length > prefix_len) && (routing_token_length < TARGET_MAX)) + { + 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; +} + +/* Event callbacks */ +/** + * This callback is called when the entire connection sequence is done (as + * described in MS-RDPBCGR section 1.3) + * + * The server may start sending graphics output and receiving keyboard/mouse + * input after this callback returns. + */ +static BOOL pf_server_post_connect(freerdp_peer* client) +{ + proxyConfig* config; + pServerContext* ps; + pClientContext* pc; + HANDLE connectionClosedEvent; + 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; + } + + 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; + 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))) + { + WLog_ERR(TAG, "CreateThread failed!"); + return FALSE; + } + + return TRUE; +} + +static BOOL pf_server_activate(freerdp_peer* client) +{ + client->settings->CompressionLevel = PACKET_COMPR_TYPE_RDP8; + return TRUE; +} + +/** + * Handles an incoming client connection, to be run in it's own thread. + * + * arg is a pointer to a freerdp_peer representing the client. + */ +static DWORD WINAPI pf_server_handle_client(LPVOID arg) +{ + HANDLE eventHandles[32]; + HANDLE ChannelEvent; + DWORD eventCount; + DWORD tmp; + DWORD status; + pServerContext* ps; + rdpContext* pc; + proxyData* pdata; + proxyConfig* config; + freerdp_peer* client = (freerdp_peer*) arg; + + if (!init_p_server_context(client)) + { + freerdp_peer_free(client); + return 0; + } + + ps = (pServerContext*) client->context; + ps->dynvcReady = CreateEvent(NULL, TRUE, FALSE, NULL); + pdata = calloc(1, sizeof(proxyData)); + ps->pdata = pdata; + /* keep configuration in proxyData */ + pdata->config = client->ContextExtra; + config = pdata->config; + client->settings->SupportGraphicsPipeline = config->GFX; + client->settings->SupportDynamicChannels = TRUE; + client->settings->CertificateFile = _strdup("server.crt"); + client->settings->PrivateKeyFile = _strdup("server.key"); + client->settings->RdpKeyFile = _strdup("server.key"); + + if (!client->settings->CertificateFile || !client->settings->PrivateKeyFile + || !client->settings->RdpKeyFile) + { + WLog_ERR(TAG, "Memory allocation failed (strdup)"); + freerdp_peer_free(client); + return 0; + } + + client->settings->RdpSecurity = config->RdpSecurity; + client->settings->TlsSecurity = config->TlsSecurity; + client->settings->NlaSecurity = config->NlaSecurity; + client->settings->EncryptionLevel = ENCRYPTION_LEVEL_CLIENT_COMPATIBLE; + client->settings->ColorDepth = 32; + client->settings->SuppressOutput = TRUE; + client->settings->RefreshRect = TRUE; + client->PostConnect = pf_server_post_connect; + client->Activate = pf_server_activate; + pf_server_register_input_callbacks(client->input); + 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); + /* Main client event handling loop */ + ChannelEvent = WTSVirtualChannelManagerGetEventHandle(ps->vcm); + + while (1) + { + eventCount = 0; + { + tmp = client->GetEventHandles(client, &eventHandles[eventCount], + 32 - eventCount); + + if (tmp == 0) + { + WLog_ERR(TAG, "Failed to get FreeRDP transport event handles"); + break; + } + + eventCount += tmp; + } + eventHandles[eventCount++] = ChannelEvent; + eventHandles[eventCount++] = WTSVirtualChannelManagerGetEventHandle(ps->vcm); + status = WaitForMultipleObjects(eventCount, eventHandles, FALSE, INFINITE); + + if (status == WAIT_FAILED) + { + /* Ignore wait fails that are caused by legitimate client disconnections */ + if (pf_common_connection_aborted_by_peer(pdata)) + break; + + WLog_ERR(TAG, "WaitForMultipleObjects failed (errno: %d)", errno); + break; + } + + if (client->CheckFileDescriptor(client) != TRUE) + break; + + if (WaitForSingleObject(ChannelEvent, 0) == WAIT_OBJECT_0) + { + if (!WTSVirtualChannelManagerCheckFileDescriptor(ps->vcm)) + { + WLog_ERR(TAG, "WTSVirtualChannelManagerCheckFileDescriptor failure"); + goto fail; + } + } + + switch (WTSVirtualChannelManagerGetDrdynvcState(ps->vcm)) + { + /* 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; + } + + break; + + case DRDYNVC_STATE_READY: + if (WaitForSingleObject(ps->dynvcReady, 0) == WAIT_TIMEOUT) + { + SetEvent(ps->dynvcReady); + } + + break; + + default: + break; + } + } + +fail: + + if (client->connected && !pf_common_connection_aborted_by_peer(pdata)) + { + pf_server_handle_client_disconnection(client); + } + + pc = (rdpContext*) pdata->pc; + freerdp_client_stop(pc); + free(pdata); + freerdp_client_context_free(pc); + client->Disconnect(client); + freerdp_peer_context_free(client); + freerdp_peer_free(client); + return 0; +} + +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))) + return FALSE; + + CloseHandle(hThread); + return TRUE; +} + +static void pf_server_mainloop(freerdp_listener* listener) +{ + HANDLE eventHandles[32]; + DWORD eventCount; + DWORD status; + + while (1) + { + eventCount = listener->GetEventHandles(listener, eventHandles, 32); + + if (0 == eventCount) + { + WLog_ERR(TAG, "Failed to get FreeRDP event handles"); + break; + } + + status = WaitForMultipleObjects(eventCount, eventHandles, FALSE, INFINITE); + + if (WAIT_FAILED == status) + { + WLog_ERR(TAG, "select failed"); + break; + } + + if (listener->CheckFileDescriptor(listener) != TRUE) + { + WLog_ERR(TAG, "Failed to check FreeRDP file descriptor"); + break; + } + } + + listener->Close(listener); +} + +int pf_server_start(proxyConfig* config) +{ + char* localSockPath; + char localSockName[MAX_PATH]; + BOOL success; + WSADATA wsaData; + freerdp_listener* listener = freerdp_listener_new(); + + if (!listener) + return -1; + + WTSRegisterWtsApiFunctionTable(FreeRDP_InitWtsApi()); + winpr_InitializeSSL(WINPR_SSL_INIT_DEFAULT); + listener->info = config; + listener->PeerAccepted = pf_server_client_connected; + + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) + { + freerdp_listener_free(listener); + return -1; + } + + /* Determine filepath for local socket */ + sprintf_s(localSockName, sizeof(localSockName), "proxy.%"PRIu16"", config->Port); + localSockPath = GetKnownSubPath(KNOWN_PATH_TEMP, localSockName); + + if (!localSockPath) + { + freerdp_listener_free(listener); + WSACleanup(); + return -1; + } + + /* Listen to local connections */ + success = listener->OpenLocal(listener, localSockPath); + + /* Listen to remote connections */ + if (!config->LocalOnly) + { + success &= listener->Open(listener, config->Host, config->Port); + } + + if (success) + { + pf_server_mainloop(listener); + } + + free(localSockPath); + freerdp_listener_free(listener); + WSACleanup(); + return 0; +} diff --git a/server/proxy/pf_server.h b/server/proxy/pf_server.h new file mode 100644 index 000000000..1da72ddd0 --- /dev/null +++ b/server/proxy/pf_server.h @@ -0,0 +1,29 @@ +/** + * 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_SERVER_H +#define FREERDP_SERVER_PROXY_SERVER_H + +#include "pf_config.h" + +int pf_server_start(proxyConfig* config); + +#endif /* FREERDP_SERVER_PROXY_SERVER_H */ diff --git a/server/proxy/pf_update.c b/server/proxy/pf_update.c new file mode 100644 index 000000000..a2d0a792e --- /dev/null +++ b/server/proxy/pf_update.c @@ -0,0 +1,45 @@ +/** + * 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 "pf_update.h" +#include "pf_context.h" + +static BOOL pf_server_refresh_rect(rdpContext* context, BYTE count, + const RECTANGLE_16* areas) +{ + pServerContext* ps = (pServerContext*)context; + rdpContext* pc = (rdpContext*) ps->pdata->pc; + return pc->update->RefreshRect(pc, count, areas); +} + +static BOOL pf_server_suppress_output(rdpContext* context, BYTE allow, + const RECTANGLE_16* area) +{ + pServerContext* ps = (pServerContext*)context; + rdpContext* pc = (rdpContext*) ps->pdata->pc; + return pc->update->SuppressOutput(pc, allow, area); +} + +void pf_server_register_update_callbacks(rdpUpdate* update) +{ + update->RefreshRect = pf_server_refresh_rect; + update->SuppressOutput = pf_server_suppress_output; +} diff --git a/server/proxy/pf_update.h b/server/proxy/pf_update.h new file mode 100644 index 000000000..828c32b92 --- /dev/null +++ b/server/proxy/pf_update.h @@ -0,0 +1,29 @@ +/** + * 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_PFUPDATE_H +#define FREERDP_SERVER_PROXY_PFUPDATE_H + +#include + +void pf_server_register_update_callbacks(rdpUpdate* input); + +#endif /* FREERDP_SERVER_PROXY_PFUPDATE_H */ diff --git a/server/proxy/server.crt b/server/proxy/server.crt new file mode 100644 index 000000000..142c50973 --- /dev/null +++ b/server/proxy/server.crt @@ -0,0 +1,17 @@ +-----BEGIN CERTIFICATE----- +MIICyjCCAbKgAwIBAgIEIZtPcjANBgkqhkiG9w0BAQUFADAUMRIwEAYDVQQDEwlj +aGFtZWxlb24wHhcNMTQwNDE4MDMzNDIyWhcNMTUwNDE4MDMzNDIyWjAUMRIwEAYD +VQQDEwljaGFtZWxlb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCz +oGjWnPFjPPPi/iJOooPgmf1IMwQHY7VWrMegtrotnuSteW/m4r0QWSceYM8+oeIV +iU71AzNi074GR9EVbtXZCSgxn00jpPK+tgDBjbLmmqeCcwRkqpbBrS77/+Mq7UZM +lSRLPv74R01vUM5DyeII4WHf0C4T07BDiOnfFvs3T2AaLMEawfWT2408sXCZxgbY +IHp0WKnRXHQcG2Ys0iOSewe0iqhw8ODY/ze/eFtJ/5vdwCpN8AUOdU9PCXIA639d +ni9hKn7EXUvUZQ/SliBSaXsTfEw2Iu8Bo0/dcUfOwrPK8G2MOROi9GW80Prxtj+w +PTp7Z7h/JJCpygMkzmohAgMBAAGjJDAiMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAsG +A1UdDwQEAwIEMDANBgkqhkiG9w0BAQUFAAOCAQEAaq84r/SZaUZYlpDnE/V3cJI2 +Uzh8B81DqgVTgMqeuVAKAcgOEVf04Cp0oQ3+nS90a+afiqrT3A1eX5Wa1gxNkAIa +m26cSysGVl4I1xoXyddjZOIRy6GYVjdchdOGkE/lBy+NtuP0xwCkscUIQ6N8Es7r +DY8yMxtERYG2FHvnvZaw9oqstDpQtXoqzrl1JHz1nMcaVuFzyzdJdZrGvgQdMR7g +X1OT6dvmUYrUFGDxdPg+HHVeB4S5rUpvQUpqX/PGTwl8PIhahaqdPwHGsHfqp9Rk +ttjPkAw1fn7CUMj+OuNmF3WDSh4k+3chNyz/bkJqpPxjPXCYSXrlgJLwcmlKXA== +-----END CERTIFICATE----- diff --git a/server/proxy/server.key b/server/proxy/server.key new file mode 100644 index 000000000..e7030b651 --- /dev/null +++ b/server/proxy/server.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCzoGjWnPFjPPPi +/iJOooPgmf1IMwQHY7VWrMegtrotnuSteW/m4r0QWSceYM8+oeIViU71AzNi074G +R9EVbtXZCSgxn00jpPK+tgDBjbLmmqeCcwRkqpbBrS77/+Mq7UZMlSRLPv74R01v +UM5DyeII4WHf0C4T07BDiOnfFvs3T2AaLMEawfWT2408sXCZxgbYIHp0WKnRXHQc +G2Ys0iOSewe0iqhw8ODY/ze/eFtJ/5vdwCpN8AUOdU9PCXIA639dni9hKn7EXUvU +ZQ/SliBSaXsTfEw2Iu8Bo0/dcUfOwrPK8G2MOROi9GW80Prxtj+wPTp7Z7h/JJCp +ygMkzmohAgMBAAECggEBAJxXJIiOxuZZ+ef6yz9n4aKORM4fYQVRela7QXEXOYpV +frGiPYxWkrn07sBZAGiCG7Yk8Wz9YS9GHmF3BntCIayqdKpj/Y7llUX6QUQ1oZ1m +xB8dHpjDhT2DD57UBBOruwES2ikH5oGQBcy6Jx0HTM3v5PEZT9F/8S2GsA+0/+gN +n5RKGds9nMN+GhGJoCPZTPqHxxsW7p0+W8Sqnv6dQ/lN9E8Iesag4H5my1+lpEWb +kI6h4Llk2xHUrcb3wL3+ttYLkkd8DGn7O/URKVbFwxPZLkx2kR2LFwL9wx88+HJ+ ++zPBaQ5JBBvJ7s94iZPW1/rfbiVn8jZJHbzs4IrsvVUCgYEA3fciYy5BHVsNMmFW +NBxgLaNbMDjaCcN7bBItCpzpBKp/INDLIX31u6MH/OxcFCZGwuW0Tq85PXsgFfJW +rjFouqeGQDy0MFw1aSwz1Y6h+J0OM4Xtqh3wQtqrQKRJi4JjR8Nrw8NwfBZnQXwe +XrlgMSwqev0NbvVpYVTZHsDRs+MCgYEAzytXGiBnqG7micj2Ic+gABBiD9pkYFCa +LMAiFn7JqWTyvBL9Yte7QO6S5sxfuCxV0/tcanehP6KqQEC1cOenefy4JTM8M1pi +Osp+WPokxRGEN+enhDxA7IKAtIC5lV8vPFYpxel3ALwzQyIB11a7QwyZ0HV9DGyD +SWgqMV16cSsCgYEApKmznvQvZgGn9pnzA1GRoAmxmkJyrg+Qvgl11Iu26bk5+jQq +nTv+UiaNxHnrjR8gG3Ggo0amJg/zK5TN7QfGc8HXfEwMOFwPW2hpQ6I+UlrgsCRI +vYzcMhxaMugtjws5b4FvrpiMF+He09uqBAdtbs0e7oJPtuLkPEpfj8rnRpUCgYEA +l1TgdoxPTCFetC2hZgdlqEZPa6crhZO7W48qF2acZun/ylTQ1WAjQAZGNuP50+5G +7+eAlaE3n+VyGidqosQFeb2awknP4u9WK/35F4P6Uh4+iBvnKVheUKXv4Grbpfp2 +5ctHDnRBYr8XbyWrVKLdfdf5j+YS531o1bmKgK75HysCgYAi1jqD5KSG5sLhO0kF +7BD9qYTIzW6Ocn4x1FRY6kxyGrE7Vb7VGNW2ULVexyn73pxhueuS4Qy5xHVDmdhO +ibolvvEr2TnSC0XR7QfOeXjmyV0m5Uvz97QNxoMnAHA60LWy8isj97LRAXuoSLEp +f2wfaMPwVN0WlBwa2PWAVm3zWA== +-----END PRIVATE KEY-----