20#include <freerdp/config.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/disp.h>
37#include "../disp_common.h"
39#define TAG CHANNELS_TAG("rdpedisp.server")
47static wStream* disp_server_single_packet_new(UINT32 type, UINT32 length)
51 wStream* s = Stream_New(
nullptr, DISPLAY_CONTROL_HEADER_LENGTH + length);
55 WLog_ERR(TAG,
"Stream_New failed!");
60 header.length = DISPLAY_CONTROL_HEADER_LENGTH + length;
62 if ((error = disp_write_header(s, &header)))
64 WLog_ERR(TAG,
"Failed to write header with error %" PRIu32
"!", error);
76 if (monitor->PhysicalWidth < DISPLAY_CONTROL_MIN_PHYSICAL_MONITOR_WIDTH ||
77 monitor->PhysicalWidth > DISPLAY_CONTROL_MAX_PHYSICAL_MONITOR_WIDTH ||
78 monitor->PhysicalHeight < DISPLAY_CONTROL_MIN_PHYSICAL_MONITOR_HEIGHT ||
79 monitor->PhysicalHeight > DISPLAY_CONTROL_MAX_PHYSICAL_MONITOR_HEIGHT)
81 if (monitor->PhysicalWidth != 0 || monitor->PhysicalHeight != 0)
84 "Sanitizing invalid physical monitor size. Old physical monitor size: [%" PRIu32
86 monitor->PhysicalWidth, monitor->PhysicalHeight);
88 monitor->PhysicalWidth = monitor->PhysicalHeight = 0;
94 WINPR_ASSERT(monitor);
96 if (monitor->Width < DISPLAY_CONTROL_MIN_MONITOR_WIDTH ||
97 monitor->Width > DISPLAY_CONTROL_MAX_MONITOR_WIDTH)
99 WLog_WARN(TAG,
"Received invalid value for monitor->Width: %" PRIu32
"", monitor->Width);
103 if (monitor->Height < DISPLAY_CONTROL_MIN_MONITOR_HEIGHT ||
104 monitor->Height > DISPLAY_CONTROL_MAX_MONITOR_HEIGHT)
106 WLog_WARN(TAG,
"Received invalid value for monitor->Height: %" PRIu32
"", monitor->Height);
110 switch (monitor->Orientation)
112 case ORIENTATION_LANDSCAPE:
113 case ORIENTATION_PORTRAIT:
114 case ORIENTATION_LANDSCAPE_FLIPPED:
115 case ORIENTATION_PORTRAIT_FLIPPED:
119 WLog_WARN(TAG,
"Received incorrect value for monitor->Orientation: %" PRIu32
"",
120 monitor->Orientation);
127static UINT disp_recv_display_control_monitor_layout_pdu(
wStream* s, DispServerContext* context)
129 UINT32 error = CHANNEL_RC_OK;
133 WINPR_ASSERT(context);
135 if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
136 return ERROR_INVALID_DATA;
138 Stream_Read_UINT32(s, pdu.MonitorLayoutSize);
140 if (pdu.MonitorLayoutSize != DISPLAY_CONTROL_MONITOR_LAYOUT_SIZE)
142 WLog_ERR(TAG,
"MonitorLayoutSize is set to %" PRIu32
". expected %d", pdu.MonitorLayoutSize,
143 DISPLAY_CONTROL_MONITOR_LAYOUT_SIZE);
144 return ERROR_INVALID_DATA;
147 Stream_Read_UINT32(s, pdu.NumMonitors);
149 if (pdu.NumMonitors > context->MaxNumMonitors)
151 WLog_ERR(TAG,
"NumMonitors (%" PRIu32
")> server MaxNumMonitors (%" PRIu32
")",
152 pdu.NumMonitors, context->MaxNumMonitors);
153 return ERROR_INVALID_DATA;
156 if (!Stream_CheckAndLogRequiredLengthOfSize(TAG, s, pdu.NumMonitors,
157 DISPLAY_CONTROL_MONITOR_LAYOUT_SIZE))
158 return ERROR_INVALID_DATA;
165 WLog_ERR(TAG,
"disp_recv_display_control_monitor_layout_pdu(): calloc failed!");
166 return CHANNEL_RC_NO_MEMORY;
169 WLog_DBG(TAG,
"disp_recv_display_control_monitor_layout_pdu: NumMonitors=%" PRIu32
"",
172 for (UINT32 index = 0; index < pdu.NumMonitors; index++)
176 Stream_Read_UINT32(s, monitor->Flags);
177 Stream_Read_INT32(s, monitor->Left);
178 Stream_Read_INT32(s, monitor->Top);
179 Stream_Read_UINT32(s, monitor->Width);
180 Stream_Read_UINT32(s, monitor->Height);
181 Stream_Read_UINT32(s, monitor->PhysicalWidth);
182 Stream_Read_UINT32(s, monitor->PhysicalHeight);
183 Stream_Read_UINT32(s, monitor->Orientation);
184 Stream_Read_UINT32(s, monitor->DesktopScaleFactor);
185 Stream_Read_UINT32(s, monitor->DeviceScaleFactor);
187 disp_server_sanitize_monitor_layout(monitor);
189 "\t%" PRIu32
" : Flags: 0x%08" PRIX32
" Left/Top: (%" PRId32
",%" PRId32
190 ") W/H=%" PRIu32
"x%" PRIu32
")",
191 index, monitor->Flags, monitor->Left, monitor->Top, monitor->Width,
194 "\t PhysicalWidth: %" PRIu32
" PhysicalHeight: %" PRIu32
" Orientation: %" PRIu32
196 monitor->PhysicalWidth, monitor->PhysicalHeight, monitor->Orientation);
198 if (!disp_server_is_monitor_layout_valid(monitor))
200 error = ERROR_INVALID_DATA;
206 IFCALLRET(context->DispMonitorLayout, error, context, &pdu);
213static UINT disp_server_receive_pdu(DispServerContext* context,
wStream* s)
215 UINT error = CHANNEL_RC_OK;
221 WINPR_ASSERT(context);
223 beg = Stream_GetPosition(s);
225 if ((error = disp_read_header(s, &header)))
227 WLog_ERR(TAG,
"disp_read_header failed with error %" PRIu32
"!", error);
233 case DISPLAY_CONTROL_PDU_TYPE_MONITOR_LAYOUT:
234 if ((error = disp_recv_display_control_monitor_layout_pdu(s, context)))
236 "disp_recv_display_control_monitor_layout_pdu "
237 "failed with error %" PRIu32
"!",
243 error = CHANNEL_RC_BAD_PROC;
244 WLog_WARN(TAG,
"Received unknown PDU type: %" PRIu32
"", header.type);
248 end = Stream_GetPosition(s);
250 if (end != (beg + header.length))
252 WLog_ERR(TAG,
"Unexpected DISP pdu end: Actual: %" PRIuz
", Expected: %" PRIuz
"", end,
253 (beg + header.length));
254 if (!Stream_SetPosition(s, (beg + header.length)))
255 return ERROR_INVALID_DATA;
261static UINT disp_server_handle_messages(DispServerContext* context)
263 DWORD BytesReturned = 0;
264 void* buffer =
nullptr;
265 UINT ret = CHANNEL_RC_OK;
266 DispServerPrivate* priv =
nullptr;
269 WINPR_ASSERT(context);
271 priv = context->priv;
274 s = priv->input_stream;
280 if (WTSVirtualChannelQuery(priv->disp_channel, WTSVirtualChannelReady, &buffer,
281 &BytesReturned) == FALSE)
283 if (GetLastError() == ERROR_NO_DATA)
284 return ERROR_NO_DATA;
286 WLog_ERR(TAG,
"WTSVirtualChannelQuery failed");
287 return ERROR_INTERNAL_ERROR;
290 priv->isReady = *((BOOL*)buffer);
291 WTSFreeMemory(buffer);
295 Stream_ResetPosition(s);
297 if (!WTSVirtualChannelRead(priv->disp_channel, 0,
nullptr, 0, &BytesReturned))
299 if (GetLastError() == ERROR_NO_DATA)
300 return ERROR_NO_DATA;
302 WLog_ERR(TAG,
"WTSVirtualChannelRead failed!");
303 return ERROR_INTERNAL_ERROR;
306 if (BytesReturned < 1)
307 return CHANNEL_RC_OK;
309 if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
311 WLog_ERR(TAG,
"Stream_EnsureRemainingCapacity failed!");
312 return CHANNEL_RC_NO_MEMORY;
315 const size_t cap = Stream_Capacity(s);
316 if (cap > UINT32_MAX)
317 return CHANNEL_RC_NO_BUFFER;
319 if (WTSVirtualChannelRead(priv->disp_channel, 0, Stream_BufferAs(s,
char), (ULONG)cap,
320 &BytesReturned) == FALSE)
322 WLog_ERR(TAG,
"WTSVirtualChannelRead failed!");
323 return ERROR_INTERNAL_ERROR;
326 if (!Stream_SetLength(s, BytesReturned))
327 return ERROR_INTERNAL_ERROR;
329 Stream_ResetPosition(s);
331 while (Stream_GetPosition(s) < Stream_Length(s))
333 if ((ret = disp_server_receive_pdu(context, s)))
336 "disp_server_receive_pdu "
337 "failed with error %" PRIu32
"!",
346static DWORD WINAPI disp_server_thread_func(LPVOID arg)
348 DispServerContext* context = (DispServerContext*)arg;
349 DispServerPrivate* priv =
nullptr;
352 HANDLE events[8] = WINPR_C_ARRAY_INIT;
353 UINT error = CHANNEL_RC_OK;
355 WINPR_ASSERT(context);
357 priv = context->priv;
360 events[nCount++] = priv->stopEvent;
361 events[nCount++] = priv->channelEvent;
366 status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
368 if (status == WAIT_FAILED)
370 error = GetLastError();
371 WLog_ERR(TAG,
"WaitForMultipleObjects failed with error %" PRIu32
"", error);
376 if (status == WAIT_OBJECT_0)
379 if ((error = disp_server_handle_messages(context)))
381 WLog_ERR(TAG,
"disp_server_handle_messages failed with error %" PRIu32
"", error);
395static UINT disp_server_open(DispServerContext* context)
397 UINT rc = ERROR_INTERNAL_ERROR;
398 DispServerPrivate* priv =
nullptr;
399 DWORD BytesReturned = 0;
400 PULONG pSessionId =
nullptr;
401 void* buffer =
nullptr;
402 UINT32 channelId = 0;
405 WINPR_ASSERT(context);
407 priv = context->priv;
410 priv->SessionId = WTS_CURRENT_SESSION;
412 if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
413 (LPSTR*)&pSessionId, &BytesReturned) == FALSE)
415 WLog_ERR(TAG,
"WTSQuerySessionInformationA failed!");
416 rc = ERROR_INTERNAL_ERROR;
420 priv->SessionId = (DWORD)*pSessionId;
421 WTSFreeMemory(pSessionId);
423 WTSVirtualChannelOpenEx(priv->SessionId, DISP_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC);
425 if (!priv->disp_channel)
427 WLog_ERR(TAG,
"WTSVirtualChannelOpenEx failed!");
432 channelId = WTSChannelGetIdByHandle(priv->disp_channel);
434 IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
437 WLog_ERR(TAG,
"context->ChannelIdAssigned failed!");
438 rc = ERROR_INTERNAL_ERROR;
443 if (!WTSVirtualChannelQuery(priv->disp_channel, WTSVirtualEventHandle, &buffer,
445 (BytesReturned !=
sizeof(HANDLE)))
448 "WTSVirtualChannelQuery failed "
449 "or invalid returned size(%" PRIu32
")",
453 WTSFreeMemory(buffer);
455 rc = ERROR_INTERNAL_ERROR;
459 priv->channelEvent = *(HANDLE*)buffer;
460 WTSFreeMemory(buffer);
462 if (priv->thread ==
nullptr)
464 if (!(priv->stopEvent = CreateEvent(
nullptr, TRUE, FALSE,
nullptr)))
466 WLog_ERR(TAG,
"CreateEvent failed!");
467 rc = ERROR_INTERNAL_ERROR;
472 CreateThread(
nullptr, 0, disp_server_thread_func, (
void*)context, 0,
nullptr)))
474 WLog_ERR(TAG,
"CreateEvent failed!");
475 (void)CloseHandle(priv->stopEvent);
476 priv->stopEvent =
nullptr;
477 rc = ERROR_INTERNAL_ERROR;
482 return CHANNEL_RC_OK;
484 (void)WTSVirtualChannelClose(priv->disp_channel);
485 priv->disp_channel =
nullptr;
486 priv->channelEvent =
nullptr;
490static UINT disp_server_packet_send(DispServerContext* context,
wStream* s)
495 WINPR_ASSERT(context);
498 const size_t pos = Stream_GetPosition(s);
500 WINPR_ASSERT(pos <= UINT32_MAX);
501 if (!WTSVirtualChannelWrite(context->priv->disp_channel, Stream_BufferAs(s,
char), (UINT32)pos,
504 WLog_ERR(TAG,
"WTSVirtualChannelWrite failed!");
505 ret = ERROR_INTERNAL_ERROR;
509 if (written < Stream_GetPosition(s))
511 WLog_WARN(TAG,
"Unexpected bytes written: %" PRIu32
"/%" PRIuz
"", written,
512 Stream_GetPosition(s));
517 Stream_Free(s, TRUE);
526static UINT disp_server_send_caps_pdu(DispServerContext* context)
530 WINPR_ASSERT(context);
532 s = disp_server_single_packet_new(DISPLAY_CONTROL_PDU_TYPE_CAPS, 12);
536 WLog_ERR(TAG,
"disp_server_single_packet_new failed!");
537 return CHANNEL_RC_NO_MEMORY;
540 Stream_Write_UINT32(s, context->MaxNumMonitors);
541 Stream_Write_UINT32(s, context->MaxMonitorAreaFactorA);
542 Stream_Write_UINT32(s, context->MaxMonitorAreaFactorB);
543 return disp_server_packet_send(context, s);
551static UINT disp_server_close(DispServerContext* context)
553 UINT error = CHANNEL_RC_OK;
554 DispServerPrivate* priv =
nullptr;
556 WINPR_ASSERT(context);
558 priv = context->priv;
563 (void)SetEvent(priv->stopEvent);
565 if (WaitForSingleObject(priv->thread, INFINITE) == WAIT_FAILED)
567 error = GetLastError();
568 WLog_ERR(TAG,
"WaitForSingleObject failed with error %" PRIu32
"", error);
572 (void)CloseHandle(priv->thread);
573 (void)CloseHandle(priv->stopEvent);
574 priv->thread =
nullptr;
575 priv->stopEvent =
nullptr;
578 if (priv->disp_channel)
580 (void)WTSVirtualChannelClose(priv->disp_channel);
581 priv->disp_channel =
nullptr;
587DispServerContext* disp_server_context_new(HANDLE vcm)
589 DispServerContext* context =
nullptr;
590 DispServerPrivate* priv =
nullptr;
591 context = (DispServerContext*)calloc(1,
sizeof(DispServerContext));
595 WLog_ERR(TAG,
"disp_server_context_new(): calloc DispServerContext failed!");
599 priv = context->priv = (DispServerPrivate*)calloc(1,
sizeof(DispServerPrivate));
603 WLog_ERR(TAG,
"disp_server_context_new(): calloc DispServerPrivate failed!");
607 priv->input_stream = Stream_New(
nullptr, 4);
609 if (!priv->input_stream)
611 WLog_ERR(TAG,
"Stream_New failed!");
616 context->Open = disp_server_open;
617 context->Close = disp_server_close;
618 context->DisplayControlCaps = disp_server_send_caps_pdu;
619 priv->isReady = FALSE;
622 WINPR_PRAGMA_DIAG_PUSH
623 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
624 disp_server_context_free(context);
625 WINPR_PRAGMA_DIAG_POP
629void disp_server_context_free(DispServerContext* context)
636 disp_server_close(context);
637 Stream_Free(context->priv->input_stream, TRUE);