20 #include <freerdp/config.h>
22 #include "gfxredir_main.h"
27 #include <winpr/crt.h>
28 #include <winpr/synch.h>
29 #include <winpr/thread.h>
30 #include <winpr/stream.h>
31 #include <winpr/sysinfo.h>
32 #include <freerdp/channels/wtsvc.h>
33 #include <freerdp/channels/log.h>
35 #include <freerdp/server/gfxredir.h>
36 #include "../gfxredir_common.h"
38 #define TAG CHANNELS_TAG("gfxredir.server")
45 static UINT gfxredir_recv_legacy_caps_pdu(
wStream* s, GfxRedirServerContext* context)
47 UINT32 error = CHANNEL_RC_OK;
50 if (!Stream_CheckAndLogRequiredLength(TAG, s, 2))
51 return ERROR_INVALID_DATA;
53 Stream_Read_UINT16(s, pdu.version);
56 IFCALLRET(context->GraphicsRedirectionLegacyCaps, error, context, &pdu);
66 static UINT gfxredir_recv_caps_advertise_pdu(
wStream* s, UINT32 length,
67 GfxRedirServerContext* context)
69 UINT32 error = CHANNEL_RC_OK;
72 if (!Stream_CheckAndLogRequiredLength(TAG, s, length))
73 return ERROR_INVALID_DATA;
76 Stream_GetPointer(s, pdu.caps);
77 Stream_Seek(s, length);
79 if (!context->GraphicsRedirectionCapsAdvertise)
81 WLog_ERR(TAG,
"server does not support CapsAdvertise PDU!");
82 return ERROR_NOT_SUPPORTED;
86 IFCALLRET(context->GraphicsRedirectionCapsAdvertise, error, context, &pdu);
97 static UINT gfxredir_recv_present_buffer_ack_pdu(
wStream* s, GfxRedirServerContext* context)
99 UINT32 error = CHANNEL_RC_OK;
102 if (!Stream_CheckAndLogRequiredLength(TAG, s, 16))
103 return ERROR_INVALID_DATA;
105 Stream_Read_UINT64(s, pdu.windowId);
106 Stream_Read_UINT64(s, pdu.presentId);
110 IFCALLRET(context->PresentBufferAck, error, context, &pdu);
121 static UINT gfxredir_server_receive_pdu(GfxRedirServerContext* context,
wStream* s)
123 UINT error = CHANNEL_RC_OK;
126 beg = Stream_GetPosition(s);
128 if ((error = gfxredir_read_header(s, &header)))
130 WLog_ERR(TAG,
"gfxredir_read_header failed with error %" PRIu32
"!", error);
134 switch (header.cmdId)
136 case GFXREDIR_CMDID_LEGACY_CAPS:
137 if ((error = gfxredir_recv_legacy_caps_pdu(s, context)))
139 "gfxredir_recv_legacy_caps_pdu "
140 "failed with error %" PRIu32
"!",
145 case GFXREDIR_CMDID_CAPS_ADVERTISE:
146 if ((error = gfxredir_recv_caps_advertise_pdu(s, header.length - 8, context)))
148 "gfxredir_recv_caps_advertise "
149 "failed with error %" PRIu32
"!",
153 case GFXREDIR_CMDID_PRESENT_BUFFER_ACK:
154 if ((error = gfxredir_recv_present_buffer_ack_pdu(s, context)))
156 "gfxredir_recv_present_buffer_ask_pdu "
157 "failed with error %" PRIu32
"!",
162 error = CHANNEL_RC_BAD_PROC;
163 WLog_WARN(TAG,
"Received unknown PDU type: %" PRIu32
"", header.cmdId);
167 end = Stream_GetPosition(s);
169 if (end != (beg + header.length))
171 WLog_ERR(TAG,
"Unexpected GFXREDIR pdu end: Actual: %d, Expected: %" PRIu32
"", end,
172 (beg + header.length));
173 Stream_SetPosition(s, (beg + header.length));
184 static UINT gfxredir_server_handle_messages(GfxRedirServerContext* context)
188 UINT ret = CHANNEL_RC_OK;
189 GfxRedirServerPrivate* priv = context->priv;
190 wStream* s = priv->input_stream;
195 if (WTSVirtualChannelQuery(priv->gfxredir_channel, WTSVirtualChannelReady, &buffer,
196 &BytesReturned) == FALSE)
198 if (GetLastError() == ERROR_NO_DATA)
199 return ERROR_NO_DATA;
201 WLog_ERR(TAG,
"WTSVirtualChannelQuery failed");
202 return ERROR_INTERNAL_ERROR;
205 priv->isReady = *((BOOL*)buffer);
206 WTSFreeMemory(buffer);
212 Stream_SetPosition(s, 0);
214 if (!WTSVirtualChannelRead(priv->gfxredir_channel, 0, NULL, 0, &BytesReturned))
216 if (GetLastError() == ERROR_NO_DATA)
217 return ERROR_NO_DATA;
219 WLog_ERR(TAG,
"WTSVirtualChannelRead failed!");
220 return ERROR_INTERNAL_ERROR;
223 if (BytesReturned < 1)
224 return CHANNEL_RC_OK;
226 if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
228 WLog_ERR(TAG,
"Stream_EnsureRemainingCapacity failed!");
229 return CHANNEL_RC_NO_MEMORY;
232 if (WTSVirtualChannelRead(priv->gfxredir_channel, 0, (PCHAR)Stream_Buffer(s),
233 Stream_Capacity(s), &BytesReturned) == FALSE)
235 WLog_ERR(TAG,
"WTSVirtualChannelRead failed!");
236 return ERROR_INTERNAL_ERROR;
239 Stream_SetLength(s, BytesReturned);
240 Stream_SetPosition(s, 0);
242 while (Stream_GetPosition(s) < Stream_Length(s))
244 if ((ret = gfxredir_server_receive_pdu(context, s)))
247 "gfxredir_server_receive_pdu "
248 "failed with error %" PRIu32
"!",
263 static DWORD WINAPI gfxredir_server_thread_func(LPVOID arg)
265 GfxRedirServerContext* context = (GfxRedirServerContext*)arg;
266 GfxRedirServerPrivate* priv = context->priv;
270 UINT error = CHANNEL_RC_OK;
272 events[nCount++] = priv->stopEvent;
273 events[nCount++] = priv->channelEvent;
277 status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
279 if (status == WAIT_FAILED)
281 error = GetLastError();
282 WLog_ERR(TAG,
"WaitForMultipleObjects failed with error %" PRIu32
"", error);
287 if (status == WAIT_OBJECT_0)
290 if ((error = gfxredir_server_handle_messages(context)))
292 WLog_ERR(TAG,
"gfxredir_server_handle_messages failed with error %" PRIu32
"", error);
312 static wStream* gfxredir_server_single_packet_new(UINT32 cmdId, UINT32 length)
316 wStream* s = Stream_New(NULL, GFXREDIR_HEADER_SIZE + length);
320 WLog_ERR(TAG,
"Stream_New failed!");
324 header.cmdId = cmdId;
325 header.length = GFXREDIR_HEADER_SIZE + length;
327 if ((error = gfxredir_write_header(s, &header)))
329 WLog_ERR(TAG,
"Failed to write header with error %" PRIu32
"!", error);
335 Stream_Free(s, TRUE);
344 static UINT gfxredir_server_packet_send(GfxRedirServerContext* context,
wStream* s)
349 if (!WTSVirtualChannelWrite(context->priv->gfxredir_channel, (PCHAR)Stream_Buffer(s),
350 Stream_GetPosition(s), &written))
352 WLog_ERR(TAG,
"WTSVirtualChannelWrite failed!");
353 ret = ERROR_INTERNAL_ERROR;
357 if (written < Stream_GetPosition(s))
359 WLog_WARN(TAG,
"Unexpected bytes written: %" PRIu32
"/%" PRIuz
"", written,
360 Stream_GetPosition(s));
365 Stream_Free(s, TRUE);
374 static UINT gfxredir_send_error(GfxRedirServerContext* context,
const GFXREDIR_ERROR_PDU* error)
376 wStream* s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_ERROR, 4);
380 WLog_ERR(TAG,
"gfxredir_server_single_packet_new failed!");
381 return CHANNEL_RC_NO_MEMORY;
384 Stream_Write_UINT32(s, error->errorCode);
385 return gfxredir_server_packet_send(context, s);
393 static UINT gfxredir_send_caps_confirm(GfxRedirServerContext* context,
399 if (graphicsCapsConfirm->length < GFXREDIR_CAPS_HEADER_SIZE)
401 WLog_ERR(TAG,
"length must be greater than header size, failed!");
402 return ERROR_INVALID_DATA;
405 s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_CAPS_CONFIRM, graphicsCapsConfirm->length);
409 WLog_ERR(TAG,
"gfxredir_server_single_packet_new failed!");
410 return CHANNEL_RC_NO_MEMORY;
413 Stream_Write_UINT32(s, GFXREDIR_CAPS_SIGNATURE);
414 Stream_Write_UINT32(s, graphicsCapsConfirm->version);
415 Stream_Write_UINT32(s, graphicsCapsConfirm->length);
416 if (graphicsCapsConfirm->length > GFXREDIR_CAPS_HEADER_SIZE)
417 Stream_Write(s, graphicsCapsConfirm->capsData,
418 graphicsCapsConfirm->length - GFXREDIR_CAPS_HEADER_SIZE);
419 ret = gfxredir_server_packet_send(context, s);
420 if (ret == CHANNEL_RC_OK)
421 context->confirmedCapsVersion = graphicsCapsConfirm->version;
430 static UINT gfxredir_send_open_pool(GfxRedirServerContext* context,
435 if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
437 WLog_ERR(TAG,
"open_pool is called with invalid version!");
438 return ERROR_INTERNAL_ERROR;
441 if (openPool->sectionNameLength == 0 || openPool->sectionName == NULL)
443 WLog_ERR(TAG,
"section name must be provided!");
444 return ERROR_INVALID_DATA;
448 if (openPool->sectionName[openPool->sectionNameLength - 1] != 0)
450 WLog_ERR(TAG,
"section name must be terminated with NULL!");
451 return ERROR_INVALID_DATA;
454 s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_OPEN_POOL,
455 20 + (openPool->sectionNameLength * 2));
459 WLog_ERR(TAG,
"gfxredir_server_single_packet_new failed!");
460 return CHANNEL_RC_NO_MEMORY;
463 Stream_Write_UINT64(s, openPool->poolId);
464 Stream_Write_UINT64(s, openPool->poolSize);
465 Stream_Write_UINT32(s, openPool->sectionNameLength);
466 Stream_Write(s, openPool->sectionName, (openPool->sectionNameLength * 2));
467 return gfxredir_server_packet_send(context, s);
475 static UINT gfxredir_send_close_pool(GfxRedirServerContext* context,
480 if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
482 WLog_ERR(TAG,
"close_pool is called with invalid version!");
483 return ERROR_INTERNAL_ERROR;
486 s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_CLOSE_POOL, 8);
490 WLog_ERR(TAG,
"gfxredir_server_single_packet_new failed!");
491 return CHANNEL_RC_NO_MEMORY;
494 Stream_Write_UINT64(s, closePool->poolId);
495 return gfxredir_server_packet_send(context, s);
503 static UINT gfxredir_send_create_buffer(GfxRedirServerContext* context,
508 if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
510 WLog_ERR(TAG,
"create_buffer is called with invalid version!");
511 return ERROR_INTERNAL_ERROR;
514 s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_CREATE_BUFFER, 40);
518 WLog_ERR(TAG,
"gfxredir_server_single_packet_new failed!");
519 return CHANNEL_RC_NO_MEMORY;
522 Stream_Write_UINT64(s, createBuffer->poolId);
523 Stream_Write_UINT64(s, createBuffer->bufferId);
524 Stream_Write_UINT64(s, createBuffer->offset);
525 Stream_Write_UINT32(s, createBuffer->stride);
526 Stream_Write_UINT32(s, createBuffer->width);
527 Stream_Write_UINT32(s, createBuffer->height);
528 Stream_Write_UINT32(s, createBuffer->format);
529 return gfxredir_server_packet_send(context, s);
537 static UINT gfxredir_send_destroy_buffer(GfxRedirServerContext* context,
542 if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
544 WLog_ERR(TAG,
"destroy_buffer is called with invalid version!");
545 return ERROR_INTERNAL_ERROR;
548 s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_DESTROY_BUFFER, 8);
552 WLog_ERR(TAG,
"gfxredir_server_single_packet_new failed!");
553 return CHANNEL_RC_NO_MEMORY;
556 Stream_Write_UINT64(s, destroyBuffer->bufferId);
557 return gfxredir_server_packet_send(context, s);
565 static UINT gfxredir_send_present_buffer(GfxRedirServerContext* context,
572 if (context->confirmedCapsVersion != GFXREDIR_CAPS_VERSION2_0)
574 WLog_ERR(TAG,
"present_buffer is called with invalid version!");
575 return ERROR_INTERNAL_ERROR;
578 if (presentBuffer->numOpaqueRects > GFXREDIR_MAX_OPAQUE_RECTS)
580 WLog_ERR(TAG,
"numOpaqueRects is larger than its limit!");
581 return ERROR_INVALID_DATA;
584 length = 64 + ((presentBuffer->numOpaqueRects ? presentBuffer->numOpaqueRects : 1) *
587 s = gfxredir_server_single_packet_new(GFXREDIR_CMDID_PRESENT_BUFFER, length);
591 WLog_ERR(TAG,
"gfxredir_server_single_packet_new failed!");
592 return CHANNEL_RC_NO_MEMORY;
595 Stream_Write_UINT64(s, presentBuffer->timestamp);
596 Stream_Write_UINT64(s, presentBuffer->presentId);
597 Stream_Write_UINT64(s, presentBuffer->windowId);
598 Stream_Write_UINT64(s, presentBuffer->bufferId);
599 Stream_Write_UINT32(s, presentBuffer->orientation);
600 Stream_Write_UINT32(s, presentBuffer->targetWidth);
601 Stream_Write_UINT32(s, presentBuffer->targetHeight);
602 Stream_Write_UINT32(s, presentBuffer->dirtyRect.left);
603 Stream_Write_UINT32(s, presentBuffer->dirtyRect.top);
604 Stream_Write_UINT32(s, presentBuffer->dirtyRect.width);
605 Stream_Write_UINT32(s, presentBuffer->dirtyRect.height);
606 Stream_Write_UINT32(s, presentBuffer->numOpaqueRects);
607 if (presentBuffer->numOpaqueRects)
608 Stream_Write(s, presentBuffer->opaqueRects,
609 (presentBuffer->numOpaqueRects *
sizeof(
RECTANGLE_32)));
612 return gfxredir_server_packet_send(context, s);
620 static UINT gfxredir_server_open(GfxRedirServerContext* context)
622 UINT rc = ERROR_INTERNAL_ERROR;
623 GfxRedirServerPrivate* priv = context->priv;
624 DWORD BytesReturned = 0;
625 PULONG pSessionId = NULL;
628 priv->SessionId = WTS_CURRENT_SESSION;
630 if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
631 (LPSTR*)&pSessionId, &BytesReturned) == FALSE)
633 WLog_ERR(TAG,
"WTSQuerySessionInformationA failed!");
634 rc = ERROR_INTERNAL_ERROR;
638 priv->SessionId = (DWORD)*pSessionId;
639 WTSFreeMemory(pSessionId);
640 priv->gfxredir_channel = (HANDLE)WTSVirtualChannelOpenEx(
641 priv->SessionId, GFXREDIR_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC);
643 if (!priv->gfxredir_channel)
645 WLog_ERR(TAG,
"WTSVirtualChannelOpenEx failed!");
651 if (!WTSVirtualChannelQuery(priv->gfxredir_channel, WTSVirtualEventHandle, &buffer,
653 (BytesReturned !=
sizeof(HANDLE)))
656 "WTSVirtualChannelQuery failed "
657 "or invalid returned size(%" PRIu32
")",
661 WTSFreeMemory(buffer);
663 rc = ERROR_INTERNAL_ERROR;
667 CopyMemory(&priv->channelEvent, buffer,
sizeof(HANDLE));
668 WTSFreeMemory(buffer);
670 if (priv->thread == NULL)
672 if (!(priv->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
674 WLog_ERR(TAG,
"CreateEvent failed!");
675 rc = ERROR_INTERNAL_ERROR;
679 CreateThread(NULL, 0, gfxredir_server_thread_func, (
void*)context, 0, NULL)))
681 WLog_ERR(TAG,
"CreateEvent failed!");
682 (void)CloseHandle(priv->stopEvent);
683 priv->stopEvent = NULL;
684 rc = ERROR_INTERNAL_ERROR;
688 return CHANNEL_RC_OK;
690 WTSVirtualChannelClose(priv->gfxredir_channel);
691 priv->gfxredir_channel = NULL;
692 priv->channelEvent = NULL;
701 static UINT gfxredir_server_close(GfxRedirServerContext* context)
703 UINT error = CHANNEL_RC_OK;
704 GfxRedirServerPrivate* priv = context->priv;
708 (void)SetEvent(priv->stopEvent);
710 if (WaitForSingleObject(priv->thread, INFINITE) == WAIT_FAILED)
712 error = GetLastError();
713 WLog_ERR(TAG,
"WaitForSingleObject failed with error %" PRIu32
"", error);
717 (void)CloseHandle(priv->thread);
718 (void)CloseHandle(priv->stopEvent);
720 priv->stopEvent = NULL;
723 if (priv->gfxredir_channel)
725 WTSVirtualChannelClose(priv->gfxredir_channel);
726 priv->gfxredir_channel = NULL;
732 GfxRedirServerContext* gfxredir_server_context_new(HANDLE vcm)
734 GfxRedirServerContext* context;
735 GfxRedirServerPrivate* priv;
736 context = (GfxRedirServerContext*)calloc(1,
sizeof(GfxRedirServerContext));
740 WLog_ERR(TAG,
"gfxredir_server_context_new(): calloc GfxRedirServerContext failed!");
744 priv = context->priv = (GfxRedirServerPrivate*)calloc(1,
sizeof(GfxRedirServerPrivate));
748 WLog_ERR(TAG,
"gfxredir_server_context_new(): calloc GfxRedirServerPrivate failed!");
752 priv->input_stream = Stream_New(NULL, 4);
754 if (!priv->input_stream)
756 WLog_ERR(TAG,
"Stream_New failed!");
761 context->Open = gfxredir_server_open;
762 context->Close = gfxredir_server_close;
763 context->Error = gfxredir_send_error;
764 context->GraphicsRedirectionCapsConfirm = gfxredir_send_caps_confirm;
765 context->OpenPool = gfxredir_send_open_pool;
766 context->ClosePool = gfxredir_send_close_pool;
767 context->CreateBuffer = gfxredir_send_create_buffer;
768 context->DestroyBuffer = gfxredir_send_destroy_buffer;
769 context->PresentBuffer = gfxredir_send_present_buffer;
770 context->confirmedCapsVersion = GFXREDIR_CAPS_VERSION1;
771 priv->isReady = FALSE;
780 void gfxredir_server_context_free(GfxRedirServerContext* context)
785 gfxredir_server_close(context);
789 Stream_Free(context->priv->input_stream, TRUE);