RDPEDISP server side implementation (#5414)

* libfreerdp: Add RDPEDISP server implementation

* server/proxy: Add RDPEDISP support
This commit is contained in:
Kobi 2019-06-11 07:13:42 -07:00 committed by akallabeth
parent 4a6b843f16
commit d9366df448
21 changed files with 1103 additions and 92 deletions

View File

@ -20,3 +20,7 @@ define_channel("disp")
if(WITH_CLIENT_CHANNELS) if(WITH_CLIENT_CHANNELS)
add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME}) add_channel_client(${MODULE_PREFIX} ${CHANNEL_NAME})
endif() endif()
if(WITH_SERVER_CHANNELS)
add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME})
endif()

View File

@ -19,7 +19,9 @@ define_channel_client("disp")
set(${MODULE_PREFIX}_SRCS set(${MODULE_PREFIX}_SRCS
disp_main.c disp_main.c
disp_main.h) disp_main.h
../disp_common.c
../disp_common.h)
include_directories(..) include_directories(..)

101
channels/disp/client/disp_main.c Executable file → Normal file
View File

@ -40,6 +40,7 @@
#include <freerdp/addin.h> #include <freerdp/addin.h>
#include "disp_main.h" #include "disp_main.h"
#include "../disp_common.h"
struct _DISP_CHANNEL_CALLBACK struct _DISP_CHANNEL_CALLBACK
{ {
@ -79,40 +80,39 @@ typedef struct _DISP_PLUGIN DISP_PLUGIN;
* *
* @return 0 on success, otherwise a Win32 error code * @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; UINT status;
wStream* s; wStream* s;
UINT32 type;
UINT32 index; UINT32 index;
UINT32 length;
DISP_PLUGIN* disp; DISP_PLUGIN* disp;
UINT32 MonitorLayoutSize; UINT32 MonitorLayoutSize;
DISPLAY_CONTROL_HEADER header;
disp = (DISP_PLUGIN*) callback->plugin; 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); if (!s)
type = DISPLAY_CONTROL_PDU_TYPE_MONITOR_LAYOUT;
s = Stream_New(NULL, length);
if(!s)
{ {
WLog_ERR(TAG, "Stream_New failed!"); WLog_ERR(TAG, "Stream_New failed!");
return CHANNEL_RC_NO_MEMORY; return CHANNEL_RC_NO_MEMORY;
} }
Stream_Write_UINT32(s, type); /* Type (4 bytes) */ if ((status = disp_write_header(s, &header)))
Stream_Write_UINT32(s, length); /* Length (4 bytes) */ {
WLog_ERR(TAG, "Failed to write header with error %"PRIu32"!", status);
goto out;
}
if (NumMonitors > disp->MaxNumMonitors) if (NumMonitors > disp->MaxNumMonitors)
NumMonitors = disp->MaxNumMonitors; NumMonitors = disp->MaxNumMonitors;
Stream_Write_UINT32(s, MonitorLayoutSize); /* MonitorLayoutSize (4 bytes) */ Stream_Write_UINT32(s, MonitorLayoutSize); /* MonitorLayoutSize (4 bytes) */
Stream_Write_UINT32(s, NumMonitors); /* NumMonitors (4 bytes) */ Stream_Write_UINT32(s, NumMonitors); /* NumMonitors (4 bytes) */
WLog_DBG(TAG, "disp_send_display_control_monitor_layout_pdu: NumMonitors=%"PRIu32"", NumMonitors); WLog_DBG(TAG, "disp_send_display_control_monitor_layout_pdu: NumMonitors=%"PRIu32"", NumMonitors);
for (index = 0; index < NumMonitors; index++) 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].Orientation); /* Orientation (4 bytes) */
Stream_Write_UINT32(s, Monitors[index].DesktopScaleFactor); /* DesktopScaleFactor (4 bytes) */ Stream_Write_UINT32(s, Monitors[index].DesktopScaleFactor); /* DesktopScaleFactor (4 bytes) */
Stream_Write_UINT32(s, Monitors[index].DeviceScaleFactor); /* DeviceScaleFactor (4 bytes) */ Stream_Write_UINT32(s, Monitors[index].DeviceScaleFactor); /* DeviceScaleFactor (4 bytes) */
WLog_DBG(TAG,
WLog_DBG(TAG, "\t%d : Flags: 0x%08"PRIX32" Left/Top: (%"PRId32",%"PRId32") W/H=%"PRIu32"x%"PRIu32")", index, "\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].Flags, Monitors[index].Left, Monitors[index].Top, Monitors[index].Width,
Monitors[index].Height); Monitors[index].Height);
WLog_DBG(TAG, "\t PhysicalWidth: %"PRIu32" PhysicalHeight: %"PRIu32" Orientation: %"PRIu32"", 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); Stream_SealLength(s);
status = callback->channel->Write(callback->channel, (UINT32) Stream_Length(s), Stream_Buffer(s),
status = callback->channel->Write(callback->channel, (UINT32) Stream_Length(s), Stream_Buffer(s), NULL); NULL);
Stream_Free(s, TRUE); Stream_Free(s, TRUE);
return status; 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) UINT disp_recv_display_control_caps_pdu(DISP_CHANNEL_CALLBACK* callback, wStream* s)
{ {
DISP_PLUGIN* disp; DISP_PLUGIN* disp;
DispClientContext *context; DispClientContext* context;
UINT ret = CHANNEL_RC_OK; UINT ret = CHANNEL_RC_OK;
disp = (DISP_PLUGIN*) callback->plugin; disp = (DISP_PLUGIN*) callback->plugin;
context = (DispClientContext *)disp->iface.pInterface; context = (DispClientContext*)disp->iface.pInterface;
if (Stream_GetRemainingLength(s) < 12) 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) */ Stream_Read_UINT32(s, disp->MaxMonitorAreaFactorB); /* MaxMonitorAreaFactorB (4 bytes) */
if (context->DisplayControlCaps) 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; 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) UINT disp_recv_pdu(DISP_CHANNEL_CALLBACK* callback, wStream* s)
{ {
UINT32 type; UINT32 error;
UINT32 length; DISPLAY_CONTROL_HEADER header;
if (Stream_GetRemainingLength(s) < 8) if (Stream_GetRemainingLength(s) < 8)
{ {
@ -207,18 +206,25 @@ UINT disp_recv_pdu(DISP_CHANNEL_CALLBACK* callback, wStream* s)
return ERROR_INVALID_DATA; return ERROR_INVALID_DATA;
} }
Stream_Read_UINT32(s, type); /* Type (4 bytes) */ if ((error = disp_read_header(s, &header)))
Stream_Read_UINT32(s, length); /* Length (4 bytes) */ {
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: case DISPLAY_CONTROL_PDU_TYPE_CAPS:
return disp_recv_display_control_caps_pdu(callback, s); return disp_recv_display_control_caps_pdu(callback, s);
default: default:
WLog_ERR(TAG, "Type %"PRIu32" not recognized!", type); WLog_ERR(TAG, "Type %"PRIu32" not recognized!", header.type);
return ERROR_INTERNAL_ERROR; 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 * @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; DISP_CHANNEL_CALLBACK* callback = (DISP_CHANNEL_CALLBACK*) pChannelCallback;
return disp_recv_pdu(callback, data); 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 * @return 0 on success, otherwise a Win32 error code
*/ */
static UINT disp_on_new_channel_connection(IWTSListenerCallback* pListenerCallback, static UINT disp_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
IWTSVirtualChannel* pChannel, BYTE* Data, BOOL* pbAccept, IWTSVirtualChannel* pChannel, BYTE* Data, BOOL* pbAccept,
IWTSVirtualChannelCallback** ppCallback) IWTSVirtualChannelCallback** ppCallback)
{ {
DISP_CHANNEL_CALLBACK* callback; DISP_CHANNEL_CALLBACK* callback;
DISP_LISTENER_CALLBACK* listener_callback = (DISP_LISTENER_CALLBACK*) pListenerCallback; DISP_LISTENER_CALLBACK* listener_callback = (DISP_LISTENER_CALLBACK*) pListenerCallback;
callback = (DISP_CHANNEL_CALLBACK*) calloc(1, sizeof(DISP_CHANNEL_CALLBACK)); callback = (DISP_CHANNEL_CALLBACK*) calloc(1, sizeof(DISP_CHANNEL_CALLBACK));
if (!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_mgr = listener_callback->channel_mgr;
callback->channel = pChannel; callback->channel = pChannel;
listener_callback->channel_callback = callback; listener_callback->channel_callback = callback;
*ppCallback = (IWTSVirtualChannelCallback*) callback; *ppCallback = (IWTSVirtualChannelCallback*) callback;
return CHANNEL_RC_OK; return CHANNEL_RC_OK;
} }
@ -287,7 +289,6 @@ static UINT disp_plugin_initialize(IWTSPlugin* pPlugin, IWTSVirtualChannelManage
{ {
UINT status; UINT status;
DISP_PLUGIN* disp = (DISP_PLUGIN*) pPlugin; DISP_PLUGIN* disp = (DISP_PLUGIN*) pPlugin;
disp->listener_callback = (DISP_LISTENER_CALLBACK*) calloc(1, sizeof(DISP_LISTENER_CALLBACK)); disp->listener_callback = (DISP_LISTENER_CALLBACK*) calloc(1, sizeof(DISP_LISTENER_CALLBACK));
if (!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->iface.OnNewChannelConnection = disp_on_new_channel_connection;
disp->listener_callback->plugin = pPlugin; disp->listener_callback->plugin = pPlugin;
disp->listener_callback->channel_mgr = pChannelMgr; disp->listener_callback->channel_mgr = pChannelMgr;
status = pChannelMgr->CreateListener(pChannelMgr, DISP_DVC_CHANNEL_NAME, 0, 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; disp->listener->pInterface = disp->iface.pInterface;
return status; return status;
} }
@ -331,11 +329,11 @@ static UINT disp_plugin_terminated(IWTSPlugin* pPlugin)
* *
* @return 0 on success, otherwise a Win32 error code * @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_PLUGIN* disp = (DISP_PLUGIN*) context->handle;
DISP_CHANNEL_CALLBACK* callback = disp->listener_callback->channel_callback; DISP_CHANNEL_CALLBACK* callback = disp->listener_callback->channel_callback;
return disp_send_display_control_monitor_layout_pdu(callback, NumMonitors, Monitors); 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; UINT error = CHANNEL_RC_OK;
DISP_PLUGIN* disp; DISP_PLUGIN* disp;
DispClientContext* context; DispClientContext* context;
disp = (DISP_PLUGIN*) pEntryPoints->GetPlugin(pEntryPoints, "disp"); disp = (DISP_PLUGIN*) pEntryPoints->GetPlugin(pEntryPoints, "disp");
if (!disp) if (!disp)
{ {
disp = (DISP_PLUGIN*) calloc(1, sizeof(DISP_PLUGIN)); disp = (DISP_PLUGIN*) calloc(1, sizeof(DISP_PLUGIN));
if (!disp) if (!disp)
{ {
WLog_ERR(TAG, "calloc failed!"); WLog_ERR(TAG, "calloc failed!");
@ -373,8 +372,8 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
disp->MaxNumMonitors = 16; disp->MaxNumMonitors = 16;
disp->MaxMonitorAreaFactorA = 8192; disp->MaxMonitorAreaFactorA = 8192;
disp->MaxMonitorAreaFactorB = 8192; disp->MaxMonitorAreaFactorB = 8192;
context = (DispClientContext*) calloc(1, sizeof(DispClientContext)); context = (DispClientContext*) calloc(1, sizeof(DispClientContext));
if (!context) if (!context)
{ {
WLog_ERR(TAG, "calloc failed!"); WLog_ERR(TAG, "calloc failed!");
@ -384,9 +383,7 @@ UINT DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
context->handle = (void*) disp; context->handle = (void*) disp;
context->SendMonitorLayout = disp_send_monitor_layout; context->SendMonitorLayout = disp_send_monitor_layout;
disp->iface.pInterface = (void*) context; disp->iface.pInterface = (void*) context;
error = pEntryPoints->RegisterPlugin(pEntryPoints, "disp", (IWTSPlugin*) disp); error = pEntryPoints->RegisterPlugin(pEntryPoints, "disp", (IWTSPlugin*) disp);
} }
else else

View File

@ -33,8 +33,6 @@
#include <freerdp/client/disp.h> #include <freerdp/client/disp.h>
#define DISPLAY_CONTROL_PDU_TYPE_CAPS 0x00000005
#define DISPLAY_CONTROL_PDU_TYPE_MONITOR_LAYOUT 0x00000002
#define TAG CHANNELS_TAG("disp.client") #define TAG CHANNELS_TAG("disp.client")

View File

@ -0,0 +1,60 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* RDPEDISP Virtual Channel Extension
*
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <winpr/crt.h>
#include <winpr/stream.h>
#include <freerdp/channels/log.h>
#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;
}

View File

@ -0,0 +1,32 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* RDPEDISP Virtual Channel Extension
*
* 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.
*/
#ifndef FREERDP_CHANNEL_DISP_COMMON_H
#define FREERDP_CHANNEL_DISP_COMMON_H
#include <winpr/crt.h>
#include <winpr/stream.h>
#include <freerdp/channels/disp.h>
#include <freerdp/api.h>
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 */

View File

@ -0,0 +1,32 @@
# FreeRDP: A Remote Desktop Protocol Implementation
# FreeRDP cmake build script
#
# 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.
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")

View File

@ -0,0 +1,576 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* RDPEDISP Virtual Channel Extension
*
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "disp_main.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winpr/crt.h>
#include <winpr/synch.h>
#include <winpr/thread.h>
#include <winpr/stream.h>
#include <winpr/sysinfo.h>
#include <freerdp/channels/wtsvc.h>
#include <freerdp/channels/log.h>
#include <freerdp/server/disp.h>
#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);
}

View File

@ -0,0 +1,37 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* RDPEDISP Virtual Channel Extension
*
* 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.
*/
#ifndef FREERDP_CHANNEL_DISP_SERVER_MAIN_H
#define FREERDP_CHANNEL_DISP_SERVER_MAIN_H
#include <freerdp/server/disp.h>
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 */

View File

@ -50,6 +50,7 @@
#include <freerdp/server/remdesk.h> #include <freerdp/server/remdesk.h>
#include <freerdp/server/encomsp.h> #include <freerdp/server/encomsp.h>
#include <freerdp/server/rdpgfx.h> #include <freerdp/server/rdpgfx.h>
#include <freerdp/server/disp.h>
void freerdp_channels_dummy() void freerdp_channels_dummy()
{ {
@ -63,6 +64,7 @@ void freerdp_channels_dummy()
RemdeskServerContext* remdesk; RemdeskServerContext* remdesk;
EncomspServerContext* encomsp; EncomspServerContext* encomsp;
RdpgfxServerContext* rdpgfx; RdpgfxServerContext* rdpgfx;
DispServerContext* disp;
audin = audin_server_context_new(NULL); audin = audin_server_context_new(NULL);
audin_server_context_free(audin); audin_server_context_free(audin);
rdpsnd = rdpsnd_server_context_new(NULL); rdpsnd = rdpsnd_server_context_new(NULL);
@ -83,6 +85,8 @@ void freerdp_channels_dummy()
encomsp_server_context_free(encomsp); encomsp_server_context_free(encomsp);
rdpgfx = rdpgfx_server_context_new(NULL); rdpgfx = rdpgfx_server_context_new(NULL);
rdpgfx_server_context_free(rdpgfx); rdpgfx_server_context_free(rdpgfx);
disp = disp_server_context_new(NULL);
disp_server_context_free(disp);
} }
/** /**

View File

@ -0,0 +1,82 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* RDPEDISP Virtual Channel Extension
*
* 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.
*/
#ifndef FREERDP_CHANNEL_DISP_H
#define FREERDP_CHANNEL_DISP_H
#include <freerdp/api.h>
#include <freerdp/types.h>
#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 */

View File

@ -22,39 +22,15 @@
#ifndef FREERDP_CHANNEL_DISP_CLIENT_DISP_H #ifndef FREERDP_CHANNEL_DISP_CLIENT_DISP_H
#define FREERDP_CHANNEL_DISP_CLIENT_DISP_H #define FREERDP_CHANNEL_DISP_CLIENT_DISP_H
#define ORIENTATION_LANDSCAPE 0 #include <freerdp/channels/disp.h>
#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"
typedef struct _disp_client_context DispClientContext; typedef struct _disp_client_context DispClientContext;
typedef UINT (*pcDispCaps)(DispClientContext* context, UINT32 MaxNumMonitors, UINT32 MaxMonitorAreaFactorA, typedef UINT(*pcDispCaps)(DispClientContext* context, UINT32 MaxNumMonitors,
UINT32 MaxMonitorAreaFactorB); UINT32 MaxMonitorAreaFactorA,
typedef UINT (*pcDispSendMonitorLayout)(DispClientContext* context, UINT32 NumMonitors, DISPLAY_CONTROL_MONITOR_LAYOUT* Monitors); UINT32 MaxMonitorAreaFactorB);
typedef UINT(*pcDispSendMonitorLayout)(DispClientContext* context, UINT32 NumMonitors,
DISPLAY_CONTROL_MONITOR_LAYOUT* Monitors);
struct _disp_client_context struct _disp_client_context
{ {
@ -66,4 +42,3 @@ struct _disp_client_context
}; };
#endif /* FREERDP_CHANNEL_DISP_CLIENT_DISP_H */ #endif /* FREERDP_CHANNEL_DISP_CLIENT_DISP_H */

View File

@ -0,0 +1,69 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* RDPEDISP Virtual Channel Extension
*
* 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.
*/
#ifndef FREERDP_CHANNEL_DISP_SERVER_DISP_H
#define FREERDP_CHANNEL_DISP_SERVER_DISP_H
#include <freerdp/channels/disp.h>
#include <freerdp/freerdp.h>
#include <freerdp/api.h>
#include <freerdp/types.h>
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 */

View File

@ -33,6 +33,8 @@ set(${MODULE_PREFIX}_SRCS
pf_update.h pf_update.h
pf_rdpgfx.c pf_rdpgfx.c
pf_rdpgfx.h pf_rdpgfx.h
pf_disp.c
pf_disp.h
pf_server.c pf_server.c
pf_server.h pf_server.h
pf_common.c pf_common.c

View File

@ -30,12 +30,14 @@
#include <freerdp/client/rail.h> #include <freerdp/client/rail.h>
#include <freerdp/client/cliprdr.h> #include <freerdp/client/cliprdr.h>
#include <freerdp/client/rdpgfx.h> #include <freerdp/client/rdpgfx.h>
#include <freerdp/client/disp.h>
#include "pf_channels.h" #include "pf_channels.h"
#include "pf_client.h" #include "pf_client.h"
#include "pf_context.h" #include "pf_context.h"
#include "pf_rdpgfx.h" #include "pf_rdpgfx.h"
#include "pf_log.h" #include "pf_log.h"
#include "pf_disp.h"
#define TAG PROXY_TAG("channels") #define TAG PROXY_TAG("channels")
@ -59,6 +61,27 @@ void pf_OnChannelConnectedEventHandler(void* context,
server = ps->gfx; server = ps->gfx;
pf_rdpgfx_pipeline_init(gfx, server, pc->pdata); 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, void pf_OnChannelDisconnectedEventHandler(void* context,
@ -74,7 +97,10 @@ void pf_OnChannelDisconnectedEventHandler(void* context,
} }
else if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0) else if (strcmp(e->name, RDPGFX_DVC_CHANNEL_NAME) == 0)
{ {
gdi_graphics_pipeline_uninit(((rdpContext*)pc)->gdi, gdi_graphics_pipeline_uninit(((rdpContext*)context)->gdi, (RdpgfxClientContext*) e->pInterface);
(RdpgfxClientContext*) e->pInterface); }
else if (strcmp(e->name, DISP_DVC_CHANNEL_NAME) == 0)
{
pc->disp = NULL;
} }
} }

View File

@ -105,4 +105,8 @@ void pf_common_copy_settings(rdpSettings* dst, rdpSettings* src)
dst->GfxAVC444 = src->GfxAVC444; dst->GfxAVC444 = src->GfxAVC444;
dst->GfxSendQoeAck = src->GfxSendQoeAck; dst->GfxSendQoeAck = src->GfxSendQoeAck;
dst->GfxAVC444v2 = src->GfxAVC444v2; dst->GfxAVC444v2 = src->GfxAVC444v2;
} dst->SupportDisplayControl = src->SupportDisplayControl;
dst->SupportMonitorLayoutPdu = src->SupportMonitorLayoutPdu;
dst->DynamicResolutionUpdate = src->DynamicResolutionUpdate;
dst->DesktopResize = src->DesktopResize;
}

View File

@ -27,6 +27,9 @@
#include <freerdp/client/rdpei.h> #include <freerdp/client/rdpei.h>
#include <freerdp/client/rdpgfx.h> #include <freerdp/client/rdpgfx.h>
#include <freerdp/server/rdpgfx.h> #include <freerdp/server/rdpgfx.h>
#include <freerdp/client/disp.h>
#include <freerdp/server/disp.h>
#include "pf_config.h" #include "pf_config.h"
#include "pf_server.h" #include "pf_server.h"
#include "pf_filters.h" #include "pf_filters.h"
@ -47,6 +50,9 @@ struct p_server_context
HANDLE dynvcReady; HANDLE dynvcReady;
RdpgfxServerContext* gfx; RdpgfxServerContext* gfx;
DispServerContext* disp;
BOOL dispOpened;
}; };
typedef struct p_server_context pServerContext; typedef struct p_server_context pServerContext;
@ -61,6 +67,7 @@ struct p_client_context
RdpeiClientContext* rdpei; RdpeiClientContext* rdpei;
RdpgfxClientContext* gfx; RdpgfxClientContext* gfx;
DispClientContext* disp;
}; };
typedef struct p_client_context pClientContext; typedef struct p_client_context pClientContext;

55
server/proxy/pf_disp.c Normal file
View File

@ -0,0 +1,55 @@
#include <freerdp/server/disp.h>
#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;
}

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

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

View File

@ -49,6 +49,7 @@
#include "pf_input.h" #include "pf_input.h"
#include "pf_update.h" #include "pf_update.h"
#include "pf_rdpgfx.h" #include "pf_rdpgfx.h"
#include "pf_disp.h"
#define TAG PROXY_TAG("server") #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_rdpgfx_init(ps);
pf_server_disp_init(ps);
/* Start a proxy's client in it's own thread */ /* Start a proxy's client in it's own thread */
if (!(ps->thread = CreateThread(NULL, 0, pf_client_start, pc, 0, NULL))) 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; goto out_free_peer;
} }
client->settings->SupportDisplayControl = TRUE;
client->settings->SupportMonitorLayoutPdu = TRUE;
client->settings->DynamicResolutionUpdate = TRUE;
client->settings->RdpSecurity = config->RdpSecurity; client->settings->RdpSecurity = config->RdpSecurity;
client->settings->TlsSecurity = config->TlsSecurity; client->settings->TlsSecurity = config->TlsSecurity;
client->settings->NlaSecurity = config->NlaSecurity; client->settings->NlaSecurity = config->NlaSecurity;
@ -263,8 +268,7 @@ static DWORD WINAPI pf_server_handle_client(LPVOID arg)
client->settings->ColorDepth = 32; client->settings->ColorDepth = 32;
client->settings->SuppressOutput = TRUE; client->settings->SuppressOutput = TRUE;
client->settings->RefreshRect = TRUE; client->settings->RefreshRect = TRUE;
client->settings->UseMultimon = TRUE; client->settings->DesktopResize = TRUE;
client->settings->SupportMonitorLayoutPdu = TRUE;
client->PostConnect = pf_server_post_connect; client->PostConnect = pf_server_post_connect;
client->Activate = pf_server_activate; client->Activate = pf_server_activate;
client->AdjustMonitorsLayout = pf_server_adjust_monitor_layout; client->AdjustMonitorsLayout = pf_server_adjust_monitor_layout;
@ -345,6 +349,17 @@ static DWORD WINAPI pf_server_handle_client(LPVOID arg)
fail: 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)) if (client->connected && !pf_common_connection_aborted_by_peer(pdata))
{ {
pf_server_handle_client_disconnection(client); pf_server_handle_client_disconnection(client);

View File

@ -19,7 +19,7 @@
* limitations under the License. * limitations under the License.
*/ */
#include <freerdp/update.h> #include <freerdp/display.h>
#include "pf_update.h" #include "pf_update.h"
#include "pf_context.h" #include "pf_context.h"