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* s)
130 UINT error = CHANNEL_RC_OK;
133 WINPR_ASSERT(context);
135 const size_t beg = Stream_GetPosition(s);
137 if ((error = gfxredir_read_header(s, &header)))
139 WLog_ERR(TAG,
"gfxredir_read_header failed with error %" PRIu32
"!", error);
143 switch (header.cmdId)
145 case GFXREDIR_CMDID_LEGACY_CAPS:
146 if ((error = gfxredir_recv_legacy_caps_pdu(s, context)))
148 "gfxredir_recv_legacy_caps_pdu "
149 "failed with error %" PRIu32
"!",
154 case GFXREDIR_CMDID_CAPS_ADVERTISE:
155 if ((error = gfxredir_recv_caps_advertise_pdu(s, header.length - 8, context)))
157 "gfxredir_recv_caps_advertise "
158 "failed with error %" PRIu32
"!",
162 case GFXREDIR_CMDID_PRESENT_BUFFER_ACK:
163 if ((error = gfxredir_recv_present_buffer_ack_pdu(s, context)))
165 "gfxredir_recv_present_buffer_ask_pdu "
166 "failed with error %" PRIu32
"!",
171 error = CHANNEL_RC_BAD_PROC;
172 WLog_WARN(TAG,
"Received unknown PDU type: %" PRIu32
"", header.cmdId);
176 const size_t end = Stream_GetPosition(s);
178 if (end != (beg + header.length))
180 WLog_ERR(TAG,
"Unexpected GFXREDIR pdu end: Actual: %" PRIuz
", Expected: %" PRIuz
"", end,
181 (beg + header.length));
182 if (!Stream_SetPosition(s, (beg + header.length)))
183 return ERROR_INVALID_DATA;
194static UINT gfxredir_server_handle_messages(GfxRedirServerContext* context)
196 UINT ret = CHANNEL_RC_OK;
198 WINPR_ASSERT(context);
200 GfxRedirServerPrivate* priv = context->priv;
203 wStream* s = priv->input_stream;
208 void* buffer =
nullptr;
209 DWORD BytesReturned = 0;
210 if (WTSVirtualChannelQuery(priv->gfxredir_channel, WTSVirtualChannelReady, &buffer,
211 &BytesReturned) == FALSE)
213 if (GetLastError() == ERROR_NO_DATA)
214 return ERROR_NO_DATA;
216 WLog_ERR(TAG,
"WTSVirtualChannelQuery failed");
217 return ERROR_INTERNAL_ERROR;
220 priv->isReady = *((BOOL*)buffer);
221 WTSFreeMemory(buffer);
227 Stream_ResetPosition(s);
229 DWORD BytesReturned = 0;
230 if (!WTSVirtualChannelRead(priv->gfxredir_channel, 0,
nullptr, 0, &BytesReturned))
232 if (GetLastError() == ERROR_NO_DATA)
233 return ERROR_NO_DATA;
235 WLog_ERR(TAG,
"WTSVirtualChannelRead failed!");
236 return ERROR_INTERNAL_ERROR;
239 if (BytesReturned < 1)
240 return CHANNEL_RC_OK;
242 if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
244 WLog_ERR(TAG,
"Stream_EnsureRemainingCapacity failed!");
245 return CHANNEL_RC_NO_MEMORY;
248 const ULONG cap = WINPR_ASSERTING_INT_CAST(ULONG, Stream_Capacity(s));
249 if (WTSVirtualChannelRead(priv->gfxredir_channel, 0, (PCHAR)Stream_Buffer(s), cap,
250 &BytesReturned) == FALSE)
252 WLog_ERR(TAG,
"WTSVirtualChannelRead failed!");
253 return ERROR_INTERNAL_ERROR;
256 if (!Stream_SetLength(s, BytesReturned))
257 return ERROR_INTERNAL_ERROR;
259 Stream_ResetPosition(s);
261 while (Stream_GetPosition(s) < Stream_Length(s))
263 if ((ret = gfxredir_server_receive_pdu(context, s)))
266 "gfxredir_server_receive_pdu "
267 "failed with error %" PRIu32
"!",
282static DWORD WINAPI gfxredir_server_thread_func(LPVOID arg)
284 GfxRedirServerContext* context = (GfxRedirServerContext*)arg;
285 WINPR_ASSERT(context);
287 GfxRedirServerPrivate* priv = context->priv;
290 HANDLE events[8] = WINPR_C_ARRAY_INIT;
291 UINT error = CHANNEL_RC_OK;
293 events[nCount++] = priv->stopEvent;
294 events[nCount++] = priv->channelEvent;
298 const DWORD status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
300 if (status == WAIT_FAILED)
302 error = GetLastError();
303 WLog_ERR(TAG,
"WaitForMultipleObjects failed with error %" PRIu32
"", error);
308 if (status == WAIT_OBJECT_0)
311 if ((error = gfxredir_server_handle_messages(context)))
313 WLog_ERR(TAG,
"gfxredir_server_handle_messages failed with error %" PRIu32
"", error);
333static wStream* gfxredir_server_single_packet_new(UINT32 cmdId,
size_t length)
336 wStream* s = Stream_New(
nullptr, GFXREDIR_HEADER_SIZE + length);
340 WLog_ERR(TAG,
"Stream_New failed!");
344 header.cmdId = cmdId;
345 header.length = WINPR_ASSERTING_INT_CAST(UINT32, GFXREDIR_HEADER_SIZE + length);
347 const UINT error = gfxredir_write_header(s, &header);
350 WLog_ERR(TAG,
"Failed to write header with error %" PRIu32
"!", error);
356 Stream_Free(s, TRUE);
365static UINT gfxredir_server_packet_send(GfxRedirServerContext* context,
wStream* s)
367 UINT ret = ERROR_INTERNAL_ERROR;
370 WINPR_ASSERT(context);
372 const ULONG cap = WINPR_ASSERTING_INT_CAST(ULONG, Stream_GetPosition(s));
373 if (!WTSVirtualChannelWrite(context->priv->gfxredir_channel, (PCHAR)Stream_Buffer(s), cap,
376 WLog_ERR(TAG,
"WTSVirtualChannelWrite failed!");
377 ret = ERROR_INTERNAL_ERROR;
381 if (written < Stream_GetPosition(s))
383 WLog_WARN(TAG,
"Unexpected bytes written: %" PRIu32
"/%" PRIuz
"", written,
384 Stream_GetPosition(s));
389 Stream_Free(s, TRUE);
398static UINT gfxredir_send_error(GfxRedirServerContext* context,
const GFXREDIR_ERROR_PDU* error)
400 WINPR_ASSERT(context);
403 wStream* s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_ERROR, 4);
407 WLog_ERR(TAG,
"gfxredir_server_single_packet_new failed!");
408 return CHANNEL_RC_NO_MEMORY;
411 Stream_Write_UINT32(s, error->errorCode);
412 return gfxredir_server_packet_send(context, s);
420static UINT gfxredir_send_caps_confirm(GfxRedirServerContext* context,
423 WINPR_ASSERT(context);
424 WINPR_ASSERT(graphicsCapsConfirm);
426 if (graphicsCapsConfirm->length < GFXREDIR_CAPS_HEADER_SIZE)
428 WLog_ERR(TAG,
"length must be greater than header size, failed!");
429 return ERROR_INVALID_DATA;
433 gfxredir_server_single_packet_new(GFXREDIR_CMDID_CAPS_CONFIRM, graphicsCapsConfirm->length);
437 WLog_ERR(TAG,
"gfxredir_server_single_packet_new failed!");
438 return CHANNEL_RC_NO_MEMORY;
441 Stream_Write_UINT32(s, GFXREDIR_CAPS_SIGNATURE);
442 Stream_Write_UINT32(s, graphicsCapsConfirm->version);
443 Stream_Write_UINT32(s, graphicsCapsConfirm->length);
444 if (graphicsCapsConfirm->length > GFXREDIR_CAPS_HEADER_SIZE)
445 Stream_Write(s, graphicsCapsConfirm->capsData,
446 graphicsCapsConfirm->length - GFXREDIR_CAPS_HEADER_SIZE);
447 const UINT ret = gfxredir_server_packet_send(context, s);
448 if (ret == CHANNEL_RC_OK)
449 context->confirmedCapsVersion = graphicsCapsConfirm->version;
458static UINT gfxredir_send_open_pool(GfxRedirServerContext* context,
461 WINPR_ASSERT(context);
462 WINPR_ASSERT(openPool);
464 if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
466 WLog_ERR(TAG,
"open_pool is called with invalid version!");
467 return ERROR_INTERNAL_ERROR;
470 if (openPool->sectionNameLength == 0 || openPool->sectionName ==
nullptr)
472 WLog_ERR(TAG,
"section name must be provided!");
473 return ERROR_INVALID_DATA;
477 if (openPool->sectionName[openPool->sectionNameLength - 1] != 0)
479 WLog_ERR(TAG,
"section name must be terminated with nullptr!");
480 return ERROR_INVALID_DATA;
483 wStream* s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_OPEN_POOL,
484 20 + (openPool->sectionNameLength * 2));
488 WLog_ERR(TAG,
"gfxredir_server_single_packet_new failed!");
489 return CHANNEL_RC_NO_MEMORY;
492 Stream_Write_UINT64(s, openPool->poolId);
493 Stream_Write_UINT64(s, openPool->poolSize);
494 Stream_Write_UINT32(s, openPool->sectionNameLength);
495 Stream_Write(s, openPool->sectionName, (2ULL * openPool->sectionNameLength));
496 return gfxredir_server_packet_send(context, s);
504static UINT gfxredir_send_close_pool(GfxRedirServerContext* context,
507 WINPR_ASSERT(context);
508 WINPR_ASSERT(closePool);
510 if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
512 WLog_ERR(TAG,
"close_pool is called with invalid version!");
513 return ERROR_INTERNAL_ERROR;
516 wStream* s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_CLOSE_POOL, 8);
520 WLog_ERR(TAG,
"gfxredir_server_single_packet_new failed!");
521 return CHANNEL_RC_NO_MEMORY;
524 Stream_Write_UINT64(s, closePool->poolId);
525 return gfxredir_server_packet_send(context, s);
533static UINT gfxredir_send_create_buffer(GfxRedirServerContext* context,
536 WINPR_ASSERT(context);
537 WINPR_ASSERT(createBuffer);
539 if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
541 WLog_ERR(TAG,
"create_buffer is called with invalid version!");
542 return ERROR_INTERNAL_ERROR;
545 wStream* s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_CREATE_BUFFER, 40);
549 WLog_ERR(TAG,
"gfxredir_server_single_packet_new failed!");
550 return CHANNEL_RC_NO_MEMORY;
553 Stream_Write_UINT64(s, createBuffer->poolId);
554 Stream_Write_UINT64(s, createBuffer->bufferId);
555 Stream_Write_UINT64(s, createBuffer->offset);
556 Stream_Write_UINT32(s, createBuffer->stride);
557 Stream_Write_UINT32(s, createBuffer->width);
558 Stream_Write_UINT32(s, createBuffer->height);
559 Stream_Write_UINT32(s, createBuffer->format);
560 return gfxredir_server_packet_send(context, s);
568static UINT gfxredir_send_destroy_buffer(GfxRedirServerContext* context,
571 WINPR_ASSERT(context);
572 WINPR_ASSERT(destroyBuffer);
574 if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
576 WLog_ERR(TAG,
"destroy_buffer is called with invalid version!");
577 return ERROR_INTERNAL_ERROR;
580 wStream* s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_DESTROY_BUFFER, 8);
584 WLog_ERR(TAG,
"gfxredir_server_single_packet_new failed!");
585 return CHANNEL_RC_NO_MEMORY;
588 Stream_Write_UINT64(s, destroyBuffer->bufferId);
589 return gfxredir_server_packet_send(context, s);
597static UINT gfxredir_send_present_buffer(GfxRedirServerContext* context,
602 WINPR_ASSERT(context);
603 WINPR_ASSERT(presentBuffer);
605 if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
607 WLog_ERR(TAG,
"present_buffer is called with invalid version!");
608 return ERROR_INTERNAL_ERROR;
611 if (presentBuffer->numOpaqueRects > GFXREDIR_MAX_OPAQUE_RECTS)
613 WLog_ERR(TAG,
"numOpaqueRects is larger than its limit!");
614 return ERROR_INVALID_DATA;
617 const size_t length =
618 64ULL + ((presentBuffer->numOpaqueRects ? presentBuffer->numOpaqueRects : 1) *
621 wStream* s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_PRESENT_BUFFER, length);
625 WLog_ERR(TAG,
"gfxredir_server_single_packet_new failed!");
626 return CHANNEL_RC_NO_MEMORY;
629 Stream_Write_UINT64(s, presentBuffer->timestamp);
630 Stream_Write_UINT64(s, presentBuffer->presentId);
631 Stream_Write_UINT64(s, presentBuffer->windowId);
632 Stream_Write_UINT64(s, presentBuffer->bufferId);
633 Stream_Write_UINT32(s, presentBuffer->orientation);
634 Stream_Write_UINT32(s, presentBuffer->targetWidth);
635 Stream_Write_UINT32(s, presentBuffer->targetHeight);
636 Stream_Write_UINT32(s, presentBuffer->dirtyRect.left);
637 Stream_Write_UINT32(s, presentBuffer->dirtyRect.top);
638 Stream_Write_UINT32(s, presentBuffer->dirtyRect.width);
639 Stream_Write_UINT32(s, presentBuffer->dirtyRect.height);
640 Stream_Write_UINT32(s, presentBuffer->numOpaqueRects);
641 if (presentBuffer->numOpaqueRects)
642 Stream_Write(s, presentBuffer->opaqueRects,
643 (presentBuffer->numOpaqueRects *
sizeof(
RECTANGLE_32)));
646 return gfxredir_server_packet_send(context, s);
654static UINT gfxredir_server_open(GfxRedirServerContext* context)
656 UINT rc = ERROR_INTERNAL_ERROR;
657 WINPR_ASSERT(context);
659 GfxRedirServerPrivate* priv = context->priv;
662 DWORD BytesReturned = 0;
663 PULONG pSessionId =
nullptr;
664 void* buffer =
nullptr;
665 priv->SessionId = WTS_CURRENT_SESSION;
667 if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
668 (LPSTR*)&pSessionId, &BytesReturned) == FALSE)
670 WLog_ERR(TAG,
"WTSQuerySessionInformationA failed!");
671 rc = ERROR_INTERNAL_ERROR;
675 priv->SessionId = (DWORD)*pSessionId;
676 WTSFreeMemory(pSessionId);
677 priv->gfxredir_channel = WTSVirtualChannelOpenEx(priv->SessionId, GFXREDIR_DVC_CHANNEL_NAME,
678 WTS_CHANNEL_OPTION_DYNAMIC);
680 if (!priv->gfxredir_channel)
682 WLog_ERR(TAG,
"WTSVirtualChannelOpenEx failed!");
688 if (!WTSVirtualChannelQuery(priv->gfxredir_channel, WTSVirtualEventHandle, &buffer,
690 (BytesReturned !=
sizeof(HANDLE)))
693 "WTSVirtualChannelQuery failed "
694 "or invalid returned size(%" PRIu32
")",
698 WTSFreeMemory(buffer);
700 rc = ERROR_INTERNAL_ERROR;
704 priv->channelEvent = *(HANDLE*)buffer;
705 WTSFreeMemory(buffer);
707 if (priv->thread ==
nullptr)
709 if (!(priv->stopEvent = CreateEvent(
nullptr, TRUE, FALSE,
nullptr)))
711 WLog_ERR(TAG,
"CreateEvent failed!");
715 if (!(priv->thread = CreateThread(
nullptr, 0, gfxredir_server_thread_func, (
void*)context,
718 WLog_ERR(TAG,
"CreateEvent failed!");
719 (void)CloseHandle(priv->stopEvent);
720 priv->stopEvent =
nullptr;
725 return CHANNEL_RC_OK;
727 WTSVirtualChannelClose(priv->gfxredir_channel);
728 priv->gfxredir_channel =
nullptr;
729 priv->channelEvent =
nullptr;
738static UINT gfxredir_server_close(GfxRedirServerContext* context)
740 UINT error = CHANNEL_RC_OK;
741 WINPR_ASSERT(context);
743 GfxRedirServerPrivate* priv = context->priv;
748 (void)SetEvent(priv->stopEvent);
750 if (WaitForSingleObject(priv->thread, INFINITE) == WAIT_FAILED)
752 error = GetLastError();
753 WLog_ERR(TAG,
"WaitForSingleObject failed with error %" PRIu32
"", error);
757 (void)CloseHandle(priv->thread);
758 (void)CloseHandle(priv->stopEvent);
759 priv->thread =
nullptr;
760 priv->stopEvent =
nullptr;
763 if (priv->gfxredir_channel)
765 WTSVirtualChannelClose(priv->gfxredir_channel);
766 priv->gfxredir_channel =
nullptr;
772GfxRedirServerContext* gfxredir_server_context_new(HANDLE vcm)
774 GfxRedirServerContext* context =
775 (GfxRedirServerContext*)calloc(1,
sizeof(GfxRedirServerContext));
779 WLog_ERR(TAG,
"gfxredir_server_context_new(): calloc GfxRedirServerContext failed!");
783 GfxRedirServerPrivate* priv = context->priv =
784 (GfxRedirServerPrivate*)calloc(1,
sizeof(GfxRedirServerPrivate));
788 WLog_ERR(TAG,
"gfxredir_server_context_new(): calloc GfxRedirServerPrivate failed!");
792 priv->input_stream = Stream_New(
nullptr, 4);
794 if (!priv->input_stream)
796 WLog_ERR(TAG,
"Stream_New failed!");
801 context->Open = gfxredir_server_open;
802 context->Close = gfxredir_server_close;
803 context->Error = gfxredir_send_error;
804 context->GraphicsRedirectionCapsConfirm = gfxredir_send_caps_confirm;
805 context->OpenPool = gfxredir_send_open_pool;
806 context->ClosePool = gfxredir_send_close_pool;
807 context->CreateBuffer = gfxredir_send_create_buffer;
808 context->DestroyBuffer = gfxredir_send_destroy_buffer;
809 context->PresentBuffer = gfxredir_send_present_buffer;
810 context->confirmedCapsVersion = GFXREDIR_CAPS_VERSION1;
811 priv->isReady = FALSE;
814 gfxredir_server_context_free(context);
818void gfxredir_server_context_free(GfxRedirServerContext* context)
823 gfxredir_server_close(context);
827 Stream_Free(context->priv->input_stream, TRUE);