From d9366df448fcd31d5edf7779d72bd710768db888 Mon Sep 17 00:00:00 2001 From: Kobi Date: Tue, 11 Jun 2019 07:13:42 -0700 Subject: [PATCH] RDPEDISP server side implementation (#5414) * libfreerdp: Add RDPEDISP server implementation * server/proxy: Add RDPEDISP support --- channels/disp/CMakeLists.txt | 4 + channels/disp/client/CMakeLists.txt | 4 +- channels/disp/client/disp_main.c | 101 +++-- channels/disp/client/disp_main.h | 2 - channels/disp/disp_common.c | 60 +++ channels/disp/disp_common.h | 32 ++ channels/disp/server/CMakeLists.txt | 32 ++ channels/disp/server/disp_main.c | 576 ++++++++++++++++++++++++++++ channels/disp/server/disp_main.h | 37 ++ channels/server/channels.c | 4 + include/freerdp/channels/disp.h | 82 ++++ include/freerdp/client/disp.h | 37 +- include/freerdp/server/disp.h | 69 ++++ server/proxy/CMakeLists.txt | 2 + server/proxy/pf_channels.c | 30 +- server/proxy/pf_common.c | 6 +- server/proxy/pf_context.h | 7 + server/proxy/pf_disp.c | 55 +++ server/proxy/pf_disp.h | 34 ++ server/proxy/pf_server.c | 19 +- server/proxy/pf_update.c | 2 +- 21 files changed, 1103 insertions(+), 92 deletions(-) mode change 100755 => 100644 channels/disp/client/disp_main.c create mode 100644 channels/disp/disp_common.c create mode 100644 channels/disp/disp_common.h create mode 100644 channels/disp/server/CMakeLists.txt create mode 100644 channels/disp/server/disp_main.c create mode 100644 channels/disp/server/disp_main.h create mode 100644 include/freerdp/channels/disp.h create mode 100644 include/freerdp/server/disp.h create mode 100644 server/proxy/pf_disp.c create mode 100644 server/proxy/pf_disp.h diff --git a/channels/disp/CMakeLists.txt b/channels/disp/CMakeLists.txt index 541892d95..44afe99f8 100644 --- a/channels/disp/CMakeLists.txt +++ b/channels/disp/CMakeLists.txt @@ -20,3 +20,7 @@ define_channel("disp") if(WITH_CLIENT_CHANNELS) add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME}) endif() + +if(WITH_SERVER_CHANNELS) + add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME}) +endif() diff --git a/channels/disp/client/CMakeLists.txt b/channels/disp/client/CMakeLists.txt index 1ec2c6f9d..6376f6e47 100644 --- a/channels/disp/client/CMakeLists.txt +++ b/channels/disp/client/CMakeLists.txt @@ -19,7 +19,9 @@ define_channel_client("disp") set(${MODULE_PREFIX}_SRCS disp_main.c - disp_main.h) + disp_main.h + ../disp_common.c + ../disp_common.h) include_directories(..) diff --git a/channels/disp/client/disp_main.c b/channels/disp/client/disp_main.c old mode 100755 new mode 100644 index 0555317d5..cbea79027 --- a/channels/disp/client/disp_main.c +++ b/channels/disp/client/disp_main.c @@ -40,6 +40,7 @@ #include #include "disp_main.h" +#include "../disp_common.h" struct _DISP_CHANNEL_CALLBACK { @@ -79,40 +80,39 @@ typedef struct _DISP_PLUGIN DISP_PLUGIN; * * @return 0 on success, otherwise a Win32 error code */ -UINT disp_send_display_control_monitor_layout_pdu(DISP_CHANNEL_CALLBACK* callback, UINT32 NumMonitors, DISPLAY_CONTROL_MONITOR_LAYOUT* Monitors) +UINT disp_send_display_control_monitor_layout_pdu(DISP_CHANNEL_CALLBACK* callback, + UINT32 NumMonitors, DISPLAY_CONTROL_MONITOR_LAYOUT* Monitors) { UINT status; wStream* s; - UINT32 type; UINT32 index; - UINT32 length; DISP_PLUGIN* disp; UINT32 MonitorLayoutSize; - + DISPLAY_CONTROL_HEADER header; disp = (DISP_PLUGIN*) callback->plugin; + MonitorLayoutSize = DISPLAY_CONTROL_MONITOR_LAYOUT_SIZE; + header.length = 8 + 8 + (NumMonitors * MonitorLayoutSize); + header.type = DISPLAY_CONTROL_PDU_TYPE_MONITOR_LAYOUT; - MonitorLayoutSize = 40; + s = Stream_New(NULL, header.length); - length = 8 + 8 + (NumMonitors * MonitorLayoutSize); - - type = DISPLAY_CONTROL_PDU_TYPE_MONITOR_LAYOUT; - - s = Stream_New(NULL, length); - if(!s) + if (!s) { WLog_ERR(TAG, "Stream_New failed!"); return CHANNEL_RC_NO_MEMORY; } - Stream_Write_UINT32(s, type); /* Type (4 bytes) */ - Stream_Write_UINT32(s, length); /* Length (4 bytes) */ + if ((status = disp_write_header(s, &header))) + { + WLog_ERR(TAG, "Failed to write header with error %"PRIu32"!", status); + goto out; + } if (NumMonitors > disp->MaxNumMonitors) NumMonitors = disp->MaxNumMonitors; Stream_Write_UINT32(s, MonitorLayoutSize); /* MonitorLayoutSize (4 bytes) */ Stream_Write_UINT32(s, NumMonitors); /* NumMonitors (4 bytes) */ - WLog_DBG(TAG, "disp_send_display_control_monitor_layout_pdu: NumMonitors=%"PRIu32"", NumMonitors); for (index = 0; index < NumMonitors; index++) @@ -144,20 +144,19 @@ UINT disp_send_display_control_monitor_layout_pdu(DISP_CHANNEL_CALLBACK* callbac Stream_Write_UINT32(s, Monitors[index].Orientation); /* Orientation (4 bytes) */ Stream_Write_UINT32(s, Monitors[index].DesktopScaleFactor); /* DesktopScaleFactor (4 bytes) */ Stream_Write_UINT32(s, Monitors[index].DeviceScaleFactor); /* DeviceScaleFactor (4 bytes) */ - - WLog_DBG(TAG, "\t%d : Flags: 0x%08"PRIX32" Left/Top: (%"PRId32",%"PRId32") W/H=%"PRIu32"x%"PRIu32")", index, - Monitors[index].Flags, Monitors[index].Left, Monitors[index].Top, Monitors[index].Width, - Monitors[index].Height); + WLog_DBG(TAG, + "\t%d : Flags: 0x%08"PRIX32" Left/Top: (%"PRId32",%"PRId32") W/H=%"PRIu32"x%"PRIu32")", index, + Monitors[index].Flags, Monitors[index].Left, Monitors[index].Top, Monitors[index].Width, + Monitors[index].Height); WLog_DBG(TAG, "\t PhysicalWidth: %"PRIu32" PhysicalHeight: %"PRIu32" Orientation: %"PRIu32"", - Monitors[index].PhysicalWidth, Monitors[index].PhysicalHeight, Monitors[index].Orientation); + Monitors[index].PhysicalWidth, Monitors[index].PhysicalHeight, Monitors[index].Orientation); } +out: Stream_SealLength(s); - - status = callback->channel->Write(callback->channel, (UINT32) Stream_Length(s), Stream_Buffer(s), NULL); - + status = callback->channel->Write(callback->channel, (UINT32) Stream_Length(s), Stream_Buffer(s), + NULL); Stream_Free(s, TRUE); - return status; } @@ -169,11 +168,10 @@ UINT disp_send_display_control_monitor_layout_pdu(DISP_CHANNEL_CALLBACK* callbac UINT disp_recv_display_control_caps_pdu(DISP_CHANNEL_CALLBACK* callback, wStream* s) { DISP_PLUGIN* disp; - DispClientContext *context; + DispClientContext* context; UINT ret = CHANNEL_RC_OK; - disp = (DISP_PLUGIN*) callback->plugin; - context = (DispClientContext *)disp->iface.pInterface; + context = (DispClientContext*)disp->iface.pInterface; if (Stream_GetRemainingLength(s) < 12) { @@ -186,7 +184,8 @@ UINT disp_recv_display_control_caps_pdu(DISP_CHANNEL_CALLBACK* callback, wStream Stream_Read_UINT32(s, disp->MaxMonitorAreaFactorB); /* MaxMonitorAreaFactorB (4 bytes) */ if (context->DisplayControlCaps) - ret = context->DisplayControlCaps(context, disp->MaxNumMonitors, disp->MaxMonitorAreaFactorA, disp->MaxMonitorAreaFactorB); + ret = context->DisplayControlCaps(context, disp->MaxNumMonitors, disp->MaxMonitorAreaFactorA, + disp->MaxMonitorAreaFactorB); return ret; } @@ -198,8 +197,8 @@ UINT disp_recv_display_control_caps_pdu(DISP_CHANNEL_CALLBACK* callback, wStream */ UINT disp_recv_pdu(DISP_CHANNEL_CALLBACK* callback, wStream* s) { - UINT32 type; - UINT32 length; + UINT32 error; + DISPLAY_CONTROL_HEADER header; if (Stream_GetRemainingLength(s) < 8) { @@ -207,18 +206,25 @@ UINT disp_recv_pdu(DISP_CHANNEL_CALLBACK* callback, wStream* s) return ERROR_INVALID_DATA; } - Stream_Read_UINT32(s, type); /* Type (4 bytes) */ - Stream_Read_UINT32(s, length); /* Length (4 bytes) */ + if ((error = disp_read_header(s, &header))) + { + WLog_ERR(TAG, "disp_read_header failed with error %"PRIu32"!", error); + return error; + } - //WLog_ERR(TAG, "Type: %"PRIu32" Length: %"PRIu32"", type, length); + if (!Stream_EnsureRemainingCapacity(s, header.length)) + { + WLog_ERR(TAG, "not enough remaining data"); + return ERROR_INVALID_DATA; + } - switch (type) + switch (header.type) { case DISPLAY_CONTROL_PDU_TYPE_CAPS: return disp_recv_display_control_caps_pdu(callback, s); default: - WLog_ERR(TAG, "Type %"PRIu32" not recognized!", type); + WLog_ERR(TAG, "Type %"PRIu32" not recognized!", header.type); return ERROR_INTERNAL_ERROR; } } @@ -228,10 +234,9 @@ UINT disp_recv_pdu(DISP_CHANNEL_CALLBACK* callback, wStream* s) * * @return 0 on success, otherwise a Win32 error code */ -static UINT disp_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream *data) +static UINT disp_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* data) { DISP_CHANNEL_CALLBACK* callback = (DISP_CHANNEL_CALLBACK*) pChannelCallback; - return disp_recv_pdu(callback, data); } @@ -252,12 +257,11 @@ static UINT disp_on_close(IWTSVirtualChannelCallback* pChannelCallback) * @return 0 on success, otherwise a Win32 error code */ static UINT disp_on_new_channel_connection(IWTSListenerCallback* pListenerCallback, - IWTSVirtualChannel* pChannel, BYTE* Data, BOOL* pbAccept, - IWTSVirtualChannelCallback** ppCallback) + IWTSVirtualChannel* pChannel, BYTE* Data, BOOL* pbAccept, + IWTSVirtualChannelCallback** ppCallback) { DISP_CHANNEL_CALLBACK* callback; DISP_LISTENER_CALLBACK* listener_callback = (DISP_LISTENER_CALLBACK*) pListenerCallback; - callback = (DISP_CHANNEL_CALLBACK*) calloc(1, sizeof(DISP_CHANNEL_CALLBACK)); if (!callback) @@ -272,9 +276,7 @@ static UINT disp_on_new_channel_connection(IWTSListenerCallback* pListenerCallba callback->channel_mgr = listener_callback->channel_mgr; callback->channel = pChannel; listener_callback->channel_callback = callback; - *ppCallback = (IWTSVirtualChannelCallback*) callback; - return CHANNEL_RC_OK; } @@ -287,7 +289,6 @@ static UINT disp_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManage { UINT status; DISP_PLUGIN* disp = (DISP_PLUGIN*) pPlugin; - disp->listener_callback = (DISP_LISTENER_CALLBACK*) calloc(1, sizeof(DISP_LISTENER_CALLBACK)); if (!disp->listener_callback) @@ -299,12 +300,9 @@ static UINT disp_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManage disp->listener_callback->iface.OnNewChannelConnection = disp_on_new_channel_connection; disp->listener_callback->plugin = pPlugin; disp->listener_callback->channel_mgr = pChannelMgr; - status = pChannelMgr->CreateListener(pChannelMgr, DISP_DVC_CHANNEL_NAME, 0, - (IWTSListenerCallback*) disp->listener_callback, &(disp->listener)); - + (IWTSListenerCallback*) disp->listener_callback, &(disp->listener)); disp->listener->pInterface = disp->iface.pInterface; - return status; } @@ -331,11 +329,11 @@ static UINT disp_plugin_terminated(IWTSPlugin* pPlugin) * * @return 0 on success, otherwise a Win32 error code */ -UINT disp_send_monitor_layout(DispClientContext* context, UINT32 NumMonitors, DISPLAY_CONTROL_MONITOR_LAYOUT* Monitors) +UINT disp_send_monitor_layout(DispClientContext* context, UINT32 NumMonitors, + DISPLAY_CONTROL_MONITOR_LAYOUT* Monitors) { DISP_PLUGIN* disp = (DISP_PLUGIN*) context->handle; DISP_CHANNEL_CALLBACK* callback = disp->listener_callback->channel_callback; - return disp_send_display_control_monitor_layout_pdu(callback, NumMonitors, Monitors); } @@ -355,11 +353,12 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints) UINT error = CHANNEL_RC_OK; DISP_PLUGIN* disp; DispClientContext* context; - disp = (DISP_PLUGIN*) pEntryPoints->GetPlugin(pEntryPoints, "disp"); + if (!disp) { disp = (DISP_PLUGIN*) calloc(1, sizeof(DISP_PLUGIN)); + if (!disp) { WLog_ERR(TAG, "calloc failed!"); @@ -373,8 +372,8 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints) disp->MaxNumMonitors = 16; disp->MaxMonitorAreaFactorA = 8192; disp->MaxMonitorAreaFactorB = 8192; - context = (DispClientContext*) calloc(1, sizeof(DispClientContext)); + if (!context) { WLog_ERR(TAG, "calloc failed!"); @@ -384,9 +383,7 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints) context->handle = (void*) disp; context->SendMonitorLayout = disp_send_monitor_layout; - disp->iface.pInterface = (void*) context; - error = pEntryPoints->RegisterPlugin(pEntryPoints, "disp", (IWTSPlugin*) disp); } else diff --git a/channels/disp/client/disp_main.h b/channels/disp/client/disp_main.h index c2aabae29..9d4f70251 100644 --- a/channels/disp/client/disp_main.h +++ b/channels/disp/client/disp_main.h @@ -33,8 +33,6 @@ #include -#define DISPLAY_CONTROL_PDU_TYPE_CAPS 0x00000005 -#define DISPLAY_CONTROL_PDU_TYPE_MONITOR_LAYOUT 0x00000002 #define TAG CHANNELS_TAG("disp.client") diff --git a/channels/disp/disp_common.c b/channels/disp/disp_common.c new file mode 100644 index 000000000..f4313d1f2 --- /dev/null +++ b/channels/disp/disp_common.c @@ -0,0 +1,60 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * RDPEDISP Virtual Channel Extension + * + * Copyright 2019 Kobi Mizrachi + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#define TAG CHANNELS_TAG("disp.common") + +#include "disp_common.h" + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +UINT disp_read_header(wStream* s, DISPLAY_CONTROL_HEADER* header) +{ + if (Stream_GetRemainingLength(s) < 8) + { + WLog_ERR(TAG, "header parsing failed: not enough data!"); + return ERROR_INVALID_DATA; + } + + Stream_Read_UINT32(s, header->type); + Stream_Read_UINT32(s, header->length); + return CHANNEL_RC_OK; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +UINT disp_write_header(wStream* s, const DISPLAY_CONTROL_HEADER* header) +{ + Stream_Write_UINT32(s, header->type); + Stream_Write_UINT32(s, header->length); + return CHANNEL_RC_OK; +} diff --git a/channels/disp/disp_common.h b/channels/disp/disp_common.h new file mode 100644 index 000000000..386b8b39d --- /dev/null +++ b/channels/disp/disp_common.h @@ -0,0 +1,32 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * RDPEDISP Virtual Channel Extension + * + * Copyright 2019 Kobi Mizrachi + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_CHANNEL_DISP_COMMON_H +#define FREERDP_CHANNEL_DISP_COMMON_H + +#include +#include + +#include +#include + +FREERDP_LOCAL UINT disp_read_header(wStream* s, DISPLAY_CONTROL_HEADER* header); +FREERDP_LOCAL UINT disp_write_header(wStream* s, const DISPLAY_CONTROL_HEADER* header); + +#endif /* FREERDP_CHANNEL_DISP_COMMON_H */ diff --git a/channels/disp/server/CMakeLists.txt b/channels/disp/server/CMakeLists.txt new file mode 100644 index 000000000..dddc15b05 --- /dev/null +++ b/channels/disp/server/CMakeLists.txt @@ -0,0 +1,32 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP cmake build script +# +# Copyright 2019 Kobi Mizrachi +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +define_channel_server("disp") + +set(${MODULE_PREFIX}_SRCS + disp_main.c + disp_main.h + ../disp_common.c + ../disp_common.h + ) + +include_directories(..) + +add_channel_server_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} FALSE "DVCPluginEntry") + +target_link_libraries(${MODULE_NAME} freerdp) +set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "Channels/${CHANNEL_NAME}/Server") diff --git a/channels/disp/server/disp_main.c b/channels/disp/server/disp_main.c new file mode 100644 index 000000000..8a632d015 --- /dev/null +++ b/channels/disp/server/disp_main.c @@ -0,0 +1,576 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * RDPEDISP Virtual Channel Extension + * + * Copyright 2019 Kobi Mizrachi + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "disp_main.h" +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + + +#include +#include "../disp_common.h" + +#define TAG CHANNELS_TAG("rdpedisp.server") + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ + +static wStream* disp_server_single_packet_new(UINT32 type, UINT32 length) +{ + UINT error; + DISPLAY_CONTROL_HEADER header; + wStream* s = Stream_New(NULL, DISPLAY_CONTROL_HEADER_LENGTH + length); + + if (!s) + { + WLog_ERR(TAG, "Stream_New failed!"); + goto error; + } + + header.type = type; + header.length = length; + + if ((error = disp_write_header(s, &header))) + { + WLog_ERR(TAG, "Failed to write header with error %"PRIu32"!", error); + goto error; + } + + return s; +error: + Stream_Free(s, TRUE); + return NULL; +} + +static BOOL disp_server_is_monitor_layout_valid(DISPLAY_CONTROL_MONITOR_LAYOUT* monitor) +{ + if (monitor->Width < DISPLAY_CONTROL_MIN_MONITOR_WIDTH || + monitor->Width > DISPLAY_CONTROL_MAX_MONITOR_WIDTH) + { + WLog_WARN(TAG, "Received invalid value for monitor->Width: %"PRIu32"", monitor->Width); + return FALSE; + } + + if (monitor->Height < DISPLAY_CONTROL_MIN_MONITOR_HEIGHT || + monitor->Height > DISPLAY_CONTROL_MAX_MONITOR_HEIGHT) + { + WLog_WARN(TAG, "Received invalid value for monitor->Height: %"PRIu32"", monitor->Width); + return FALSE; + } + + if (monitor->PhysicalWidth < DISPLAY_CONTROL_MIN_PHYSICAL_MONITOR_WIDTH || + monitor->PhysicalWidth > DISPLAY_CONTROL_MAX_PHYSICAL_MONITOR_WIDTH) + { + WLog_WARN(TAG, "Received invalid value for monitor->PhysicalWidth: %"PRIu32"", + monitor->PhysicalWidth); + return FALSE; + } + + if (monitor->PhysicalHeight < DISPLAY_CONTROL_MIN_PHYSICAL_MONITOR_HEIGHT || + monitor->PhysicalHeight > DISPLAY_CONTROL_MAX_PHYSICAL_MONITOR_HEIGHT) + { + WLog_WARN(TAG, "Received invalid value for monitor->Height: %"PRIu32"", monitor->PhysicalHeight); + return FALSE; + } + + switch (monitor->Orientation) + { + case ORIENTATION_LANDSCAPE: + case ORIENTATION_PORTRAIT: + case ORIENTATION_LANDSCAPE_FLIPPED: + case ORIENTATION_PORTRAIT_FLIPPED: + break; + + default: + WLog_WARN(TAG, "Received incorrect value for monitor->Orientation: %"PRIu32"", + monitor->Orientation); + return FALSE; + } + + return TRUE; +} + +static UINT disp_recv_display_control_monitor_layout_pdu(wStream* s, DispServerContext* context) +{ + UINT32 error = CHANNEL_RC_OK; + UINT32 index; + DISPLAY_CONTROL_MONITOR_LAYOUT_PDU pdu; + DISPLAY_CONTROL_MONITOR_LAYOUT* monitor; + + if (Stream_GetRemainingLength(s) < 8) + { + WLog_ERR(TAG, "not enough data!"); + return ERROR_INVALID_DATA; + } + + Stream_Read_UINT32(s, pdu.MonitorLayoutSize); /* MonitorLayoutSize (4 bytes) */ + + if (pdu.MonitorLayoutSize != DISPLAY_CONTROL_MONITOR_LAYOUT_SIZE) + { + WLog_ERR(TAG, "MonitorLayoutSize is set to %"PRIu32". expected %"PRIu32"", pdu.MonitorLayoutSize, + DISPLAY_CONTROL_MONITOR_LAYOUT_SIZE); + return ERROR_INVALID_DATA; + } + + Stream_Read_UINT32(s, pdu.NumMonitors); /* NumMonitors (4 bytes) */ + + if (pdu.NumMonitors > context->MaxNumMonitors) + { + WLog_ERR(TAG, "NumMonitors (%"PRIu32")> server MaxNumMonitors (%"PRIu32")", pdu.NumMonitors, + context->MaxNumMonitors); + return ERROR_INVALID_DATA; + } + + if (Stream_GetRemainingLength(s) < DISPLAY_CONTROL_MONITOR_LAYOUT_SIZE * pdu.NumMonitors) + { + WLog_ERR(TAG, "not enough data!"); + return ERROR_INVALID_DATA; + } + + pdu.Monitors = (DISPLAY_CONTROL_MONITOR_LAYOUT*) calloc(pdu.NumMonitors, + sizeof(DISPLAY_CONTROL_MONITOR_LAYOUT)); + + if (!pdu.Monitors) + { + WLog_ERR(TAG, "disp_recv_display_control_monitor_layout_pdu(): calloc failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + WLog_DBG(TAG, "disp_recv_display_control_monitor_layout_pdu: NumMonitors=%"PRIu32"", + pdu.NumMonitors); + + for (index = 0; index < pdu.NumMonitors; index++) + { + monitor = &(pdu.Monitors[index]); + Stream_Read_UINT32(s, monitor->Flags); /* Flags (4 bytes) */ + Stream_Read_UINT32(s, monitor->Left); /* Left (4 bytes) */ + Stream_Read_UINT32(s, monitor->Top); /* Top (4 bytes) */ + Stream_Read_UINT32(s, monitor->Width); /* Width (4 bytes) */ + Stream_Read_UINT32(s, monitor->Height); /* Height (4 bytes) */ + Stream_Read_UINT32(s, monitor->PhysicalWidth); /* PhysicalWidth (4 bytes) */ + Stream_Read_UINT32(s, monitor->PhysicalHeight); /* PhysicalHeight (4 bytes) */ + Stream_Read_UINT32(s, monitor->Orientation); /* Orientation (4 bytes) */ + Stream_Read_UINT32(s, monitor->DesktopScaleFactor); /* DesktopScaleFactor (4 bytes) */ + Stream_Read_UINT32(s, monitor->DeviceScaleFactor); /* DeviceScaleFactor (4 bytes) */ + WLog_DBG(TAG, + "\t%d : Flags: 0x%08"PRIX32" Left/Top: (%"PRId32",%"PRId32") W/H=%"PRIu32"x%"PRIu32")", index, + monitor->Flags, monitor->Left, monitor->Top, + monitor->Width, + monitor->Height); + WLog_DBG(TAG, "\t PhysicalWidth: %"PRIu32" PhysicalHeight: %"PRIu32" Orientation: %"PRIu32"", + monitor->PhysicalWidth, monitor->PhysicalHeight, + monitor->Orientation); + + if (!disp_server_is_monitor_layout_valid(monitor)) + { + error = ERROR_INVALID_DATA; + goto out; + } + } + + if (context) + IFCALLRET(context->DispMonitorLayout, error, context, &pdu); + +out: + free(pdu.Monitors); + return error; +} + + +static UINT disp_server_receive_pdu(DispServerContext* context, wStream* s) +{ + UINT error = CHANNEL_RC_OK; + size_t beg, end; + DISPLAY_CONTROL_HEADER header; + beg = Stream_GetPosition(s); + + if ((error = disp_read_header(s, &header))) + { + WLog_ERR(TAG, "disp_read_header failed with error %"PRIu32"!", error); + return error; + } + + switch (header.type) + { + case DISPLAY_CONTROL_PDU_TYPE_MONITOR_LAYOUT: + if ((error = disp_recv_display_control_monitor_layout_pdu(s, context))) + WLog_ERR(TAG, "disp_recv_display_control_monitor_layout_pdu " + "failed with error %"PRIu32"!", error); + + break; + + default: + error = CHANNEL_RC_BAD_PROC; + WLog_WARN(TAG, "Received unknown PDU type: %"PRIu32"", header.type); + break; + } + + end = Stream_GetPosition(s); + + if (end != (beg + header.length)) + { + WLog_ERR(TAG, "Unexpected DISP pdu end: Actual: %d, Expected: %"PRIu32"", + end, (beg + header.length)); + Stream_SetPosition(s, (beg + header.length)); + } + + return error; +} + +static UINT disp_server_handle_messages(DispServerContext* context) +{ + DWORD BytesReturned; + void* buffer; + UINT ret = CHANNEL_RC_OK; + DispServerPrivate* priv = context->priv; + wStream* s = priv->input_stream; + + /* Check whether the dynamic channel is ready */ + if (!priv->isReady) + { + if (WTSVirtualChannelQuery(priv->disp_channel, + WTSVirtualChannelReady, + &buffer, &BytesReturned) == FALSE) + { + if (GetLastError() == ERROR_NO_DATA) + return ERROR_NO_DATA; + + WLog_ERR(TAG, "WTSVirtualChannelQuery failed"); + return ERROR_INTERNAL_ERROR; + } + + priv->isReady = *((BOOL*) buffer); + WTSFreeMemory(buffer); + } + + /* Consume channel event only after the gfx dynamic channel is ready */ + Stream_SetPosition(s, 0); + + if (!WTSVirtualChannelRead(priv->disp_channel, + 0, NULL, 0, &BytesReturned)) + { + if (GetLastError() == ERROR_NO_DATA) + return ERROR_NO_DATA; + + WLog_ERR(TAG, "WTSVirtualChannelRead failed!"); + return ERROR_INTERNAL_ERROR; + } + + if (BytesReturned < 1) + return CHANNEL_RC_OK; + + if (!Stream_EnsureRemainingCapacity(s, BytesReturned)) + { + WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + if (WTSVirtualChannelRead(priv->disp_channel, 0, + (PCHAR) Stream_Buffer(s), + Stream_Capacity(s), &BytesReturned) == FALSE) + { + WLog_ERR(TAG, "WTSVirtualChannelRead failed!"); + return ERROR_INTERNAL_ERROR; + } + + Stream_SetLength(s, BytesReturned); + Stream_SetPosition(s, 0); + + while (Stream_GetPosition(s) < Stream_Length(s)) + { + if ((ret = disp_server_receive_pdu(context, s))) + { + WLog_ERR(TAG, "disp_server_receive_pdu " + "failed with error %"PRIu32"!", ret); + return ret; + } + } + + return ret; +} + +static DWORD WINAPI disp_server_thread_func(LPVOID arg) +{ + DispServerContext* context = (DispServerContext*) arg; + DispServerPrivate* priv = context->priv; + DWORD status; + DWORD nCount; + HANDLE events[8]; + UINT error = CHANNEL_RC_OK; + nCount = 0; + events[nCount++] = priv->stopEvent; + events[nCount++] = priv->channelEvent; + + /* Main virtual channel loop. RDPEDISP do not need version negotiation */ + while (TRUE) + { + status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE); + + if (status == WAIT_FAILED) + { + error = GetLastError(); + WLog_ERR(TAG, "WaitForMultipleObjects failed with error %"PRIu32"", error); + break; + } + + /* Stop Event */ + if (status == WAIT_OBJECT_0) + break; + + if ((error = disp_server_handle_messages(context))) + { + WLog_ERR(TAG, "disp_server_handle_messages failed with error %"PRIu32"", + error); + break; + } + } + + ExitThread(error); + return error; +} + + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT disp_server_open(DispServerContext* context) +{ + UINT rc = ERROR_INTERNAL_ERROR; + DispServerPrivate* priv = context->priv; + DWORD BytesReturned = 0; + PULONG pSessionId = NULL; + void* buffer; + buffer = NULL; + priv->SessionId = WTS_CURRENT_SESSION; + + if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, + WTSSessionId, (LPSTR*) &pSessionId, + &BytesReturned) == FALSE) + { + WLog_ERR(TAG, "WTSQuerySessionInformationA failed!"); + rc = ERROR_INTERNAL_ERROR; + goto out_close; + } + + priv->SessionId = (DWORD) * pSessionId; + priv->disp_channel = (HANDLE) WTSVirtualChannelOpenEx(priv->SessionId, + DISP_DVC_CHANNEL_NAME, + WTS_CHANNEL_OPTION_DYNAMIC); + + if (!priv->disp_channel) + { + WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed!"); + rc = GetLastError(); + goto out_close; + } + + /* Query for channel event handle */ + if (!WTSVirtualChannelQuery(priv->disp_channel, WTSVirtualEventHandle, + &buffer, &BytesReturned) + || (BytesReturned != sizeof(HANDLE))) + { + WLog_ERR(TAG, "WTSVirtualChannelQuery failed " + "or invalid returned size(%"PRIu32")", + BytesReturned); + + if (buffer) + WTSFreeMemory(buffer); + + rc = ERROR_INTERNAL_ERROR; + goto out_close; + } + + CopyMemory(&priv->channelEvent, buffer, sizeof(HANDLE)); + WTSFreeMemory(buffer); + + if (priv->thread == NULL) + { + if (!(priv->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL))) + { + WLog_ERR(TAG, "CreateEvent failed!"); + rc = ERROR_INTERNAL_ERROR; + } + + if (!(priv->thread = CreateThread(NULL, 0, disp_server_thread_func, (void*) context, 0, NULL))) + { + WLog_ERR(TAG, "CreateEvent failed!"); + CloseHandle(priv->stopEvent); + priv->stopEvent = NULL; + rc = ERROR_INTERNAL_ERROR; + } + } + + return CHANNEL_RC_OK; +out_close: + WTSVirtualChannelClose(priv->disp_channel); + priv->disp_channel = NULL; + priv->channelEvent = NULL; + return rc; +} + +static UINT disp_server_packet_send(DispServerContext* context, wStream* s) +{ + UINT ret; + ULONG written; + + if (!WTSVirtualChannelWrite(context->priv->disp_channel, + (PCHAR) Stream_Buffer(s), + Stream_GetPosition(s), &written)) + { + WLog_ERR(TAG, "WTSVirtualChannelWrite failed!"); + ret = ERROR_INTERNAL_ERROR; + goto out; + } + + if (written < Stream_GetPosition(s)) + { + WLog_WARN(TAG, "Unexpected bytes written: %"PRIu32"/%"PRIuz"", + written, Stream_GetPosition(s)); + } + + ret = CHANNEL_RC_OK; +out: + Stream_Free(s, TRUE); + return ret; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT disp_server_send_caps_pdu(DispServerContext* context) +{ + wStream* s = disp_server_single_packet_new(DISPLAY_CONTROL_PDU_TYPE_CAPS, 12); + + if (!s) + { + WLog_ERR(TAG, "disp_server_single_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write_UINT32(s, context->MaxNumMonitors); /* MaxNumMonitors (4 bytes) */ + Stream_Write_UINT32(s, context->MaxMonitorAreaFactorA); /* MaxMonitorAreaFactorA (4 bytes) */ + Stream_Write_UINT32(s, context->MaxMonitorAreaFactorB); /* MaxMonitorAreaFactorB (4 bytes) */ + return disp_server_packet_send(context, s); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT disp_server_close(DispServerContext* context) +{ + UINT error = CHANNEL_RC_OK; + DispServerPrivate* priv = context->priv; + + if (priv->thread) + { + SetEvent(priv->stopEvent); + + if (WaitForSingleObject(priv->thread, INFINITE) == WAIT_FAILED) + { + error = GetLastError(); + WLog_ERR(TAG, "WaitForSingleObject failed with error %"PRIu32"", error); + return error; + } + + CloseHandle(priv->thread); + CloseHandle(priv->stopEvent); + priv->thread = NULL; + priv->stopEvent = NULL; + } + + return error; +} + +DispServerContext* disp_server_context_new(HANDLE vcm) +{ + DispServerContext* context; + DispServerPrivate* priv; + context = (DispServerContext*) calloc(1, sizeof(DispServerContext)); + + if (!context) + { + WLog_ERR(TAG, "disp_server_context_new(): calloc DispServerContext failed!"); + goto out_free; + } + + priv = context->priv = (DispServerPrivate*) calloc(1, sizeof(DispServerPrivate)); + + if (!context->priv) + { + WLog_ERR(TAG, "disp_server_context_new(): calloc DispServerPrivate failed!"); + goto out_free; + } + + priv->input_stream = Stream_New(NULL, 4); + + if (!priv->input_stream) + { + WLog_ERR(TAG, "Stream_New failed!"); + goto out_free_priv; + } + + context->vcm = vcm; + context->Open = disp_server_open; + context->Close = disp_server_close; + context->DisplayControlCaps = disp_server_send_caps_pdu; + priv->isReady = FALSE; + return context; +out_free_priv: + free(context->priv); +out_free: + free(context); + return NULL; +} + +void disp_server_context_free(DispServerContext* context) +{ + if (!context) + return; + + disp_server_close(context); + + if (context->priv) + { + Stream_Free(context->priv->input_stream, TRUE); + free(context->priv); + } + + free(context); +} diff --git a/channels/disp/server/disp_main.h b/channels/disp/server/disp_main.h new file mode 100644 index 000000000..c47462bed --- /dev/null +++ b/channels/disp/server/disp_main.h @@ -0,0 +1,37 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * RDPEDISP Virtual Channel Extension + * + * Copyright 2019 Kobi Mizrachi + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_CHANNEL_DISP_SERVER_MAIN_H +#define FREERDP_CHANNEL_DISP_SERVER_MAIN_H + +#include + +struct _disp_server_private +{ + BOOL isReady; + wStream* input_stream; + HANDLE channelEvent; + HANDLE thread; + HANDLE stopEvent; + DWORD SessionId; + + void* disp_channel; +}; + +#endif /* FREERDP_CHANNEL_DISP_SERVER_MAIN_H */ diff --git a/channels/server/channels.c b/channels/server/channels.c index 8efff3fad..95a6ab5f2 100644 --- a/channels/server/channels.c +++ b/channels/server/channels.c @@ -50,6 +50,7 @@ #include #include #include +#include void freerdp_channels_dummy() { @@ -63,6 +64,7 @@ void freerdp_channels_dummy() RemdeskServerContext* remdesk; EncomspServerContext* encomsp; RdpgfxServerContext* rdpgfx; + DispServerContext* disp; audin = audin_server_context_new(NULL); audin_server_context_free(audin); rdpsnd = rdpsnd_server_context_new(NULL); @@ -83,6 +85,8 @@ void freerdp_channels_dummy() encomsp_server_context_free(encomsp); rdpgfx = rdpgfx_server_context_new(NULL); rdpgfx_server_context_free(rdpgfx); + disp = disp_server_context_new(NULL); + disp_server_context_free(disp); } /** diff --git a/include/freerdp/channels/disp.h b/include/freerdp/channels/disp.h new file mode 100644 index 000000000..7e6eefe9a --- /dev/null +++ b/include/freerdp/channels/disp.h @@ -0,0 +1,82 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * RDPEDISP Virtual Channel Extension + * + * Copyright 2019 Kobi Mizrachi + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_CHANNEL_DISP_H +#define FREERDP_CHANNEL_DISP_H + +#include +#include + +#define DISPLAY_CONTROL_PDU_TYPE_CAPS 0x00000005 +#define DISPLAY_CONTROL_PDU_TYPE_MONITOR_LAYOUT 0x00000002 +#define DISPLAY_CONTROL_MONITOR_LAYOUT_SIZE 40 + +#define DISP_DVC_CHANNEL_NAME "Microsoft::Windows::RDS::DisplayControl" +#define ORIENTATION_LANDSCAPE 0 +#define ORIENTATION_PORTRAIT 90 +#define ORIENTATION_LANDSCAPE_FLIPPED 180 +#define ORIENTATION_PORTRAIT_FLIPPED 270 + +#define DISPLAY_CONTROL_MONITOR_PRIMARY 0x00000001 +#define DISPLAY_CONTROL_HEADER_LENGTH 0x00000008 + +#define DISPLAY_CONTROL_MIN_MONITOR_WIDTH 200 +#define DISPLAY_CONTROL_MAX_MONITOR_WIDTH 8192 + +#define DISPLAY_CONTROL_MIN_MONITOR_HEIGHT 200 +#define DISPLAY_CONTROL_MAX_MONITOR_HEIGHT 8192 + +#define DISPLAY_CONTROL_MIN_PHYSICAL_MONITOR_WIDTH 10 +#define DISPLAY_CONTROL_MAX_PHYSICAL_MONITOR_WIDTH 10000 + +#define DISPLAY_CONTROL_MIN_PHYSICAL_MONITOR_HEIGHT 10 +#define DISPLAY_CONTROL_MAX_PHYSICAL_MONITOR_HEIGHT 10000 + +struct _DISPLAY_CONTROL_HEADER +{ + UINT32 type; + UINT32 length; +}; +typedef struct _DISPLAY_CONTROL_HEADER DISPLAY_CONTROL_HEADER; + +struct _DISPLAY_CONTROL_MONITOR_LAYOUT +{ + UINT32 Flags; + INT32 Left; + INT32 Top; + UINT32 Width; + UINT32 Height; + UINT32 PhysicalWidth; + UINT32 PhysicalHeight; + UINT32 Orientation; + UINT32 DesktopScaleFactor; + UINT32 DeviceScaleFactor; +}; +typedef struct _DISPLAY_CONTROL_MONITOR_LAYOUT DISPLAY_CONTROL_MONITOR_LAYOUT; + +struct _DISPLAY_CONTROL_MONITOR_LAYOUT_PDU +{ + UINT32 MonitorLayoutSize; + UINT32 NumMonitors; + DISPLAY_CONTROL_MONITOR_LAYOUT* Monitors; +}; + +typedef struct _DISPLAY_CONTROL_MONITOR_LAYOUT_PDU DISPLAY_CONTROL_MONITOR_LAYOUT_PDU; + +#endif /* FREERDP_CHANNEL_DISP_H */ diff --git a/include/freerdp/client/disp.h b/include/freerdp/client/disp.h index 0b39d3365..18423d715 100644 --- a/include/freerdp/client/disp.h +++ b/include/freerdp/client/disp.h @@ -22,39 +22,15 @@ #ifndef FREERDP_CHANNEL_DISP_CLIENT_DISP_H #define FREERDP_CHANNEL_DISP_CLIENT_DISP_H -#define ORIENTATION_LANDSCAPE 0 -#define ORIENTATION_PORTRAIT 90 -#define ORIENTATION_LANDSCAPE_FLIPPED 180 -#define ORIENTATION_PORTRAIT_FLIPPED 270 - -#define DISPLAY_CONTROL_MONITOR_PRIMARY 0x00000001 - -struct _DISPLAY_CONTROL_MONITOR_LAYOUT -{ - UINT32 Flags; - INT32 Left; - INT32 Top; - UINT32 Width; - UINT32 Height; - UINT32 PhysicalWidth; - UINT32 PhysicalHeight; - UINT32 Orientation; - UINT32 DesktopScaleFactor; - UINT32 DeviceScaleFactor; -}; -typedef struct _DISPLAY_CONTROL_MONITOR_LAYOUT DISPLAY_CONTROL_MONITOR_LAYOUT; - -/** - * Client Interface - */ - -#define DISP_DVC_CHANNEL_NAME "Microsoft::Windows::RDS::DisplayControl" +#include typedef struct _disp_client_context DispClientContext; -typedef UINT (*pcDispCaps)(DispClientContext* context, UINT32 MaxNumMonitors, UINT32 MaxMonitorAreaFactorA, - UINT32 MaxMonitorAreaFactorB); -typedef UINT (*pcDispSendMonitorLayout)(DispClientContext* context, UINT32 NumMonitors, DISPLAY_CONTROL_MONITOR_LAYOUT* Monitors); +typedef UINT(*pcDispCaps)(DispClientContext* context, UINT32 MaxNumMonitors, + UINT32 MaxMonitorAreaFactorA, + UINT32 MaxMonitorAreaFactorB); +typedef UINT(*pcDispSendMonitorLayout)(DispClientContext* context, UINT32 NumMonitors, + DISPLAY_CONTROL_MONITOR_LAYOUT* Monitors); struct _disp_client_context { @@ -66,4 +42,3 @@ struct _disp_client_context }; #endif /* FREERDP_CHANNEL_DISP_CLIENT_DISP_H */ - diff --git a/include/freerdp/server/disp.h b/include/freerdp/server/disp.h new file mode 100644 index 000000000..65503b3bb --- /dev/null +++ b/include/freerdp/server/disp.h @@ -0,0 +1,69 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * RDPEDISP Virtual Channel Extension + * + * Copyright 2019 Kobi Mizrachi + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_CHANNEL_DISP_SERVER_DISP_H +#define FREERDP_CHANNEL_DISP_SERVER_DISP_H + +#include + +#include +#include +#include + +typedef struct _disp_server_private DispServerPrivate; +typedef struct _disp_server_context DispServerContext; + +typedef UINT(*psDispMonitorLayout)(DispServerContext* context, + const DISPLAY_CONTROL_MONITOR_LAYOUT_PDU* pdu); +typedef UINT(*psDispCaps)(DispServerContext* context); +typedef UINT(*psDispOpen)(DispServerContext* context); +typedef UINT(*psDispClose)(DispServerContext* context); + +struct _disp_server_context +{ + void* custom; + HANDLE vcm; + + /* Server capabilities */ + UINT32 MaxNumMonitors; + UINT32 MaxMonitorAreaFactorA; + UINT32 MaxMonitorAreaFactorB; + + psDispOpen Open; + psDispClose Close; + + psDispMonitorLayout DispMonitorLayout; + psDispCaps DisplayControlCaps; + + DispServerPrivate* priv; + rdpContext* rdpcontext; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +FREERDP_API DispServerContext* disp_server_context_new(HANDLE vcm); +FREERDP_API void disp_server_context_free(DispServerContext* context); + +#ifdef __cplusplus +} +#endif + +#endif /* FREERDP_CHANNEL_DISP_SERVER_DISP_H */ diff --git a/server/proxy/CMakeLists.txt b/server/proxy/CMakeLists.txt index a944f005e..4133a3aa9 100644 --- a/server/proxy/CMakeLists.txt +++ b/server/proxy/CMakeLists.txt @@ -33,6 +33,8 @@ set(${MODULE_PREFIX}_SRCS pf_update.h pf_rdpgfx.c pf_rdpgfx.h + pf_disp.c + pf_disp.h pf_server.c pf_server.h pf_common.c diff --git a/server/proxy/pf_channels.c b/server/proxy/pf_channels.c index 36e99caad..f728d71d3 100644 --- a/server/proxy/pf_channels.c +++ b/server/proxy/pf_channels.c @@ -30,12 +30,14 @@ #include #include #include +#include #include "pf_channels.h" #include "pf_client.h" #include "pf_context.h" #include "pf_rdpgfx.h" #include "pf_log.h" +#include "pf_disp.h" #define TAG PROXY_TAG("channels") @@ -59,6 +61,27 @@ void pf_OnChannelConnectedEventHandler(void* context, server = ps->gfx; pf_rdpgfx_pipeline_init(gfx, server, pc->pdata); } + else if (strcmp(e->name, DISP_DVC_CHANNEL_NAME) == 0) + { + UINT error; + pc->disp = (DispClientContext*) e->pInterface; + ps->dispOpened = FALSE; + + if ((error = ps->disp->Open(ps->disp)) != CHANNEL_RC_OK) + { + if (error == ERROR_NOT_FOUND) + { + /* disp is not opened by client, ignore */ + return; + } + + WLog_WARN(TAG, "Failed to open disp channel"); + return; + } + + ps->dispOpened = TRUE; + pf_disp_register_callbacks(pc->disp, ps->disp, pc->pdata); + } } void pf_OnChannelDisconnectedEventHandler(void* context, @@ -74,7 +97,10 @@ void pf_OnChannelDisconnectedEventHandler(void* context, } else if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0) { - gdi_graphics_pipeline_uninit(((rdpContext*)pc)->gdi, - (RdpgfxClientContext*) e->pInterface); + gdi_graphics_pipeline_uninit(((rdpContext*)context)->gdi, (RdpgfxClientContext*) e->pInterface); + } + else if (strcmp(e->name, DISP_DVC_CHANNEL_NAME) == 0) + { + pc->disp = NULL; } } diff --git a/server/proxy/pf_common.c b/server/proxy/pf_common.c index a5b0c7f9c..b9dcf4cfb 100644 --- a/server/proxy/pf_common.c +++ b/server/proxy/pf_common.c @@ -105,4 +105,8 @@ void pf_common_copy_settings(rdpSettings* dst, rdpSettings* src) dst->GfxAVC444 = src->GfxAVC444; dst->GfxSendQoeAck = src->GfxSendQoeAck; dst->GfxAVC444v2 = src->GfxAVC444v2; -} \ No newline at end of file + dst->SupportDisplayControl = src->SupportDisplayControl; + dst->SupportMonitorLayoutPdu = src->SupportMonitorLayoutPdu; + dst->DynamicResolutionUpdate = src->DynamicResolutionUpdate; + dst->DesktopResize = src->DesktopResize; +} diff --git a/server/proxy/pf_context.h b/server/proxy/pf_context.h index b63da7d8b..d4c0ea54a 100644 --- a/server/proxy/pf_context.h +++ b/server/proxy/pf_context.h @@ -27,6 +27,9 @@ #include #include #include +#include +#include + #include "pf_config.h" #include "pf_server.h" #include "pf_filters.h" @@ -47,6 +50,9 @@ struct p_server_context HANDLE dynvcReady; RdpgfxServerContext* gfx; + DispServerContext* disp; + + BOOL dispOpened; }; typedef struct p_server_context pServerContext; @@ -61,6 +67,7 @@ struct p_client_context RdpeiClientContext* rdpei; RdpgfxClientContext* gfx; + DispClientContext* disp; }; typedef struct p_client_context pClientContext; diff --git a/server/proxy/pf_disp.c b/server/proxy/pf_disp.c new file mode 100644 index 000000000..f567772e0 --- /dev/null +++ b/server/proxy/pf_disp.c @@ -0,0 +1,55 @@ +#include + +#include "pf_disp.h" +#include "pf_log.h" + +#define TAG PROXY_TAG("disp") + +BOOL pf_server_disp_init(pServerContext* ps) +{ + DispServerContext* disp; + disp = ps->disp = disp_server_context_new(ps->vcm); + + if (!disp) + { + return FALSE; + } + + disp->rdpcontext = (rdpContext*)ps; + return TRUE; +} + +static UINT pf_disp_monitor_layout(DispServerContext* context, + const DISPLAY_CONTROL_MONITOR_LAYOUT_PDU* pdu) +{ + proxyData* pdata = (proxyData*) context->custom; + DispClientContext* client = (DispClientContext*) pdata->pc->disp; + WLog_INFO(TAG, __FUNCTION__); + return client->SendMonitorLayout(client, pdu->NumMonitors, pdu->Monitors); +} + +static UINT pf_disp_on_caps_control(DispClientContext* context, UINT32 MaxNumMonitors, + UINT32 MaxMonitorAreaFactorA, + UINT32 MaxMonitorAreaFactorB) +{ + proxyData* pdata = (proxyData*) context->custom; + DispServerContext* server = (DispServerContext*) pdata->ps->disp; + WLog_INFO(TAG, __FUNCTION__); + /* Update caps of proxy's disp server */ + server->MaxMonitorAreaFactorA = MaxMonitorAreaFactorA; + server->MaxMonitorAreaFactorB = MaxMonitorAreaFactorB; + server->MaxNumMonitors = MaxNumMonitors; + /* Send CapsControl to client */ + return server->DisplayControlCaps(server); +} + +void pf_disp_register_callbacks(DispClientContext* client, DispServerContext* server, + proxyData* pdata) +{ + client->custom = (void*) pdata; + server->custom = (void*) pdata; + /* client receives from server, forward using disp server to original client */ + client->DisplayControlCaps = pf_disp_on_caps_control; + /* server receives from client, forward to target server using disp client */ + server->DispMonitorLayout = pf_disp_monitor_layout; +} diff --git a/server/proxy/pf_disp.h b/server/proxy/pf_disp.h new file mode 100644 index 000000000..5aad354eb --- /dev/null +++ b/server/proxy/pf_disp.h @@ -0,0 +1,34 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * FreeRDP Proxy Server + * + * Copyright 2019 Mati Shabtay + * Copyright 2019 Kobi Mizrachi + * Copyright 2019 Idan Freiberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_SERVER_PROXY_RDPEDISP_H +#define FREERDP_SERVER_PROXY_RDPEDISP_H + +#include +#include + +#include "pf_context.h" + +BOOL pf_server_disp_init(pServerContext* ps); +void pf_disp_register_callbacks(DispClientContext* client, DispServerContext* server, + proxyData* pdata); + +#endif /*FREERDP_SERVER_PROXY_RDPEDISP_H*/ diff --git a/server/proxy/pf_server.c b/server/proxy/pf_server.c index 9be46a977..d85e6f123 100644 --- a/server/proxy/pf_server.c +++ b/server/proxy/pf_server.c @@ -49,6 +49,7 @@ #include "pf_input.h" #include "pf_update.h" #include "pf_rdpgfx.h" +#include "pf_disp.h" #define TAG PROXY_TAG("server") @@ -182,6 +183,7 @@ static BOOL pf_server_post_connect(freerdp_peer* client) } pf_server_rdpgfx_init(ps); + pf_server_disp_init(ps); /* Start a proxy's client in it's own thread */ if (!(ps->thread = CreateThread(NULL, 0, pf_client_start, pc, 0, NULL))) @@ -256,6 +258,9 @@ static DWORD WINAPI pf_server_handle_client(LPVOID arg) goto out_free_peer; } + client->settings->SupportDisplayControl = TRUE; + client->settings->SupportMonitorLayoutPdu = TRUE; + client->settings->DynamicResolutionUpdate = TRUE; client->settings->RdpSecurity = config->RdpSecurity; client->settings->TlsSecurity = config->TlsSecurity; client->settings->NlaSecurity = config->NlaSecurity; @@ -263,8 +268,7 @@ static DWORD WINAPI pf_server_handle_client(LPVOID arg) client->settings->ColorDepth = 32; client->settings->SuppressOutput = TRUE; client->settings->RefreshRect = TRUE; - client->settings->UseMultimon = TRUE; - client->settings->SupportMonitorLayoutPdu = TRUE; + client->settings->DesktopResize = TRUE; client->PostConnect = pf_server_post_connect; client->Activate = pf_server_activate; client->AdjustMonitorsLayout = pf_server_adjust_monitor_layout; @@ -345,6 +349,17 @@ static DWORD WINAPI pf_server_handle_client(LPVOID arg) fail: + if (ps->disp) + { + if (ps->dispOpened) + { + WLog_INFO(TAG, "Closing disp server"); + (void)ps->disp->Close(ps->disp); + } + + disp_server_context_free(ps->disp); + } + if (client->connected && !pf_common_connection_aborted_by_peer(pdata)) { pf_server_handle_client_disconnection(client); diff --git a/server/proxy/pf_update.c b/server/proxy/pf_update.c index 6effbfecc..73fee2160 100644 --- a/server/proxy/pf_update.c +++ b/server/proxy/pf_update.c @@ -19,7 +19,7 @@ * limitations under the License. */ -#include +#include #include "pf_update.h" #include "pf_context.h"