diff --git a/channels/gfxredir/CMakeLists.txt b/channels/gfxredir/CMakeLists.txt new file mode 100644 index 000000000..bfea7571c --- /dev/null +++ b/channels/gfxredir/CMakeLists.txt @@ -0,0 +1,23 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP cmake build script +# +# Copyright 2020 Microsoft +# +# 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. + +if(WITH_CHANNEL_GFXREDIR) + define_channel("gfxredir") + if(WITH_SERVER_CHANNELS) + add_channel_server(${MODULE_PREFIX} ${CHANNEL_NAME}) + endif() +endif() diff --git a/channels/gfxredir/ChannelOptions.cmake b/channels/gfxredir/ChannelOptions.cmake new file mode 100644 index 000000000..62cdceba6 --- /dev/null +++ b/channels/gfxredir/ChannelOptions.cmake @@ -0,0 +1,12 @@ +if(WITH_CHANNEL_GFXREDIR) + set(OPTION_DEFAULT OFF) + set(OPTION_CLIENT_DEFAULT OFF) + set(OPTION_SERVER_DEFAULT ON) + + define_channel_options(NAME "gfxredir" TYPE "dynamic" + DESCRIPTION "Graphics Redirection Virtual Channel Extension" + SPECIFICATIONS "[MS-RDPXXXX]" + DEFAULT ${OPTION_DEFAULT}) + + define_channel_server_options(${OPTION_SERVER_DEFAULT}) +endif() diff --git a/channels/gfxredir/gfxredir_common.c b/channels/gfxredir/gfxredir_common.c new file mode 100644 index 000000000..b8a298f14 --- /dev/null +++ b/channels/gfxredir/gfxredir_common.c @@ -0,0 +1,60 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * RDPXXXX Remote App Graphics Redirection Virtual Channel Extension + * + * Copyright 2020 Microsoft + * + * 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("gfxredir.common") + +#include "gfxredir_common.h" + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +UINT gfxredir_read_header(wStream* s, GFXREDIR_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->cmdId); + Stream_Read_UINT32(s, header->length); + return CHANNEL_RC_OK; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +UINT gfxredir_write_header(wStream* s, const GFXREDIR_HEADER* header) +{ + Stream_Write_UINT32(s, header->cmdId); + Stream_Write_UINT32(s, header->length); + return CHANNEL_RC_OK; +} diff --git a/channels/gfxredir/gfxredir_common.h b/channels/gfxredir/gfxredir_common.h new file mode 100644 index 000000000..18710a9ab --- /dev/null +++ b/channels/gfxredir/gfxredir_common.h @@ -0,0 +1,32 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * RDPXXXX Remote App Graphics Redirection Virtual Channel Extension + * + * Copyright 2020 Microsoft + * + * 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_GFXREDIR_COMMON_H +#define FREERDP_CHANNEL_GFXREDIR_COMMON_H + +#include +#include + +#include +#include + +FREERDP_LOCAL UINT gfxredir_read_header(wStream* s, GFXREDIR_HEADER* header); +FREERDP_LOCAL UINT gfxredir_write_header(wStream* s, const GFXREDIR_HEADER* header); + +#endif /* FREERDP_CHANNEL_GFXREDIR_COMMON_H */ diff --git a/channels/gfxredir/server/CMakeLists.txt b/channels/gfxredir/server/CMakeLists.txt new file mode 100644 index 000000000..41cf225e6 --- /dev/null +++ b/channels/gfxredir/server/CMakeLists.txt @@ -0,0 +1,32 @@ +# FreeRDP: A Remote Desktop Protocol Implementation +# FreeRDP cmake build script +# +# Copyright 2020 Microsoft +# +# 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("gfxredir") + +set(${MODULE_PREFIX}_SRCS + gfxredir_main.c + gfxredir_main.h + ../gfxredir_common.c + ../gfxredir_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/gfxredir/server/gfxredir_main.c b/channels/gfxredir/server/gfxredir_main.c new file mode 100644 index 000000000..d73d6924d --- /dev/null +++ b/channels/gfxredir/server/gfxredir_main.c @@ -0,0 +1,805 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * RDPXXXX Remote App Graphics Redirection Virtual Channel Extension + * + * Copyright 2020 Microsoft + * + * 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 "gfxredir_main.h" +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "../gfxredir_common.h" + +#define TAG CHANNELS_TAG("gfxredir.server") + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gfxredir_recv_legacy_caps_pdu(wStream* s, GfxRedirServerContext* context) +{ + UINT32 error = CHANNEL_RC_OK; + GFXREDIR_LEGACY_CAPS_PDU pdu; + + if (Stream_GetRemainingLength(s) < 2) + { + WLog_ERR(TAG, "not enough data!"); + return ERROR_INVALID_DATA; + } + + Stream_Read_UINT16(s, pdu.version); /* version (2 bytes) */ + + if (context) + IFCALLRET(context->GraphicsRedirectionLegacyCaps, error, context, &pdu); + + return error; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gfxredir_recv_caps_advertise_pdu(wStream* s, UINT32 length, + GfxRedirServerContext* context) +{ + UINT32 error = CHANNEL_RC_OK; + GFXREDIR_CAPS_ADVERTISE_PDU pdu; + + if (Stream_GetRemainingLength(s) < length) + { + WLog_ERR(TAG, "not enough data!"); + return ERROR_INVALID_DATA; + } + + pdu.length = length; + Stream_GetPointer(s, pdu.caps); + Stream_Seek(s, length); + + if (!context->GraphicsRedirectionCapsAdvertise) + { + WLog_ERR(TAG, "server does not support CapsAdvertise PDU!"); + return ERROR_NOT_SUPPORTED; + } + else if (context) + { + IFCALLRET(context->GraphicsRedirectionCapsAdvertise, error, context, &pdu); + } + + return error; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gfxredir_recv_present_buffer_ack_pdu(wStream* s, GfxRedirServerContext* context) +{ + UINT32 error = CHANNEL_RC_OK; + GFXREDIR_PRESENT_BUFFER_ACK_PDU pdu; + + if (Stream_GetRemainingLength(s) < 16) + { + WLog_ERR(TAG, "not enough data!"); + return ERROR_INVALID_DATA; + } + + Stream_Read_UINT64(s, pdu.windowId); /* windowId (8 bytes) */ + Stream_Read_UINT64(s, pdu.presentId); /* presentId (8 bytes) */ + + if (context) + { + IFCALLRET(context->PresentBufferAck, error, context, &pdu); + } + + return error; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gfxredir_server_receive_pdu(GfxRedirServerContext* context, wStream* s) +{ + UINT error = CHANNEL_RC_OK; + size_t beg, end; + GFXREDIR_HEADER header; + beg = Stream_GetPosition(s); + + if ((error = gfxredir_read_header(s, &header))) + { + WLog_ERR(TAG, "gfxredir_read_header failed with error %" PRIu32 "!", error); + return error; + } + + switch (header.cmdId) + { + case GFXREDIR_CMDID_LEGACY_CAPS: + if ((error = gfxredir_recv_legacy_caps_pdu(s, context))) + WLog_ERR(TAG, + "gfxredir_recv_legacy_caps_pdu " + "failed with error %" PRIu32 "!", + error); + + break; + + case GFXREDIR_CMDID_CAPS_ADVERTISE: + if ((error = gfxredir_recv_caps_advertise_pdu(s, header.length - 8, context))) + WLog_ERR(TAG, + "gfxredir_recv_caps_advertise " + "failed with error %" PRIu32 "!", + error); + break; + + case GFXREDIR_CMDID_PRESENT_BUFFER_ACK: + if ((error = gfxredir_recv_present_buffer_ack_pdu(s, context))) + WLog_ERR(TAG, + "gfxredir_recv_present_buffer_ask_pdu " + "failed with error %" PRIu32 "!", + error); + break; + + default: + error = CHANNEL_RC_BAD_PROC; + WLog_WARN(TAG, "Received unknown PDU type: %" PRIu32 "", header.cmdId); + break; + } + + end = Stream_GetPosition(s); + + if (end != (beg + header.length)) + { + WLog_ERR(TAG, "Unexpected GFXREDIR pdu end: Actual: %d, Expected: %" PRIu32 "", end, + (beg + header.length)); + Stream_SetPosition(s, (beg + header.length)); + } + + return error; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gfxredir_server_handle_messages(GfxRedirServerContext* context) +{ + DWORD BytesReturned; + void* buffer; + UINT ret = CHANNEL_RC_OK; + GfxRedirServerPrivate* priv = context->priv; + wStream* s = priv->input_stream; + + /* Check whether the dynamic channel is ready */ + if (!priv->isReady) + { + if (WTSVirtualChannelQuery(priv->gfxredir_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 dynamic channel is ready */ + if (priv->isReady) + { + Stream_SetPosition(s, 0); + + if (!WTSVirtualChannelRead(priv->gfxredir_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->gfxredir_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 = gfxredir_server_receive_pdu(context, s))) + { + WLog_ERR(TAG, + "gfxredir_server_receive_pdu " + "failed with error %" PRIu32 "!", + ret); + return ret; + } + } + } + + return ret; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static DWORD WINAPI gfxredir_server_thread_func(LPVOID arg) +{ + GfxRedirServerContext* context = (GfxRedirServerContext*)arg; + GfxRedirServerPrivate* 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; + + 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 = gfxredir_server_handle_messages(context))) + { + WLog_ERR(TAG, "gfxredir_server_handle_messages failed with error %" PRIu32 "", error); + break; + } + } + + ExitThread(error); + return error; +} + +/** + * Function description + * Create new stream for single gfxredir packet. The new stream length + * would be required data length + header. The header will be written + * to the stream before return. + * + * @param cmdId + * @param length - data length without header + * + * @return new stream + */ +static wStream* gfxredir_server_single_packet_new(UINT32 cmdId, UINT32 length) +{ + UINT error; + GFXREDIR_HEADER header; + wStream* s = Stream_New(NULL, GFXREDIR_HEADER_SIZE + length); + + if (!s) + { + WLog_ERR(TAG, "Stream_New failed!"); + goto error; + } + + header.cmdId = cmdId; + header.length = GFXREDIR_HEADER_SIZE + length; + + if ((error = gfxredir_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; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gfxredir_server_packet_send(GfxRedirServerContext* context, wStream* s) +{ + UINT ret; + ULONG written; + + if (!WTSVirtualChannelWrite(context->priv->gfxredir_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 gfxredir_send_error(GfxRedirServerContext* context, const GFXREDIR_ERROR_PDU* error) +{ + wStream* s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_ERROR, 4); + + if (!s) + { + WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write_UINT32(s, error->errorCode); + return gfxredir_server_packet_send(context, s); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gfxredir_send_caps_confirm(GfxRedirServerContext* context, + const GFXREDIR_CAPS_CONFIRM_PDU* graphicsCapsConfirm) +{ + UINT ret; + wStream* s; + + if (graphicsCapsConfirm->length < GFXREDIR_CAPS_HEADER_SIZE) + { + WLog_ERR(TAG, "length must be greater than header size, failed!"); + return ERROR_INVALID_DATA; + } + + s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_CAPS_CONFIRM, graphicsCapsConfirm->length); + + if (!s) + { + WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write_UINT32(s, GFXREDIR_CAPS_SIGNATURE); + Stream_Write_UINT32(s, graphicsCapsConfirm->version); + Stream_Write_UINT32(s, graphicsCapsConfirm->length); + if (graphicsCapsConfirm->length > GFXREDIR_CAPS_HEADER_SIZE) + Stream_Write(s, graphicsCapsConfirm->capsData, + graphicsCapsConfirm->length - GFXREDIR_CAPS_HEADER_SIZE); + ret = gfxredir_server_packet_send(context, s); + if (ret == CHANNEL_RC_OK) + context->confirmedCapsVersion = graphicsCapsConfirm->version; + return ret; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gfxredir_send_open_pool(GfxRedirServerContext* context, + const GFXREDIR_OPEN_POOL_PDU* openPool) +{ + wStream* s; + + if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0) + { + WLog_ERR(TAG, "open_pool is called with invalid version!"); + return ERROR_INTERNAL_ERROR; + } + + if (openPool->sectionNameLength == 0 || openPool->sectionName == NULL) + { + WLog_ERR(TAG, "section name must be provided!"); + return ERROR_INVALID_DATA; + } + + /* make sure null-terminate */ + if (openPool->sectionName[openPool->sectionNameLength - 1] != 0) + { + WLog_ERR(TAG, "section name must be terminated with NULL!"); + return ERROR_INVALID_DATA; + } + + s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_OPEN_POOL, + 20 + (openPool->sectionNameLength * 2)); + + if (!s) + { + WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write_UINT64(s, openPool->poolId); + Stream_Write_UINT64(s, openPool->poolSize); + Stream_Write_UINT32(s, openPool->sectionNameLength); + Stream_Write(s, openPool->sectionName, (openPool->sectionNameLength * 2)); + return gfxredir_server_packet_send(context, s); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gfxredir_send_close_pool(GfxRedirServerContext* context, + const GFXREDIR_CLOSE_POOL_PDU* closePool) +{ + wStream* s; + + if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0) + { + WLog_ERR(TAG, "close_pool is called with invalid version!"); + return ERROR_INTERNAL_ERROR; + } + + s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_CLOSE_POOL, 8); + + if (!s) + { + WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write_UINT64(s, closePool->poolId); + return gfxredir_server_packet_send(context, s); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gfxredir_send_create_buffer(GfxRedirServerContext* context, + const GFXREDIR_CREATE_BUFFER_PDU* createBuffer) +{ + wStream* s; + + if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0) + { + WLog_ERR(TAG, "create_buffer is called with invalid version!"); + return ERROR_INTERNAL_ERROR; + } + + s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_CREATE_BUFFER, 40); + + if (!s) + { + WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write_UINT64(s, createBuffer->poolId); + Stream_Write_UINT64(s, createBuffer->bufferId); + Stream_Write_UINT64(s, createBuffer->offset); + Stream_Write_UINT32(s, createBuffer->stride); + Stream_Write_UINT32(s, createBuffer->width); + Stream_Write_UINT32(s, createBuffer->height); + Stream_Write_UINT32(s, createBuffer->format); + return gfxredir_server_packet_send(context, s); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gfxredir_send_destroy_buffer(GfxRedirServerContext* context, + const GFXREDIR_DESTROY_BUFFER_PDU* destroyBuffer) +{ + wStream* s; + + if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0) + { + WLog_ERR(TAG, "destroy_buffer is called with invalid version!"); + return ERROR_INTERNAL_ERROR; + } + + s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_DESTROY_BUFFER, 8); + + if (!s) + { + WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write_UINT64(s, destroyBuffer->bufferId); + return gfxredir_server_packet_send(context, s); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gfxredir_send_present_buffer(GfxRedirServerContext* context, + const GFXREDIR_PRESENT_BUFFER_PDU* presentBuffer) +{ + UINT32 length; + wStream* s; + RECTANGLE_32 dummyRect = { 0, 0, 0, 0 }; + + if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0) + { + WLog_ERR(TAG, "present_buffer is called with invalid version!"); + return ERROR_INTERNAL_ERROR; + } + + if (presentBuffer->numOpaqueRects > GFXREDIR_MAX_OPAQUE_RECTS) + { + WLog_ERR(TAG, "numOpaqueRects is larger than its limit!"); + return ERROR_INVALID_DATA; + } + + length = 64 + ((presentBuffer->numOpaqueRects ? presentBuffer->numOpaqueRects : 1) * + sizeof(RECTANGLE_32)); + + s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_PRESENT_BUFFER, length); + + if (!s) + { + WLog_ERR(TAG, "gfxredir_server_single_packet_new failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + Stream_Write_UINT64(s, presentBuffer->timestamp); + Stream_Write_UINT64(s, presentBuffer->presentId); + Stream_Write_UINT64(s, presentBuffer->windowId); + Stream_Write_UINT64(s, presentBuffer->bufferId); + Stream_Write_UINT32(s, presentBuffer->orientation); + Stream_Write_UINT32(s, presentBuffer->targetWidth); + Stream_Write_UINT32(s, presentBuffer->targetHeight); + Stream_Write_UINT32(s, presentBuffer->dirtyRect.left); + Stream_Write_UINT32(s, presentBuffer->dirtyRect.top); + Stream_Write_UINT32(s, presentBuffer->dirtyRect.width); + Stream_Write_UINT32(s, presentBuffer->dirtyRect.height); + Stream_Write_UINT32(s, presentBuffer->numOpaqueRects); + if (presentBuffer->numOpaqueRects) + Stream_Write(s, presentBuffer->opaqueRects, + (presentBuffer->numOpaqueRects * sizeof(RECTANGLE_32))); + else + Stream_Write(s, &dummyRect, sizeof(RECTANGLE_32)); + return gfxredir_server_packet_send(context, s); +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gfxredir_server_open(GfxRedirServerContext* context) +{ + UINT rc = ERROR_INTERNAL_ERROR; + GfxRedirServerPrivate* 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; + WTSFreeMemory(pSessionId); + priv->gfxredir_channel = (HANDLE)WTSVirtualChannelOpenEx( + priv->SessionId, GFXREDIR_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC); + + if (!priv->gfxredir_channel) + { + WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed!"); + rc = GetLastError(); + goto out_close; + } + + /* Query for channel event handle */ + if (!WTSVirtualChannelQuery(priv->gfxredir_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, gfxredir_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->gfxredir_channel); + priv->gfxredir_channel = NULL; + priv->channelEvent = NULL; + return rc; +} + +/** + * Function description + * + * @return 0 on success, otherwise a Win32 error code + */ +static UINT gfxredir_server_close(GfxRedirServerContext* context) +{ + UINT error = CHANNEL_RC_OK; + GfxRedirServerPrivate* 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; + } + + if (priv->gfxredir_channel) + { + WTSVirtualChannelClose(priv->gfxredir_channel); + priv->gfxredir_channel = NULL; + } + + return error; +} + +GfxRedirServerContext* gfxredir_server_context_new(HANDLE vcm) +{ + GfxRedirServerContext* context; + GfxRedirServerPrivate* priv; + context = (GfxRedirServerContext*)calloc(1, sizeof(GfxRedirServerContext)); + + if (!context) + { + WLog_ERR(TAG, "gfxredir_server_context_new(): calloc GfxRedirServerContext failed!"); + return NULL; + } + + priv = context->priv = (GfxRedirServerPrivate*)calloc(1, sizeof(GfxRedirServerPrivate)); + + if (!context->priv) + { + WLog_ERR(TAG, "gfxredir_server_context_new(): calloc GfxRedirServerPrivate 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 = gfxredir_server_open; + context->Close = gfxredir_server_close; + context->Error = gfxredir_send_error; + context->GraphicsRedirectionCapsConfirm = gfxredir_send_caps_confirm; + context->OpenPool = gfxredir_send_open_pool; + context->ClosePool = gfxredir_send_close_pool; + context->CreateBuffer = gfxredir_send_create_buffer; + context->DestroyBuffer = gfxredir_send_destroy_buffer; + context->PresentBuffer = gfxredir_send_present_buffer; + context->confirmedCapsVersion = GFXREDIR_CAPS_VERSION1; + priv->isReady = FALSE; + return context; +out_free_priv: + free(context->priv); +out_free: + free(context); + return NULL; +} + +void gfxredir_server_context_free(GfxRedirServerContext* context) +{ + if (!context) + return; + + gfxredir_server_close(context); + + if (context->priv) + { + Stream_Free(context->priv->input_stream, TRUE); + free(context->priv); + } + + free(context); +} diff --git a/channels/gfxredir/server/gfxredir_main.h b/channels/gfxredir/server/gfxredir_main.h new file mode 100644 index 000000000..e44e16a56 --- /dev/null +++ b/channels/gfxredir/server/gfxredir_main.h @@ -0,0 +1,37 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * RDPXXXX Remote App Graphics Redirection Virtual Channel Extension + * + * Copyright 2020 Microsoft + * + * 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_GFXREDIR_SERVER_MAIN_H +#define FREERDP_CHANNEL_GFXREDIR_SERVER_MAIN_H + +#include + +struct _gfxredir_server_private +{ + BOOL isReady; + wStream* input_stream; + HANDLE channelEvent; + HANDLE thread; + HANDLE stopEvent; + DWORD SessionId; + + void* gfxredir_channel; +}; + +#endif /* FREERDP_CHANNEL_GFXREDIR_SERVER_MAIN_H */ diff --git a/channels/server/channels.c b/channels/server/channels.c index 531848d9e..dec096954 100644 --- a/channels/server/channels.c +++ b/channels/server/channels.c @@ -52,6 +52,7 @@ #include #include #include +#include extern void freerdp_channels_dummy(void); @@ -69,6 +70,9 @@ void freerdp_channels_dummy(void) RailServerContext* rail; RdpgfxServerContext* rdpgfx; DispServerContext* disp; +#ifdef WITH_CHANNEL_GFXREDIR + GfxRedirServerContext* gfxredir; +#endif // WITH_CHANNEL_GFXREDIR audin = audin_server_context_new(NULL); audin_server_context_free(audin); rdpsnd = rdpsnd_server_context_new(NULL); @@ -93,6 +97,10 @@ void freerdp_channels_dummy(void) rdpgfx_server_context_free(rdpgfx); disp = disp_server_context_new(NULL); disp_server_context_free(disp); +#ifdef WITH_CHANNEL_GFXREDIR + gfxredir = gfxredir_server_context_new(NULL); + gfxredir_server_context_free(gfxredir); +#endif // WITH_CHANNEL_GFXREDIR } /** diff --git a/config.h.in b/config.h.in index ff4f98f05..2ab817a2a 100644 --- a/config.h.in +++ b/config.h.in @@ -58,6 +58,9 @@ #cmakedefine WITH_VAAPI +#cmakedefine WITH_CHANNEL_GFXREDIR +#cmakedefine WITH_CHANNEL_RDPAPPLIST + /* Plugins */ #cmakedefine BUILTIN_CHANNELS #cmakedefine WITH_RDPDR @@ -84,6 +87,9 @@ #cmakedefine CHANNEL_ENCOMSP #cmakedefine CHANNEL_ENCOMSP_CLIENT #cmakedefine CHANNEL_ENCOMSP_SERVER +#cmakedefine CHANNEL_GFXREDIR +#cmakedefine CHANNEL_GFXREDIR_CLIENT +#cmakedefine CHANNEL_GFXREDIR_SERVER #cmakedefine CHANNEL_PARALLEL #cmakedefine CHANNEL_PARALLEL_CLIENT #cmakedefine CHANNEL_PARALLEL_SERVER @@ -93,6 +99,9 @@ #cmakedefine CHANNEL_RAIL #cmakedefine CHANNEL_RAIL_CLIENT #cmakedefine CHANNEL_RAIL_SERVER +#cmakedefine CHANNEL_RDPAPPLIST +#cmakedefine CHANNEL_RDPAPPLIST_CLIENT +#cmakedefine CHANNEL_RDPAPPLIST_SERVER #cmakedefine CHANNEL_RDPDR #cmakedefine CHANNEL_RDPDR_CLIENT #cmakedefine CHANNEL_RDPDR_SERVER diff --git a/include/freerdp/channels/gfxredir.h b/include/freerdp/channels/gfxredir.h new file mode 100644 index 000000000..cf6e20ed0 --- /dev/null +++ b/include/freerdp/channels/gfxredir.h @@ -0,0 +1,183 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * RDPXXXX Remote App Graphics Redirection Virtual Channel Extension + * + * Copyright 2020 Hideyuki Nagase + * + * 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_GFXREDIR_H +#define FREERDP_CHANNEL_GFXREDIR_H + +#include +#include + +#define GFXREDIR_DVC_CHANNEL_NAME "Microsoft::Windows::RDS::RemoteAppGraphicsRedirection" + +/* GFXREDIR_LEGACY_CAPS_PDU.version */ +#define GFXREDIR_CHANNEL_VERSION_LEGACY 1 + +#define GFXREDIR_CHANNEL_VERSION_MAJOR 2 +#define GFXREDIR_CHANNEL_VERSION_MINOR 0 + +/* GFXREDIR_CAPS_VERSION1 */ +#define GFXREDIR_CMDID_LEGACY_CAPS 0x00000001 +#define GFXREDIR_CMDID_ERROR 0x00000006 +#define GFXREDIR_CMDID_CAPS_ADVERTISE 0x00000008 +#define GFXREDIR_CMDID_CAPS_CONFIRM 0x00000009 +/* GFXREDIR_CAPS_VERSION2_0 */ +#define GFXREDIR_CMDID_OPEN_POOL 0x0000000a +#define GFXREDIR_CMDID_CLOSE_POOL 0x0000000b +#define GFXREDIR_CMDID_CREATE_BUFFER 0x0000000c +#define GFXREDIR_CMDID_DESTROY_BUFFER 0x0000000d +#define GFXREDIR_CMDID_PRESENT_BUFFER 0x0000000e +#define GFXREDIR_CMDID_PRESENT_BUFFER_ACK 0x0000000f + +/* GFXREDIR_HEADER */ +#define GFXREDIR_HEADER_SIZE 8 + +/* GFXREDIR_CAPS_HEADER */ +#define GFXREDIR_CAPS_HEADER_SIZE 12 +/* GFXREDIR_CAPS_HEADER.signature */ +#define GFXREDIR_CAPS_SIGNATURE 0x53504143 /* = 'SPAC' */ +/* GFXREDIR_CAPS_HEADER.version */ +#define GFXREDIR_CAPS_VERSION1 0x1 +#define GFXREDIR_CAPS_VERSION2_0 0x2000 + +/* GFXREDIR_CREATE_BUFFER_PDU.format */ +#define GFXREDIR_BUFFER_PIXEL_FORMAT_XRGB_8888 1 +#define GFXREDIR_BUFFER_PIXEL_FORMAT_ARGB_8888 2 + +/* GFXREDIR_PRESENT_BUFFER_PDU.numOpaqueRects */ +#define GFXREDIR_MAX_OPAQUE_RECTS 0x10 + +struct _GFXREDIR_HEADER +{ + UINT32 cmdId; + UINT32 length; +}; + +typedef struct _GFXREDIR_HEADER GFXREDIR_HEADER; + +struct _GFXREDIR_LEGACY_CAPS_PDU +{ + UINT16 version; // GFXREDIR_CHANNEL_VERSION_LEGACY +}; + +typedef struct _GFXREDIR_LEGACY_CAPS_PDU GFXREDIR_LEGACY_CAPS_PDU; + +struct _GFXREDIR_CAPS_HEADER +{ + UINT32 signature; // GFXREDIR_CAPS_SIGNATURE + UINT32 version; // GFXREDIR_CAPS_VERSION + UINT32 length; // GFXREDIR_CAPS_HEADER_SIZE + size of capsData + const BYTE capsData[0]; // GFXREDIR_CAPS_HEADER.length - GFXREDIR_CAPS_HEADER_SIZE +}; + +typedef struct _GFXREDIR_CAPS_HEADER GFXREDIR_CAPS_HEADER; + +struct _GFXREDIR_CAPS_V2_0_PDU +{ + GFXREDIR_CAPS_HEADER header; + UINT32 supportedFeatures; /* Reserved for future extensions */ +}; + +typedef struct _GFXREDIR_CAPS_V2_0_PDU GFXREDIR_CAPS_V2_0_PDU; + +struct _GFXREDIR_ERROR_PDU +{ + UINT32 errorCode; +}; + +typedef struct _GFXREDIR_ERROR_PDU GFXREDIR_ERROR_PDU; + +struct _GFXREDIR_CAPS_ADVERTISE_PDU +{ + UINT32 length; // length of caps; + const BYTE* caps; // points variable length array of GFXREDIR_CAPS_HEADER. +}; + +typedef struct _GFXREDIR_CAPS_ADVERTISE_PDU GFXREDIR_CAPS_ADVERTISE_PDU; + +struct _GFXREDIR_CAPS_CONFIRM_PDU +{ + UINT32 version; // confirmed version, must be one of advertised by client. + UINT32 length; // GFXREDIR_CAPS_HEADER_SIZE + size of capsData. + const BYTE* capsData; // confirmed capsData from selected GFXREDIR_CAPS_HEADER.capsData. +}; + +typedef struct _GFXREDIR_CAPS_CONFIRM_PDU GFXREDIR_CAPS_CONFIRM_PDU; + +struct _GFXREDIR_OPEN_POOL_PDU +{ + UINT64 poolId; + UINT64 poolSize; + UINT32 sectionNameLength; // number of charactor, must include null terminated char. + const unsigned short* sectionName; // Windows-style 2 bytes wchar_t with null-terminated. +}; + +typedef struct _GFXREDIR_OPEN_POOL_PDU GFXREDIR_OPEN_POOL_PDU; + +struct _GFXREDIR_CLOSE_POOL_PDU +{ + UINT64 poolId; +}; + +typedef struct _GFXREDIR_CLOSE_POOL_PDU GFXREDIR_CLOSE_POOL_PDU; + +struct _GFXREDIR_CREATE_BUFFER_PDU +{ + UINT64 poolId; + UINT64 bufferId; + UINT64 offset; + UINT32 stride; + UINT32 width; + UINT32 height; + UINT32 format; // GFXREDIR_BUFFER_PIXEL_FORMAT_ +}; + +typedef struct _GFXREDIR_CREATE_BUFFER_PDU GFXREDIR_CREATE_BUFFER_PDU; + +struct _GFXREDIR_DESTROY_BUFFER_PDU +{ + UINT64 bufferId; +}; + +typedef struct _GFXREDIR_DESTROY_BUFFER_PDU GFXREDIR_DESTROY_BUFFER_PDU; + +struct _GFXREDIR_PRESENT_BUFFER_PDU +{ + UINT64 timestamp; + UINT64 presentId; + UINT64 windowId; + UINT64 bufferId; + UINT32 orientation; // 0, 90, 180 or 270. + UINT32 targetWidth; + UINT32 targetHeight; + RECTANGLE_32 dirtyRect; + UINT32 numOpaqueRects; + RECTANGLE_32* opaqueRects; +}; + +typedef struct _GFXREDIR_PRESENT_BUFFER_PDU GFXREDIR_PRESENT_BUFFER_PDU; + +struct _GFXREDIR_PRESENT_BUFFER_ACK_PDU +{ + UINT64 windowId; + UINT64 presentId; +}; + +typedef struct _GFXREDIR_PRESENT_BUFFER_ACK_PDU GFXREDIR_PRESENT_BUFFER_ACK_PDU; + +#endif /* FREERDP_CHANNEL_GFXREDIR_H */ diff --git a/include/freerdp/server/gfxredir.h b/include/freerdp/server/gfxredir.h new file mode 100644 index 000000000..05fdea62c --- /dev/null +++ b/include/freerdp/server/gfxredir.h @@ -0,0 +1,102 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * RDPXXXX Remote App Graphics Redirection Virtual Channel Extension + * + * Copyright 2020 Microsoft + * + * 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_GFXREDIR_SERVER_GFXREDIR_H +#define FREERDP_CHANNEL_GFXREDIR_SERVER_GFXREDIR_H + +#include + +#include +#include +#include + +typedef struct _gfxredir_server_private GfxRedirServerPrivate; +typedef struct _gfxredir_server_context GfxRedirServerContext; + +typedef UINT (*psGfxRedirOpen)(GfxRedirServerContext* context); +typedef UINT (*psGfxRedirClose)(GfxRedirServerContext* context); + +typedef UINT (*psGfxRedirError)(GfxRedirServerContext* context, const GFXREDIR_ERROR_PDU* error); + +typedef UINT (*psGfxRedirGraphicsRedirectionLegacyCaps)( + GfxRedirServerContext* context, const GFXREDIR_LEGACY_CAPS_PDU* graphicsCaps); + +typedef UINT (*psGfxRedirGraphicsRedirectionCapsAdvertise)( + GfxRedirServerContext* context, const GFXREDIR_CAPS_ADVERTISE_PDU* graphicsCapsAdvertise); +typedef UINT (*psGfxRedirGraphicsRedirectionCapsConfirm)( + GfxRedirServerContext* context, const GFXREDIR_CAPS_CONFIRM_PDU* graphicsCapsConfirm); + +typedef UINT (*psGfxRedirOpenPool)(GfxRedirServerContext* context, + const GFXREDIR_OPEN_POOL_PDU* openPool); +typedef UINT (*psGfxRedirClosePool)(GfxRedirServerContext* context, + const GFXREDIR_CLOSE_POOL_PDU* closePool); + +typedef UINT (*psGfxRedirCreateBuffer)(GfxRedirServerContext* context, + const GFXREDIR_CREATE_BUFFER_PDU* createBuffer); +typedef UINT (*psGfxRedirDestroyBuffer)(GfxRedirServerContext* context, + const GFXREDIR_DESTROY_BUFFER_PDU* destroyBuffer); + +typedef UINT (*psGfxRedirPresentBuffer)(GfxRedirServerContext* context, + const GFXREDIR_PRESENT_BUFFER_PDU* presentBuffer); +typedef UINT (*psGfxRedirPresentBufferAck)(GfxRedirServerContext* context, + const GFXREDIR_PRESENT_BUFFER_ACK_PDU* presentBufferAck); + +struct _gfxredir_server_context +{ + void* custom; + HANDLE vcm; + + psGfxRedirOpen Open; + psGfxRedirClose Close; + + psGfxRedirError Error; + + psGfxRedirGraphicsRedirectionLegacyCaps GraphicsRedirectionLegacyCaps; + + psGfxRedirGraphicsRedirectionCapsAdvertise GraphicsRedirectionCapsAdvertise; + psGfxRedirGraphicsRedirectionCapsConfirm GraphicsRedirectionCapsConfirm; + + psGfxRedirOpenPool OpenPool; + psGfxRedirClosePool ClosePool; + + psGfxRedirCreateBuffer CreateBuffer; + psGfxRedirDestroyBuffer DestroyBuffer; + + psGfxRedirPresentBuffer PresentBuffer; + psGfxRedirPresentBufferAck PresentBufferAck; + + GfxRedirServerPrivate* priv; + rdpContext* rdpcontext; + + UINT32 confirmedCapsVersion; +}; + +#ifdef __cplusplus +extern "C" +{ +#endif + + FREERDP_API GfxRedirServerContext* gfxredir_server_context_new(HANDLE vcm); + FREERDP_API void gfxredir_server_context_free(GfxRedirServerContext* context); + +#ifdef __cplusplus +} +#endif + +#endif /* FREERDP_CHANNEL_GFXREDIR_SERVER_GFXREDIR_H */ diff --git a/include/freerdp/types.h b/include/freerdp/types.h index 165c1717e..32b4fbfd2 100644 --- a/include/freerdp/types.h +++ b/include/freerdp/types.h @@ -74,6 +74,15 @@ struct _RECTANGLE_16 }; typedef struct _RECTANGLE_16 RECTANGLE_16; +struct _RECTANGLE_32 +{ + UINT32 left; + UINT32 top; + UINT32 width; + UINT32 height; +}; +typedef struct _RECTANGLE_32 RECTANGLE_32; + /* Plugin events */ #include