20 #include <freerdp/config.h>
22 #include "gfxredir_main.h"
27 #include <winpr/crt.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")
46 static 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);
69 static 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);
102 static 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);
128 static 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: %d, Expected: %" PRIu32
"", end,
181 (beg + header.length));
182 Stream_SetPosition(s, (beg + header.length));
193 static UINT gfxredir_server_handle_messages(GfxRedirServerContext* context)
195 UINT ret = CHANNEL_RC_OK;
197 WINPR_ASSERT(context);
199 GfxRedirServerPrivate* priv = context->priv;
202 wStream* s = priv->input_stream;
208 DWORD BytesReturned = 0;
209 if (WTSVirtualChannelQuery(priv->gfxredir_channel, WTSVirtualChannelReady, &buffer,
210 &BytesReturned) == FALSE)
212 if (GetLastError() == ERROR_NO_DATA)
213 return ERROR_NO_DATA;
215 WLog_ERR(TAG,
"WTSVirtualChannelQuery failed");
216 return ERROR_INTERNAL_ERROR;
219 priv->isReady = *((BOOL*)buffer);
220 WTSFreeMemory(buffer);
226 Stream_SetPosition(s, 0);
228 DWORD BytesReturned = 0;
229 if (!WTSVirtualChannelRead(priv->gfxredir_channel, 0, NULL, 0, &BytesReturned))
231 if (GetLastError() == ERROR_NO_DATA)
232 return ERROR_NO_DATA;
234 WLog_ERR(TAG,
"WTSVirtualChannelRead failed!");
235 return ERROR_INTERNAL_ERROR;
238 if (BytesReturned < 1)
239 return CHANNEL_RC_OK;
241 if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
243 WLog_ERR(TAG,
"Stream_EnsureRemainingCapacity failed!");
244 return CHANNEL_RC_NO_MEMORY;
247 const ULONG cap = WINPR_ASSERTING_INT_CAST(ULONG, Stream_Capacity(s));
248 if (WTSVirtualChannelRead(priv->gfxredir_channel, 0, (PCHAR)Stream_Buffer(s), cap,
249 &BytesReturned) == FALSE)
251 WLog_ERR(TAG,
"WTSVirtualChannelRead failed!");
252 return ERROR_INTERNAL_ERROR;
255 Stream_SetLength(s, BytesReturned);
256 Stream_SetPosition(s, 0);
258 while (Stream_GetPosition(s) < Stream_Length(s))
260 if ((ret = gfxredir_server_receive_pdu(context, s)))
263 "gfxredir_server_receive_pdu "
264 "failed with error %" PRIu32
"!",
279 static DWORD WINAPI gfxredir_server_thread_func(LPVOID arg)
281 GfxRedirServerContext* context = (GfxRedirServerContext*)arg;
282 WINPR_ASSERT(context);
284 GfxRedirServerPrivate* priv = context->priv;
287 HANDLE events[8] = { 0 };
288 UINT error = CHANNEL_RC_OK;
290 events[nCount++] = priv->stopEvent;
291 events[nCount++] = priv->channelEvent;
295 const DWORD status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
297 if (status == WAIT_FAILED)
299 error = GetLastError();
300 WLog_ERR(TAG,
"WaitForMultipleObjects failed with error %" PRIu32
"", error);
305 if (status == WAIT_OBJECT_0)
308 if ((error = gfxredir_server_handle_messages(context)))
310 WLog_ERR(TAG,
"gfxredir_server_handle_messages failed with error %" PRIu32
"", error);
330 static wStream* gfxredir_server_single_packet_new(UINT32 cmdId,
size_t length)
333 wStream* s = Stream_New(NULL, GFXREDIR_HEADER_SIZE + length);
337 WLog_ERR(TAG,
"Stream_New failed!");
341 header.cmdId = cmdId;
342 header.length = WINPR_ASSERTING_INT_CAST(UINT32, GFXREDIR_HEADER_SIZE + length);
344 const UINT error = gfxredir_write_header(s, &header);
347 WLog_ERR(TAG,
"Failed to write header with error %" PRIu32
"!", error);
353 Stream_Free(s, TRUE);
362 static UINT gfxredir_server_packet_send(GfxRedirServerContext* context,
wStream* s)
364 UINT ret = ERROR_INTERNAL_ERROR;
367 WINPR_ASSERT(context);
369 const ULONG cap = WINPR_ASSERTING_INT_CAST(ULONG, Stream_GetPosition(s));
370 if (!WTSVirtualChannelWrite(context->priv->gfxredir_channel, (PCHAR)Stream_Buffer(s), cap,
373 WLog_ERR(TAG,
"WTSVirtualChannelWrite failed!");
374 ret = ERROR_INTERNAL_ERROR;
378 if (written < Stream_GetPosition(s))
380 WLog_WARN(TAG,
"Unexpected bytes written: %" PRIu32
"/%" PRIuz
"", written,
381 Stream_GetPosition(s));
386 Stream_Free(s, TRUE);
395 static UINT gfxredir_send_error(GfxRedirServerContext* context,
const GFXREDIR_ERROR_PDU* error)
397 WINPR_ASSERT(context);
400 wStream* s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_ERROR, 4);
404 WLog_ERR(TAG,
"gfxredir_server_single_packet_new failed!");
405 return CHANNEL_RC_NO_MEMORY;
408 Stream_Write_UINT32(s, error->errorCode);
409 return gfxredir_server_packet_send(context, s);
417 static UINT gfxredir_send_caps_confirm(GfxRedirServerContext* context,
420 WINPR_ASSERT(context);
421 WINPR_ASSERT(graphicsCapsConfirm);
423 if (graphicsCapsConfirm->length < GFXREDIR_CAPS_HEADER_SIZE)
425 WLog_ERR(TAG,
"length must be greater than header size, failed!");
426 return ERROR_INVALID_DATA;
430 gfxredir_server_single_packet_new(GFXREDIR_CMDID_CAPS_CONFIRM, graphicsCapsConfirm->length);
434 WLog_ERR(TAG,
"gfxredir_server_single_packet_new failed!");
435 return CHANNEL_RC_NO_MEMORY;
438 Stream_Write_UINT32(s, GFXREDIR_CAPS_SIGNATURE);
439 Stream_Write_UINT32(s, graphicsCapsConfirm->version);
440 Stream_Write_UINT32(s, graphicsCapsConfirm->length);
441 if (graphicsCapsConfirm->length > GFXREDIR_CAPS_HEADER_SIZE)
442 Stream_Write(s, graphicsCapsConfirm->capsData,
443 graphicsCapsConfirm->length - GFXREDIR_CAPS_HEADER_SIZE);
444 const UINT ret = gfxredir_server_packet_send(context, s);
445 if (ret == CHANNEL_RC_OK)
446 context->confirmedCapsVersion = graphicsCapsConfirm->version;
455 static UINT gfxredir_send_open_pool(GfxRedirServerContext* context,
458 WINPR_ASSERT(context);
459 WINPR_ASSERT(openPool);
461 if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
463 WLog_ERR(TAG,
"open_pool is called with invalid version!");
464 return ERROR_INTERNAL_ERROR;
467 if (openPool->sectionNameLength == 0 || openPool->sectionName == NULL)
469 WLog_ERR(TAG,
"section name must be provided!");
470 return ERROR_INVALID_DATA;
474 if (openPool->sectionName[openPool->sectionNameLength - 1] != 0)
476 WLog_ERR(TAG,
"section name must be terminated with NULL!");
477 return ERROR_INVALID_DATA;
480 wStream* s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_OPEN_POOL,
481 20 + (openPool->sectionNameLength * 2));
485 WLog_ERR(TAG,
"gfxredir_server_single_packet_new failed!");
486 return CHANNEL_RC_NO_MEMORY;
489 Stream_Write_UINT64(s, openPool->poolId);
490 Stream_Write_UINT64(s, openPool->poolSize);
491 Stream_Write_UINT32(s, openPool->sectionNameLength);
492 Stream_Write(s, openPool->sectionName, (2ULL * openPool->sectionNameLength));
493 return gfxredir_server_packet_send(context, s);
501 static UINT gfxredir_send_close_pool(GfxRedirServerContext* context,
504 WINPR_ASSERT(context);
505 WINPR_ASSERT(closePool);
507 if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
509 WLog_ERR(TAG,
"close_pool is called with invalid version!");
510 return ERROR_INTERNAL_ERROR;
513 wStream* s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_CLOSE_POOL, 8);
517 WLog_ERR(TAG,
"gfxredir_server_single_packet_new failed!");
518 return CHANNEL_RC_NO_MEMORY;
521 Stream_Write_UINT64(s, closePool->poolId);
522 return gfxredir_server_packet_send(context, s);
530 static UINT gfxredir_send_create_buffer(GfxRedirServerContext* context,
533 WINPR_ASSERT(context);
534 WINPR_ASSERT(createBuffer);
536 if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
538 WLog_ERR(TAG,
"create_buffer is called with invalid version!");
539 return ERROR_INTERNAL_ERROR;
542 wStream* s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_CREATE_BUFFER, 40);
546 WLog_ERR(TAG,
"gfxredir_server_single_packet_new failed!");
547 return CHANNEL_RC_NO_MEMORY;
550 Stream_Write_UINT64(s, createBuffer->poolId);
551 Stream_Write_UINT64(s, createBuffer->bufferId);
552 Stream_Write_UINT64(s, createBuffer->offset);
553 Stream_Write_UINT32(s, createBuffer->stride);
554 Stream_Write_UINT32(s, createBuffer->width);
555 Stream_Write_UINT32(s, createBuffer->height);
556 Stream_Write_UINT32(s, createBuffer->format);
557 return gfxredir_server_packet_send(context, s);
565 static UINT gfxredir_send_destroy_buffer(GfxRedirServerContext* context,
568 WINPR_ASSERT(context);
569 WINPR_ASSERT(destroyBuffer);
571 if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
573 WLog_ERR(TAG,
"destroy_buffer is called with invalid version!");
574 return ERROR_INTERNAL_ERROR;
577 wStream* s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_DESTROY_BUFFER, 8);
581 WLog_ERR(TAG,
"gfxredir_server_single_packet_new failed!");
582 return CHANNEL_RC_NO_MEMORY;
585 Stream_Write_UINT64(s, destroyBuffer->bufferId);
586 return gfxredir_server_packet_send(context, s);
594 static UINT gfxredir_send_present_buffer(GfxRedirServerContext* context,
599 WINPR_ASSERT(context);
600 WINPR_ASSERT(presentBuffer);
602 if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
604 WLog_ERR(TAG,
"present_buffer is called with invalid version!");
605 return ERROR_INTERNAL_ERROR;
608 if (presentBuffer->numOpaqueRects > GFXREDIR_MAX_OPAQUE_RECTS)
610 WLog_ERR(TAG,
"numOpaqueRects is larger than its limit!");
611 return ERROR_INVALID_DATA;
614 const size_t length =
615 64ULL + ((presentBuffer->numOpaqueRects ? presentBuffer->numOpaqueRects : 1) *
618 wStream* s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_PRESENT_BUFFER, length);
622 WLog_ERR(TAG,
"gfxredir_server_single_packet_new failed!");
623 return CHANNEL_RC_NO_MEMORY;
626 Stream_Write_UINT64(s, presentBuffer->timestamp);
627 Stream_Write_UINT64(s, presentBuffer->presentId);
628 Stream_Write_UINT64(s, presentBuffer->windowId);
629 Stream_Write_UINT64(s, presentBuffer->bufferId);
630 Stream_Write_UINT32(s, presentBuffer->orientation);
631 Stream_Write_UINT32(s, presentBuffer->targetWidth);
632 Stream_Write_UINT32(s, presentBuffer->targetHeight);
633 Stream_Write_UINT32(s, presentBuffer->dirtyRect.left);
634 Stream_Write_UINT32(s, presentBuffer->dirtyRect.top);
635 Stream_Write_UINT32(s, presentBuffer->dirtyRect.width);
636 Stream_Write_UINT32(s, presentBuffer->dirtyRect.height);
637 Stream_Write_UINT32(s, presentBuffer->numOpaqueRects);
638 if (presentBuffer->numOpaqueRects)
639 Stream_Write(s, presentBuffer->opaqueRects,
640 (presentBuffer->numOpaqueRects *
sizeof(
RECTANGLE_32)));
643 return gfxredir_server_packet_send(context, s);
651 static UINT gfxredir_server_open(GfxRedirServerContext* context)
653 UINT rc = ERROR_INTERNAL_ERROR;
654 WINPR_ASSERT(context);
656 GfxRedirServerPrivate* priv = context->priv;
659 DWORD BytesReturned = 0;
660 PULONG pSessionId = NULL;
662 priv->SessionId = WTS_CURRENT_SESSION;
664 if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
665 (LPSTR*)&pSessionId, &BytesReturned) == FALSE)
667 WLog_ERR(TAG,
"WTSQuerySessionInformationA failed!");
668 rc = ERROR_INTERNAL_ERROR;
672 priv->SessionId = (DWORD)*pSessionId;
673 WTSFreeMemory(pSessionId);
674 priv->gfxredir_channel = WTSVirtualChannelOpenEx(priv->SessionId, GFXREDIR_DVC_CHANNEL_NAME,
675 WTS_CHANNEL_OPTION_DYNAMIC);
677 if (!priv->gfxredir_channel)
679 WLog_ERR(TAG,
"WTSVirtualChannelOpenEx failed!");
685 if (!WTSVirtualChannelQuery(priv->gfxredir_channel, WTSVirtualEventHandle, &buffer,
687 (BytesReturned !=
sizeof(HANDLE)))
690 "WTSVirtualChannelQuery failed "
691 "or invalid returned size(%" PRIu32
")",
695 WTSFreeMemory(buffer);
697 rc = ERROR_INTERNAL_ERROR;
701 priv->channelEvent = *(HANDLE*)buffer;
702 WTSFreeMemory(buffer);
704 if (priv->thread == NULL)
706 if (!(priv->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
708 WLog_ERR(TAG,
"CreateEvent failed!");
713 CreateThread(NULL, 0, gfxredir_server_thread_func, (
void*)context, 0, NULL)))
715 WLog_ERR(TAG,
"CreateEvent failed!");
716 (void)CloseHandle(priv->stopEvent);
717 priv->stopEvent = NULL;
722 return CHANNEL_RC_OK;
724 WTSVirtualChannelClose(priv->gfxredir_channel);
725 priv->gfxredir_channel = NULL;
726 priv->channelEvent = NULL;
735 static UINT gfxredir_server_close(GfxRedirServerContext* context)
737 UINT error = CHANNEL_RC_OK;
738 WINPR_ASSERT(context);
740 GfxRedirServerPrivate* priv = context->priv;
745 (void)SetEvent(priv->stopEvent);
747 if (WaitForSingleObject(priv->thread, INFINITE) == WAIT_FAILED)
749 error = GetLastError();
750 WLog_ERR(TAG,
"WaitForSingleObject failed with error %" PRIu32
"", error);
754 (void)CloseHandle(priv->thread);
755 (void)CloseHandle(priv->stopEvent);
757 priv->stopEvent = NULL;
760 if (priv->gfxredir_channel)
762 WTSVirtualChannelClose(priv->gfxredir_channel);
763 priv->gfxredir_channel = NULL;
769 GfxRedirServerContext* gfxredir_server_context_new(HANDLE vcm)
771 GfxRedirServerContext* context =
772 (GfxRedirServerContext*)calloc(1,
sizeof(GfxRedirServerContext));
776 WLog_ERR(TAG,
"gfxredir_server_context_new(): calloc GfxRedirServerContext failed!");
780 GfxRedirServerPrivate* priv = context->priv =
781 (GfxRedirServerPrivate*)calloc(1,
sizeof(GfxRedirServerPrivate));
785 WLog_ERR(TAG,
"gfxredir_server_context_new(): calloc GfxRedirServerPrivate failed!");
789 priv->input_stream = Stream_New(NULL, 4);
791 if (!priv->input_stream)
793 WLog_ERR(TAG,
"Stream_New failed!");
798 context->Open = gfxredir_server_open;
799 context->Close = gfxredir_server_close;
800 context->Error = gfxredir_send_error;
801 context->GraphicsRedirectionCapsConfirm = gfxredir_send_caps_confirm;
802 context->OpenPool = gfxredir_send_open_pool;
803 context->ClosePool = gfxredir_send_close_pool;
804 context->CreateBuffer = gfxredir_send_create_buffer;
805 context->DestroyBuffer = gfxredir_send_destroy_buffer;
806 context->PresentBuffer = gfxredir_send_present_buffer;
807 context->confirmedCapsVersion = GFXREDIR_CAPS_VERSION1;
808 priv->isReady = FALSE;
811 gfxredir_server_context_free(context);
815 void gfxredir_server_context_free(GfxRedirServerContext* context)
820 gfxredir_server_close(context);
824 Stream_Free(context->priv->input_stream, TRUE);