20#include <freerdp/config.h>
22#include "gfxredir_main.h"
28#include <winpr/assert.h>
29#include <winpr/synch.h>
30#include <winpr/thread.h>
31#include <winpr/stream.h>
32#include <winpr/sysinfo.h>
33#include <freerdp/channels/wtsvc.h>
34#include <freerdp/channels/log.h>
36#include <freerdp/server/gfxredir.h>
37#include <gfxredir_common.h>
39#define TAG CHANNELS_TAG("gfxredir.server")
46static UINT gfxredir_recv_legacy_caps_pdu(
wStream* s, GfxRedirServerContext* context)
48 UINT32 error = CHANNEL_RC_OK;
51 WINPR_ASSERT(context);
53 if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
54 return ERROR_INVALID_DATA;
56 Stream_Read_UINT16(s, pdu.version);
59 IFCALLRET(context->GraphicsRedirectionLegacyCaps, error, context, &pdu);
69static UINT gfxredir_recv_caps_advertise_pdu(
wStream* s, UINT32 length,
70 GfxRedirServerContext* context)
72 UINT32 error = CHANNEL_RC_OK;
75 WINPR_ASSERT(context);
77 if (!Stream_CheckAndLogRequiredLength(TAG, s, length))
78 return ERROR_INVALID_DATA;
81 Stream_GetPointer(s, pdu.caps);
82 Stream_Seek(s, length);
84 if (!context->GraphicsRedirectionCapsAdvertise)
86 WLog_ERR(TAG,
"server does not support CapsAdvertise PDU!");
87 return ERROR_NOT_SUPPORTED;
91 IFCALLRET(context->GraphicsRedirectionCapsAdvertise, error, context, &pdu);
102static UINT gfxredir_recv_present_buffer_ack_pdu(
wStream* s, GfxRedirServerContext* context)
104 UINT32 error = CHANNEL_RC_OK;
107 WINPR_ASSERT(context);
109 if (!Stream_CheckAndLogRequiredLength(TAG, s, 16))
110 return ERROR_INVALID_DATA;
112 Stream_Read_UINT64(s, pdu.windowId);
113 Stream_Read_UINT64(s, pdu.presentId);
117 IFCALLRET(context->PresentBufferAck, error, context, &pdu);
128static UINT gfxredir_server_receive_pdu(GfxRedirServerContext* context,
wStream* data)
130 UINT error = CHANNEL_RC_OK;
133 WINPR_ASSERT(context);
135 size_t packetLen = 0;
136 if ((error = gfxredir_read_header(data, &header, &packetLen)))
138 WLog_ERR(TAG,
"gfxredir_read_header failed with error %" PRIu32
"!", error);
142 wStream sbuffer = WINPR_C_ARRAY_INIT;
143 wStream* s = Stream_StaticConstInit(&sbuffer, Stream_ConstPointer(data), packetLen);
144 Stream_Seek(data, packetLen);
146 switch (header.cmdId)
148 case GFXREDIR_CMDID_LEGACY_CAPS:
149 if ((error = gfxredir_recv_legacy_caps_pdu(s, context)))
151 "gfxredir_recv_legacy_caps_pdu "
152 "failed with error %" PRIu32
"!",
157 case GFXREDIR_CMDID_CAPS_ADVERTISE:
158 if ((error = gfxredir_recv_caps_advertise_pdu(s, header.length - 8, context)))
160 "gfxredir_recv_caps_advertise "
161 "failed with error %" PRIu32
"!",
165 case GFXREDIR_CMDID_PRESENT_BUFFER_ACK:
166 if ((error = gfxredir_recv_present_buffer_ack_pdu(s, context)))
168 "gfxredir_recv_present_buffer_ask_pdu "
169 "failed with error %" PRIu32
"!",
174 error = CHANNEL_RC_BAD_PROC;
175 WLog_WARN(TAG,
"Received unknown PDU type: %" PRIu32
"", header.cmdId);
179 const size_t end = Stream_GetPosition(s);
181 if (end != packetLen)
183 WLog_ERR(TAG,
"Unexpected GFXREDIR pdu end: Actual: %" PRIuz
", Expected: %" PRIuz
"", end,
195static UINT gfxredir_server_handle_messages(GfxRedirServerContext* context)
197 UINT ret = CHANNEL_RC_OK;
199 WINPR_ASSERT(context);
201 GfxRedirServerPrivate* priv = context->priv;
204 wStream* s = priv->input_stream;
209 void* buffer =
nullptr;
210 DWORD BytesReturned = 0;
211 if (WTSVirtualChannelQuery(priv->gfxredir_channel, WTSVirtualChannelReady, &buffer,
212 &BytesReturned) == FALSE)
214 if (GetLastError() == ERROR_NO_DATA)
215 return ERROR_NO_DATA;
217 WLog_ERR(TAG,
"WTSVirtualChannelQuery failed");
218 return ERROR_INTERNAL_ERROR;
221 priv->isReady = *((BOOL*)buffer);
222 WTSFreeMemory(buffer);
228 Stream_ResetPosition(s);
230 DWORD BytesReturned = 0;
231 if (!WTSVirtualChannelRead(priv->gfxredir_channel, 0,
nullptr, 0, &BytesReturned))
233 if (GetLastError() == ERROR_NO_DATA)
234 return ERROR_NO_DATA;
236 WLog_ERR(TAG,
"WTSVirtualChannelRead failed!");
237 return ERROR_INTERNAL_ERROR;
240 if (BytesReturned < 1)
241 return CHANNEL_RC_OK;
243 if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
245 WLog_ERR(TAG,
"Stream_EnsureRemainingCapacity failed!");
246 return CHANNEL_RC_NO_MEMORY;
249 const ULONG cap = WINPR_ASSERTING_INT_CAST(ULONG, Stream_Capacity(s));
250 if (WTSVirtualChannelRead(priv->gfxredir_channel, 0, (PCHAR)Stream_Buffer(s), cap,
251 &BytesReturned) == FALSE)
253 WLog_ERR(TAG,
"WTSVirtualChannelRead failed!");
254 return ERROR_INTERNAL_ERROR;
257 if (!Stream_SetLength(s, BytesReturned))
258 return ERROR_INTERNAL_ERROR;
260 Stream_ResetPosition(s);
262 while (Stream_GetPosition(s) < Stream_Length(s))
264 if ((ret = gfxredir_server_receive_pdu(context, s)))
267 "gfxredir_server_receive_pdu "
268 "failed with error %" PRIu32
"!",
283static DWORD WINAPI gfxredir_server_thread_func(LPVOID arg)
285 GfxRedirServerContext* context = (GfxRedirServerContext*)arg;
286 WINPR_ASSERT(context);
288 GfxRedirServerPrivate* priv = context->priv;
291 HANDLE events[8] = WINPR_C_ARRAY_INIT;
292 UINT error = CHANNEL_RC_OK;
294 events[nCount++] = priv->stopEvent;
295 events[nCount++] = priv->channelEvent;
299 const DWORD status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
301 if (status == WAIT_FAILED)
303 error = GetLastError();
304 WLog_ERR(TAG,
"WaitForMultipleObjects failed with error %" PRIu32
"", error);
309 if (status == WAIT_OBJECT_0)
312 if ((error = gfxredir_server_handle_messages(context)))
314 WLog_ERR(TAG,
"gfxredir_server_handle_messages failed with error %" PRIu32
"", error);
334static wStream* gfxredir_server_single_packet_new(UINT32 cmdId,
size_t length)
337 wStream* s = Stream_New(
nullptr, GFXREDIR_HEADER_SIZE + length);
341 WLog_ERR(TAG,
"Stream_New failed!");
345 header.cmdId = cmdId;
346 header.length = WINPR_ASSERTING_INT_CAST(UINT32, GFXREDIR_HEADER_SIZE + length);
348 const UINT error = gfxredir_write_header(s, &header);
351 WLog_ERR(TAG,
"Failed to write header with error %" PRIu32
"!", error);
357 Stream_Free(s, TRUE);
366static UINT gfxredir_server_packet_send(GfxRedirServerContext* context,
wStream* s)
368 UINT ret = ERROR_INTERNAL_ERROR;
371 WINPR_ASSERT(context);
373 const ULONG cap = WINPR_ASSERTING_INT_CAST(ULONG, Stream_GetPosition(s));
374 if (!WTSVirtualChannelWrite(context->priv->gfxredir_channel, (PCHAR)Stream_Buffer(s), cap,
377 WLog_ERR(TAG,
"WTSVirtualChannelWrite failed!");
378 ret = ERROR_INTERNAL_ERROR;
382 if (written < Stream_GetPosition(s))
384 WLog_WARN(TAG,
"Unexpected bytes written: %" PRIu32
"/%" PRIuz
"", written,
385 Stream_GetPosition(s));
390 Stream_Free(s, TRUE);
399static UINT gfxredir_send_error(GfxRedirServerContext* context,
const GFXREDIR_ERROR_PDU* error)
401 WINPR_ASSERT(context);
404 wStream* s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_ERROR, 4);
408 WLog_ERR(TAG,
"gfxredir_server_single_packet_new failed!");
409 return CHANNEL_RC_NO_MEMORY;
412 Stream_Write_UINT32(s, error->errorCode);
413 return gfxredir_server_packet_send(context, s);
421static UINT gfxredir_send_caps_confirm(GfxRedirServerContext* context,
424 WINPR_ASSERT(context);
425 WINPR_ASSERT(graphicsCapsConfirm);
427 if (graphicsCapsConfirm->length < GFXREDIR_CAPS_HEADER_SIZE)
429 WLog_ERR(TAG,
"length must be greater than header size, failed!");
430 return ERROR_INVALID_DATA;
434 gfxredir_server_single_packet_new(GFXREDIR_CMDID_CAPS_CONFIRM, graphicsCapsConfirm->length);
438 WLog_ERR(TAG,
"gfxredir_server_single_packet_new failed!");
439 return CHANNEL_RC_NO_MEMORY;
442 Stream_Write_UINT32(s, GFXREDIR_CAPS_SIGNATURE);
443 Stream_Write_UINT32(s, graphicsCapsConfirm->version);
444 Stream_Write_UINT32(s, graphicsCapsConfirm->length);
445 if (graphicsCapsConfirm->length > GFXREDIR_CAPS_HEADER_SIZE)
446 Stream_Write(s, graphicsCapsConfirm->capsData,
447 graphicsCapsConfirm->length - GFXREDIR_CAPS_HEADER_SIZE);
448 const UINT ret = gfxredir_server_packet_send(context, s);
449 if (ret == CHANNEL_RC_OK)
450 context->confirmedCapsVersion = graphicsCapsConfirm->version;
459static UINT gfxredir_send_open_pool(GfxRedirServerContext* context,
462 WINPR_ASSERT(context);
463 WINPR_ASSERT(openPool);
465 if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
467 WLog_ERR(TAG,
"open_pool is called with invalid version!");
468 return ERROR_INTERNAL_ERROR;
471 if (openPool->sectionNameLength == 0 || openPool->sectionName ==
nullptr)
473 WLog_ERR(TAG,
"section name must be provided!");
474 return ERROR_INVALID_DATA;
478 if (openPool->sectionName[openPool->sectionNameLength - 1] != 0)
480 WLog_ERR(TAG,
"section name must be terminated with nullptr!");
481 return ERROR_INVALID_DATA;
484 wStream* s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_OPEN_POOL,
485 20 + (openPool->sectionNameLength * 2));
489 WLog_ERR(TAG,
"gfxredir_server_single_packet_new failed!");
490 return CHANNEL_RC_NO_MEMORY;
493 Stream_Write_UINT64(s, openPool->poolId);
494 Stream_Write_UINT64(s, openPool->poolSize);
495 Stream_Write_UINT32(s, openPool->sectionNameLength);
496 Stream_Write(s, openPool->sectionName, (2ULL * openPool->sectionNameLength));
497 return gfxredir_server_packet_send(context, s);
505static UINT gfxredir_send_close_pool(GfxRedirServerContext* context,
508 WINPR_ASSERT(context);
509 WINPR_ASSERT(closePool);
511 if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
513 WLog_ERR(TAG,
"close_pool is called with invalid version!");
514 return ERROR_INTERNAL_ERROR;
517 wStream* s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_CLOSE_POOL, 8);
521 WLog_ERR(TAG,
"gfxredir_server_single_packet_new failed!");
522 return CHANNEL_RC_NO_MEMORY;
525 Stream_Write_UINT64(s, closePool->poolId);
526 return gfxredir_server_packet_send(context, s);
534static UINT gfxredir_send_create_buffer(GfxRedirServerContext* context,
537 WINPR_ASSERT(context);
538 WINPR_ASSERT(createBuffer);
540 if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
542 WLog_ERR(TAG,
"create_buffer is called with invalid version!");
543 return ERROR_INTERNAL_ERROR;
546 wStream* s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_CREATE_BUFFER, 40);
550 WLog_ERR(TAG,
"gfxredir_server_single_packet_new failed!");
551 return CHANNEL_RC_NO_MEMORY;
554 Stream_Write_UINT64(s, createBuffer->poolId);
555 Stream_Write_UINT64(s, createBuffer->bufferId);
556 Stream_Write_UINT64(s, createBuffer->offset);
557 Stream_Write_UINT32(s, createBuffer->stride);
558 Stream_Write_UINT32(s, createBuffer->width);
559 Stream_Write_UINT32(s, createBuffer->height);
560 Stream_Write_UINT32(s, createBuffer->format);
561 return gfxredir_server_packet_send(context, s);
569static UINT gfxredir_send_destroy_buffer(GfxRedirServerContext* context,
572 WINPR_ASSERT(context);
573 WINPR_ASSERT(destroyBuffer);
575 if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
577 WLog_ERR(TAG,
"destroy_buffer is called with invalid version!");
578 return ERROR_INTERNAL_ERROR;
581 wStream* s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_DESTROY_BUFFER, 8);
585 WLog_ERR(TAG,
"gfxredir_server_single_packet_new failed!");
586 return CHANNEL_RC_NO_MEMORY;
589 Stream_Write_UINT64(s, destroyBuffer->bufferId);
590 return gfxredir_server_packet_send(context, s);
598static UINT gfxredir_send_present_buffer(GfxRedirServerContext* context,
603 WINPR_ASSERT(context);
604 WINPR_ASSERT(presentBuffer);
606 if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
608 WLog_ERR(TAG,
"present_buffer is called with invalid version!");
609 return ERROR_INTERNAL_ERROR;
612 if (presentBuffer->numOpaqueRects > GFXREDIR_MAX_OPAQUE_RECTS)
614 WLog_ERR(TAG,
"numOpaqueRects is larger than its limit!");
615 return ERROR_INVALID_DATA;
618 const size_t length =
619 64ULL + ((presentBuffer->numOpaqueRects ? presentBuffer->numOpaqueRects : 1) *
622 wStream* s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_PRESENT_BUFFER, length);
626 WLog_ERR(TAG,
"gfxredir_server_single_packet_new failed!");
627 return CHANNEL_RC_NO_MEMORY;
630 Stream_Write_UINT64(s, presentBuffer->timestamp);
631 Stream_Write_UINT64(s, presentBuffer->presentId);
632 Stream_Write_UINT64(s, presentBuffer->windowId);
633 Stream_Write_UINT64(s, presentBuffer->bufferId);
634 Stream_Write_UINT32(s, presentBuffer->orientation);
635 Stream_Write_UINT32(s, presentBuffer->targetWidth);
636 Stream_Write_UINT32(s, presentBuffer->targetHeight);
637 Stream_Write_UINT32(s, presentBuffer->dirtyRect.left);
638 Stream_Write_UINT32(s, presentBuffer->dirtyRect.top);
639 Stream_Write_UINT32(s, presentBuffer->dirtyRect.width);
640 Stream_Write_UINT32(s, presentBuffer->dirtyRect.height);
641 Stream_Write_UINT32(s, presentBuffer->numOpaqueRects);
642 if (presentBuffer->numOpaqueRects)
643 Stream_Write(s, presentBuffer->opaqueRects,
644 (presentBuffer->numOpaqueRects *
sizeof(
RECTANGLE_32)));
647 return gfxredir_server_packet_send(context, s);
655static UINT gfxredir_server_open(GfxRedirServerContext* context)
657 UINT rc = ERROR_INTERNAL_ERROR;
658 WINPR_ASSERT(context);
660 GfxRedirServerPrivate* priv = context->priv;
663 DWORD BytesReturned = 0;
664 PULONG pSessionId =
nullptr;
665 void* buffer =
nullptr;
666 priv->SessionId = WTS_CURRENT_SESSION;
668 if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
669 (LPSTR*)&pSessionId, &BytesReturned) == FALSE)
671 WLog_ERR(TAG,
"WTSQuerySessionInformationA failed!");
672 rc = ERROR_INTERNAL_ERROR;
676 priv->SessionId = (DWORD)*pSessionId;
677 WTSFreeMemory(pSessionId);
678 priv->gfxredir_channel = WTSVirtualChannelOpenEx(priv->SessionId, GFXREDIR_DVC_CHANNEL_NAME,
679 WTS_CHANNEL_OPTION_DYNAMIC);
681 if (!priv->gfxredir_channel)
683 WLog_ERR(TAG,
"WTSVirtualChannelOpenEx failed!");
689 if (!WTSVirtualChannelQuery(priv->gfxredir_channel, WTSVirtualEventHandle, &buffer,
691 (BytesReturned !=
sizeof(HANDLE)))
694 "WTSVirtualChannelQuery failed "
695 "or invalid returned size(%" PRIu32
")",
699 WTSFreeMemory(buffer);
701 rc = ERROR_INTERNAL_ERROR;
705 priv->channelEvent = *(HANDLE*)buffer;
706 WTSFreeMemory(buffer);
708 if (priv->thread ==
nullptr)
710 if (!(priv->stopEvent = CreateEvent(
nullptr, TRUE, FALSE,
nullptr)))
712 WLog_ERR(TAG,
"CreateEvent failed!");
716 if (!(priv->thread = CreateThread(
nullptr, 0, gfxredir_server_thread_func, (
void*)context,
719 WLog_ERR(TAG,
"CreateEvent failed!");
720 (void)CloseHandle(priv->stopEvent);
721 priv->stopEvent =
nullptr;
726 return CHANNEL_RC_OK;
728 WTSVirtualChannelClose(priv->gfxredir_channel);
729 priv->gfxredir_channel =
nullptr;
730 priv->channelEvent =
nullptr;
739static UINT gfxredir_server_close(GfxRedirServerContext* context)
741 UINT error = CHANNEL_RC_OK;
742 WINPR_ASSERT(context);
744 GfxRedirServerPrivate* priv = context->priv;
749 (void)SetEvent(priv->stopEvent);
751 if (WaitForSingleObject(priv->thread, INFINITE) == WAIT_FAILED)
753 error = GetLastError();
754 WLog_ERR(TAG,
"WaitForSingleObject failed with error %" PRIu32
"", error);
758 (void)CloseHandle(priv->thread);
759 (void)CloseHandle(priv->stopEvent);
760 priv->thread =
nullptr;
761 priv->stopEvent =
nullptr;
764 if (priv->gfxredir_channel)
766 WTSVirtualChannelClose(priv->gfxredir_channel);
767 priv->gfxredir_channel =
nullptr;
773GfxRedirServerContext* gfxredir_server_context_new(HANDLE vcm)
775 GfxRedirServerContext* context =
776 (GfxRedirServerContext*)calloc(1,
sizeof(GfxRedirServerContext));
780 WLog_ERR(TAG,
"gfxredir_server_context_new(): calloc GfxRedirServerContext failed!");
784 GfxRedirServerPrivate* priv = context->priv =
785 (GfxRedirServerPrivate*)calloc(1,
sizeof(GfxRedirServerPrivate));
789 WLog_ERR(TAG,
"gfxredir_server_context_new(): calloc GfxRedirServerPrivate failed!");
793 priv->input_stream = Stream_New(
nullptr, 4);
795 if (!priv->input_stream)
797 WLog_ERR(TAG,
"Stream_New failed!");
802 context->Open = gfxredir_server_open;
803 context->Close = gfxredir_server_close;
804 context->Error = gfxredir_send_error;
805 context->GraphicsRedirectionCapsConfirm = gfxredir_send_caps_confirm;
806 context->OpenPool = gfxredir_send_open_pool;
807 context->ClosePool = gfxredir_send_close_pool;
808 context->CreateBuffer = gfxredir_send_create_buffer;
809 context->DestroyBuffer = gfxredir_send_destroy_buffer;
810 context->PresentBuffer = gfxredir_send_present_buffer;
811 context->confirmedCapsVersion = GFXREDIR_CAPS_VERSION1;
812 priv->isReady = FALSE;
815 gfxredir_server_context_free(context);
819void gfxredir_server_context_free(GfxRedirServerContext* context)
824 gfxredir_server_close(context);
828 Stream_Free(context->priv->input_stream, TRUE);