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 <matishabtay@gmail.com>

* 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 <matishabtay@gmail.com>

* 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 <kmizrachi18@gmail.com>
This commit is contained in:
Mati Shabtay 2019-05-17 15:32:54 +03:00 committed by akallabeth
parent d96a61d8ca
commit b907324009
36 changed files with 2794 additions and 0 deletions

1
.gitignore vendored
View File

@ -104,6 +104,7 @@ client/Sample/sfreerdp
client/Wayland/wlfreerdp client/Wayland/wlfreerdp
server/Sample/sfreerdp-server server/Sample/sfreerdp-server
server/X11/xfreerdp-server server/X11/xfreerdp-server
server/proxy/freerdp-proxy
xcode xcode
libfreerdp/codec/test/TestOpenH264ASM libfreerdp/codec/test/TestOpenH264ASM

View File

@ -490,6 +490,8 @@ FREERDP_API void setChannelError(rdpContext* context, UINT errorNum,
char* description); char* description);
FREERDP_API BOOL checkChannelErrorEvent(rdpContext* context); FREERDP_API BOOL checkChannelErrorEvent(rdpContext* context);
FREERDP_API const char* freerdp_nego_get_routing_token(rdpContext* context, DWORD* length);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@ -21,6 +21,7 @@
#define FREERDP_CHANNEL_RDPGFX_SERVER_RDPGFX_H #define FREERDP_CHANNEL_RDPGFX_SERVER_RDPGFX_H
#include <freerdp/channels/rdpgfx.h> #include <freerdp/channels/rdpgfx.h>
#include <freerdp/freerdp.h>
typedef struct _rdpgfx_server_context RdpgfxServerContext; typedef struct _rdpgfx_server_context RdpgfxServerContext;
typedef struct _rdpgfx_server_private RdpgfxServerPrivate; typedef struct _rdpgfx_server_private RdpgfxServerPrivate;

View File

@ -1084,3 +1084,11 @@ void setChannelError(rdpContext* context, UINT errorNum, char* description)
strncpy(context->errorDescription, description, 499); strncpy(context->errorDescription, description, 499);
SetEvent(context->channelErrorEvent); 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);
}

View File

@ -1440,3 +1440,12 @@ void nego_free_nla(rdpNego* nego)
nla_free(nego->transport->nla); nla_free(nego->transport->nla);
nego->transport->nla = NULL; 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;
}

View File

@ -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_tls(rdpNego* nego, BOOL enable_tls);
FREERDP_LOCAL void nego_enable_nla(rdpNego* nego, BOOL enable_nla); FREERDP_LOCAL void nego_enable_nla(rdpNego* nego, BOOL enable_nla);
FREERDP_LOCAL void nego_enable_ext(rdpNego* nego, BOOL enable_ext); 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, FREERDP_LOCAL BOOL nego_set_routing_token(rdpNego* nego, BYTE* RoutingToken,
DWORD RoutingTokenLength); DWORD RoutingTokenLength);
FREERDP_LOCAL BOOL nego_set_cookie(rdpNego* nego, char* cookie); FREERDP_LOCAL BOOL nego_set_cookie(rdpNego* nego, char* cookie);

1
server/.gitignore vendored
View File

@ -1,4 +1,5 @@
/* /*
!/proxy
!/common !/common
!/Mac !/Mac
!/Sample !/Sample

View File

@ -19,6 +19,7 @@
add_subdirectory(common) add_subdirectory(common)
add_subdirectory(shadow) add_subdirectory(shadow)
add_subdirectory(proxy)
if(FREERDP_VENDOR) if(FREERDP_VENDOR)
if(WITH_SAMPLE) if(WITH_SAMPLE)

View File

@ -0,0 +1,76 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP Proxy Server
#
# Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
# Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
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")

33
server/proxy/config.ini Normal file
View File

@ -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=<target server>", 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"

View File

@ -0,0 +1,77 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Proxy Server
*
* Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "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;
}

View File

@ -0,0 +1,80 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Proxy Server
*
* Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <freerdp/gdi/gfx.h>
#include <freerdp/client/rdpei.h>
#include <freerdp/client/tsmf.h>
#include <freerdp/client/rail.h>
#include <freerdp/client/cliprdr.h>
#include <freerdp/client/rdpgfx.h>
#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);
}
}

View File

@ -0,0 +1,33 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Proxy Server
*
* Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_SERVER_PROXY_PFCHANNELS_H
#define FREERDP_SERVER_PROXY_PFCHANNELS_H
#include <freerdp/freerdp.h>
#include <freerdp/client/channels.h>
void pf_OnChannelConnectedEventHandler(void* context,
ChannelConnectedEventArgs* e);
void pf_OnChannelDisconnectedEventHandler(void* context,
ChannelDisconnectedEventArgs* e);
#endif /* FREERDP_SERVER_PROXY_PFCHANNELS_H */

415
server/proxy/pf_client.c Normal file
View File

@ -0,0 +1,415 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Proxy Server
*
* Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <freerdp/freerdp.h>
#include <freerdp/constants.h>
#include <freerdp/gdi/gdi.h>
#include <freerdp/utils/signal.h>
#include <freerdp/client/file.h>
#include <freerdp/client/cmdline.h>
#include <freerdp/client/cliprdr.h>
#include <freerdp/client/channels.h>
#include <freerdp/channels/channels.h>
#include <winpr/crt.h>
#include <winpr/synch.h>
#include <freerdp/log.h>
#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);
}

31
server/proxy/pf_client.h Normal file
View File

@ -0,0 +1,31 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Proxy Server
*
* Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_SERVER_PROXY_PFCLIENT_H
#define FREERDP_SERVER_PROXY_PFCLIENT_H
#include <freerdp/freerdp.h>
#include <winpr/wtypes.h>
int RdpClientEntry(RDP_CLIENT_ENTRY_POINTS* pEntryPoints);
DWORD WINAPI pf_client_start(LPVOID arg);
#endif /* FREERDP_SERVER_PROXY_PFCLIENT_H */

107
server/proxy/pf_common.c Normal file
View File

@ -0,0 +1,107 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Proxy Server
*
* Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "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;
}

31
server/proxy/pf_common.h Normal file
View File

@ -0,0 +1,31 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Proxy Server
*
* Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_SERVER_PROXY_PFCOMMON_H
#define FREERDP_SERVER_PROXY_PFCOMMON_H
#include <freerdp/freerdp.h>
#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 */

188
server/proxy/pf_config.c Normal file
View File

@ -0,0 +1,188 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Proxy Server
*
* Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdio.h>
#include <string.h>
#include <winpr/crt.h>
#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);
}

71
server/proxy/pf_config.h Normal file
View File

@ -0,0 +1,71 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Proxy Server
*
* Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_SERVER_PROXY_PFCONFIG_H
#define FREERDP_SERVER_PROXY_PFCONFIG_H
#define CONFIG_PARSE_SUCCESS 0
#define CONFIG_PARSE_ERROR 1
#define CONFIG_INVALID 2
#include <winpr/ini.h>
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 */

81
server/proxy/pf_context.c Normal file
View File

@ -0,0 +1,81 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Proxy Server
*
* Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "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;
}

83
server/proxy/pf_context.h Normal file
View File

@ -0,0 +1,83 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Proxy Server
*
* Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_SERVER_PROXY_PFCONTEXT_H
#define FREERDP_SERVER_PROXY_PFCONTEXT_H
#include <freerdp/freerdp.h>
#include <freerdp/channels/wtsvc.h>
#include <freerdp/client/rdpei.h>
#include <freerdp/client/rdpgfx.h>
#include <freerdp/server/rdpgfx.h>
#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 */

151
server/proxy/pf_gdi.c Normal file
View File

@ -0,0 +1,151 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Proxy Server
*
* Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <freerdp/gdi/gdi.h>
#include <freerdp/codec/rfx.h>
#include <freerdp/codec/nsc.h>
#include <freerdp/constants.h>
#include <freerdp/codec/color.h>
#include <freerdp/codec/bitmap.h>
#include <freerdp/freerdp.h>
#include "pf_gdi.h"
#include "pf_log.h"
#include <freerdp/log.h>
#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;
}

29
server/proxy/pf_gdi.h Normal file
View File

@ -0,0 +1,29 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Proxy Server
*
* Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_SERVER_PROXY_PFGDI_H
#define FREERDP_SERVER_PROXY_PFGDI_H
#include <freerdp/gdi/gdi.h>
void pf_gdi_register_update_callbacks(rdpUpdate* update);
#endif /* FREERDP_SERVER_PROXY_PFGDI_H */

171
server/proxy/pf_graphics.c Normal file
View File

@ -0,0 +1,171 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Proxy Server
*
* Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <winpr/crt.h>
#include <freerdp/codec/bitmap.h>
#include "pf_graphics.h"
#include "pf_log.h"
#include "pf_gdi.h"
#include "pf_context.h"
#include <freerdp/gdi/dc.h>
#include <freerdp/gdi/shape.h>
#include <freerdp/gdi/region.h>
#include <freerdp/gdi/bitmap.h>
#include <freerdp/gdi/bitmap.h>
#include <freerdp/graphics.h>
#include <freerdp/log.h>
#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;
}

View File

@ -0,0 +1,31 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Proxy Server
*
* Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_SERVER_PROXY_PFGRAPHICS_H
#define FREERDP_SERVER_PROXY_PFGRAPHICS_H
#include <freerdp/freerdp.h>
#include "pf_client.h"
BOOL pf_register_pointer(rdpGraphics* graphics);
BOOL pf_register_graphics(rdpGraphics* graphics);
#endif /* FREERDP_SERVER_PROXY_PFGRAPHICS_H */

93
server/proxy/pf_input.c Normal file
View File

@ -0,0 +1,93 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Proxy Server
*
* Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "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;
}

29
server/proxy/pf_input.h Normal file
View File

@ -0,0 +1,29 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Proxy Server
*
* Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_SERVER_PROXY_PFINPUT_H
#define FREERDP_SERVER_PROXY_PFINPUT_H
#include <freerdp/freerdp.h>
void pf_server_register_input_callbacks(rdpInput* input);
#endif /* FREERDP_SERVER_PROXY_PFINPUT_H */

29
server/proxy/pf_log.h Normal file
View File

@ -0,0 +1,29 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Proxy Server
*
* Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_SERVER_PROXY_PFLOG_H
#define FREERDP_SERVER_PROXY_PFLOG_H
#include <winpr/wlog.h>
#define PROXY_TAG(tag) "proxy." tag
#endif /* FREERDP_SERVER_PROXY_PFLOG_H */

319
server/proxy/pf_rdpgfx.c Normal file
View File

@ -0,0 +1,319 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Proxy Server
*
* Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <freerdp/client/rdpgfx.h>
#include <freerdp/server/rdpgfx.h>
#include <winpr/synch.h>
#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;
}

34
server/proxy/pf_rdpgfx.h Normal file
View File

@ -0,0 +1,34 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Proxy Server
*
* Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_SERVER_PROXY_PFRDPGFX_H
#define FREERDP_SERVER_PROXY_PFRDPGFX_H
#include <freerdp/client/rdpgfx.h>
#include <freerdp/server/rdpgfx.h>
#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*/

430
server/proxy/pf_server.c Normal file
View File

@ -0,0 +1,430 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Proxy Server
*
* Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <errno.h>
#include <signal.h>
#include <freerdp/freerdp.h>
#include <freerdp/listener.h>
#include <winpr/crt.h>
#include <winpr/ssl.h>
#include <winpr/synch.h>
#include <winpr/string.h>
#include <winpr/path.h>
#include <winpr/winsock.h>
#include <winpr/thread.h>
#include <freerdp/channels/wtsvc.h>
#include <freerdp/channels/channels.h>
#include <freerdp/constants.h>
#include <freerdp/server/rdpsnd.h>
#include <freerdp/server/rdpgfx.h>
#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;
}

29
server/proxy/pf_server.h Normal file
View File

@ -0,0 +1,29 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Proxy Server
*
* Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_SERVER_PROXY_SERVER_H
#define FREERDP_SERVER_PROXY_SERVER_H
#include "pf_config.h"
int pf_server_start(proxyConfig* config);
#endif /* FREERDP_SERVER_PROXY_SERVER_H */

45
server/proxy/pf_update.c Normal file
View File

@ -0,0 +1,45 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Proxy Server
*
* Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "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;
}

29
server/proxy/pf_update.h Normal file
View File

@ -0,0 +1,29 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* FreeRDP Proxy Server
*
* Copyright 2019 Mati Shabtay <matishabtay@gmail.com>
* Copyright 2019 Kobi Mizrachi <kmizrachi18@gmail.com>
* Copyright 2019 Idan Freiberg <speidy@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef FREERDP_SERVER_PROXY_PFUPDATE_H
#define FREERDP_SERVER_PROXY_PFUPDATE_H
#include <freerdp/freerdp.h>
void pf_server_register_update_callbacks(rdpUpdate* input);
#endif /* FREERDP_SERVER_PROXY_PFUPDATE_H */

17
server/proxy/server.crt Normal file
View File

@ -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-----

28
server/proxy/server.key Normal file
View File

@ -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-----