mirror of
https://github.com/FreeRDP/FreeRDP.git
synced 2024-11-27 03:44:06 +08:00
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:
parent
d96a61d8ca
commit
b907324009
1
.gitignore
vendored
1
.gitignore
vendored
@ -104,6 +104,7 @@ client/Sample/sfreerdp
|
||||
client/Wayland/wlfreerdp
|
||||
server/Sample/sfreerdp-server
|
||||
server/X11/xfreerdp-server
|
||||
server/proxy/freerdp-proxy
|
||||
xcode
|
||||
libfreerdp/codec/test/TestOpenH264ASM
|
||||
|
||||
|
@ -490,6 +490,8 @@ FREERDP_API void setChannelError(rdpContext* context, UINT errorNum,
|
||||
char* description);
|
||||
FREERDP_API BOOL checkChannelErrorEvent(rdpContext* context);
|
||||
|
||||
FREERDP_API const char* freerdp_nego_get_routing_token(rdpContext* context, DWORD* length);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@ -21,6 +21,7 @@
|
||||
#define FREERDP_CHANNEL_RDPGFX_SERVER_RDPGFX_H
|
||||
|
||||
#include <freerdp/channels/rdpgfx.h>
|
||||
#include <freerdp/freerdp.h>
|
||||
|
||||
typedef struct _rdpgfx_server_context RdpgfxServerContext;
|
||||
typedef struct _rdpgfx_server_private RdpgfxServerPrivate;
|
||||
|
@ -1084,3 +1084,11 @@ void setChannelError(rdpContext* context, UINT errorNum, char* description)
|
||||
strncpy(context->errorDescription, description, 499);
|
||||
SetEvent(context->channelErrorEvent);
|
||||
}
|
||||
|
||||
const char* freerdp_nego_get_routing_token(rdpContext* context, DWORD* length)
|
||||
{
|
||||
if (!context || !context->rdp)
|
||||
return NULL;
|
||||
|
||||
return (const char*)nego_get_routing_token(context->rdp->nego, length);
|
||||
}
|
||||
|
@ -1440,3 +1440,12 @@ void nego_free_nla(rdpNego* nego)
|
||||
nla_free(nego->transport->nla);
|
||||
nego->transport->nla = NULL;
|
||||
}
|
||||
|
||||
const BYTE* nego_get_routing_token(rdpNego* nego, DWORD* RoutingTokenLength)
|
||||
{
|
||||
if (!nego)
|
||||
return NULL;
|
||||
if (RoutingTokenLength)
|
||||
*RoutingTokenLength = nego->RoutingTokenLength;
|
||||
return nego->RoutingToken;
|
||||
}
|
||||
|
@ -119,6 +119,7 @@ FREERDP_LOCAL void nego_enable_rdp(rdpNego* nego, BOOL enable_rdp);
|
||||
FREERDP_LOCAL void nego_enable_tls(rdpNego* nego, BOOL enable_tls);
|
||||
FREERDP_LOCAL void nego_enable_nla(rdpNego* nego, BOOL enable_nla);
|
||||
FREERDP_LOCAL void nego_enable_ext(rdpNego* nego, BOOL enable_ext);
|
||||
FREERDP_LOCAL const BYTE* nego_get_routing_token(rdpNego* nego, DWORD* RoutingTokenLength);
|
||||
FREERDP_LOCAL BOOL nego_set_routing_token(rdpNego* nego, BYTE* RoutingToken,
|
||||
DWORD RoutingTokenLength);
|
||||
FREERDP_LOCAL BOOL nego_set_cookie(rdpNego* nego, char* cookie);
|
||||
|
1
server/.gitignore
vendored
1
server/.gitignore
vendored
@ -1,4 +1,5 @@
|
||||
/*
|
||||
!/proxy
|
||||
!/common
|
||||
!/Mac
|
||||
!/Sample
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
add_subdirectory(common)
|
||||
add_subdirectory(shadow)
|
||||
add_subdirectory(proxy)
|
||||
|
||||
if(FREERDP_VENDOR)
|
||||
if(WITH_SAMPLE)
|
||||
|
76
server/proxy/CMakeLists.txt
Normal file
76
server/proxy/CMakeLists.txt
Normal 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
33
server/proxy/config.ini
Normal 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"
|
77
server/proxy/freerdp_proxy.c
Normal file
77
server/proxy/freerdp_proxy.c
Normal 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;
|
||||
}
|
80
server/proxy/pf_channels.c
Normal file
80
server/proxy/pf_channels.c
Normal 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);
|
||||
}
|
||||
}
|
33
server/proxy/pf_channels.h
Normal file
33
server/proxy/pf_channels.h
Normal 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
415
server/proxy/pf_client.c
Normal 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
31
server/proxy/pf_client.h
Normal 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
107
server/proxy/pf_common.c
Normal 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
31
server/proxy/pf_common.h
Normal 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
188
server/proxy/pf_config.c
Normal 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
71
server/proxy/pf_config.h
Normal 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
81
server/proxy/pf_context.c
Normal 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
83
server/proxy/pf_context.h
Normal 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
151
server/proxy/pf_gdi.c
Normal 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
29
server/proxy/pf_gdi.h
Normal 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
171
server/proxy/pf_graphics.c
Normal 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;
|
||||
}
|
31
server/proxy/pf_graphics.h
Normal file
31
server/proxy/pf_graphics.h
Normal 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
93
server/proxy/pf_input.c
Normal 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
29
server/proxy/pf_input.h
Normal 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
29
server/proxy/pf_log.h
Normal 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
319
server/proxy/pf_rdpgfx.c
Normal 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
34
server/proxy/pf_rdpgfx.h
Normal 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
430
server/proxy/pf_server.c
Normal 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
29
server/proxy/pf_server.h
Normal 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
45
server/proxy/pf_update.c
Normal 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
29
server/proxy/pf_update.h
Normal 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
17
server/proxy/server.crt
Normal 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
28
server/proxy/server.key
Normal 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-----
|
Loading…
Reference in New Issue
Block a user