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* data)
215 UINT error = CHANNEL_RC_OK;
219 WINPR_ASSERT(context);
221 size_t packetLen = 0;
222 if ((error = disp_read_header(data, &header, &packetLen)))
224 WLog_ERR(TAG,
"disp_read_header failed with error %" PRIu32
"!", error);
228 wStream sbuffer = WINPR_C_ARRAY_INIT;
229 wStream* s = Stream_StaticConstInit(&sbuffer, Stream_Pointer(data), packetLen);
230 Stream_Seek(data, packetLen);
234 case DISPLAY_CONTROL_PDU_TYPE_MONITOR_LAYOUT:
235 if ((error = disp_recv_display_control_monitor_layout_pdu(s, context)))
237 "disp_recv_display_control_monitor_layout_pdu "
238 "failed with error %" PRIu32
"!",
244 error = CHANNEL_RC_BAD_PROC;
245 WLog_WARN(TAG,
"Received unknown PDU type: %" PRIu32
"", header.type);
249 const size_t end = Stream_GetPosition(s);
251 if (end != packetLen)
253 WLog_ERR(TAG,
"Unexpected DISP pdu end: Actual: %" PRIuz
", Expected: %" PRIuz
"", end,
260static UINT disp_server_handle_messages(DispServerContext* context)
262 DWORD BytesReturned = 0;
263 void* buffer =
nullptr;
264 UINT ret = CHANNEL_RC_OK;
265 DispServerPrivate* priv =
nullptr;
268 WINPR_ASSERT(context);
270 priv = context->priv;
273 s = priv->input_stream;
279 if (WTSVirtualChannelQuery(priv->disp_channel, WTSVirtualChannelReady, &buffer,
280 &BytesReturned) == FALSE)
282 if (GetLastError() == ERROR_NO_DATA)
283 return ERROR_NO_DATA;
285 WLog_ERR(TAG,
"WTSVirtualChannelQuery failed");
286 return ERROR_INTERNAL_ERROR;
289 priv->isReady = *((BOOL*)buffer);
290 WTSFreeMemory(buffer);
294 Stream_ResetPosition(s);
296 if (!WTSVirtualChannelRead(priv->disp_channel, 0,
nullptr, 0, &BytesReturned))
298 if (GetLastError() == ERROR_NO_DATA)
299 return ERROR_NO_DATA;
301 WLog_ERR(TAG,
"WTSVirtualChannelRead failed!");
302 return ERROR_INTERNAL_ERROR;
305 if (BytesReturned < 1)
306 return CHANNEL_RC_OK;
308 if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
310 WLog_ERR(TAG,
"Stream_EnsureRemainingCapacity failed!");
311 return CHANNEL_RC_NO_MEMORY;
314 const size_t cap = Stream_Capacity(s);
315 if (cap > UINT32_MAX)
316 return CHANNEL_RC_NO_BUFFER;
318 if (WTSVirtualChannelRead(priv->disp_channel, 0, Stream_BufferAs(s,
char), (ULONG)cap,
319 &BytesReturned) == FALSE)
321 WLog_ERR(TAG,
"WTSVirtualChannelRead failed!");
322 return ERROR_INTERNAL_ERROR;
325 if (!Stream_SetLength(s, BytesReturned))
326 return ERROR_INTERNAL_ERROR;
328 Stream_ResetPosition(s);
330 while (Stream_GetPosition(s) < Stream_Length(s))
332 if ((ret = disp_server_receive_pdu(context, s)))
335 "disp_server_receive_pdu "
336 "failed with error %" PRIu32
"!",
345static DWORD WINAPI disp_server_thread_func(LPVOID arg)
347 DispServerContext* context = (DispServerContext*)arg;
348 DispServerPrivate* priv =
nullptr;
351 HANDLE events[8] = WINPR_C_ARRAY_INIT;
352 UINT error = CHANNEL_RC_OK;
354 WINPR_ASSERT(context);
356 priv = context->priv;
359 events[nCount++] = priv->stopEvent;
360 events[nCount++] = priv->channelEvent;
365 status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
367 if (status == WAIT_FAILED)
369 error = GetLastError();
370 WLog_ERR(TAG,
"WaitForMultipleObjects failed with error %" PRIu32
"", error);
375 if (status == WAIT_OBJECT_0)
378 if ((error = disp_server_handle_messages(context)))
380 WLog_ERR(TAG,
"disp_server_handle_messages failed with error %" PRIu32
"", error);
394static UINT disp_server_open(DispServerContext* context)
396 UINT rc = ERROR_INTERNAL_ERROR;
397 DispServerPrivate* priv =
nullptr;
398 DWORD BytesReturned = 0;
399 PULONG pSessionId =
nullptr;
400 void* buffer =
nullptr;
401 UINT32 channelId = 0;
404 WINPR_ASSERT(context);
406 priv = context->priv;
409 priv->SessionId = WTS_CURRENT_SESSION;
411 if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
412 (LPSTR*)&pSessionId, &BytesReturned) == FALSE)
414 WLog_ERR(TAG,
"WTSQuerySessionInformationA failed!");
415 rc = ERROR_INTERNAL_ERROR;
419 priv->SessionId = (DWORD)*pSessionId;
420 WTSFreeMemory(pSessionId);
422 WTSVirtualChannelOpenEx(priv->SessionId, DISP_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC);
424 if (!priv->disp_channel)
426 WLog_ERR(TAG,
"WTSVirtualChannelOpenEx failed!");
431 channelId = WTSChannelGetIdByHandle(priv->disp_channel);
433 IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
436 WLog_ERR(TAG,
"context->ChannelIdAssigned failed!");
437 rc = ERROR_INTERNAL_ERROR;
442 if (!WTSVirtualChannelQuery(priv->disp_channel, WTSVirtualEventHandle, &buffer,
444 (BytesReturned !=
sizeof(HANDLE)))
447 "WTSVirtualChannelQuery failed "
448 "or invalid returned size(%" PRIu32
")",
452 WTSFreeMemory(buffer);
454 rc = ERROR_INTERNAL_ERROR;
458 priv->channelEvent = *(HANDLE*)buffer;
459 WTSFreeMemory(buffer);
461 if (priv->thread ==
nullptr)
463 if (!(priv->stopEvent = CreateEvent(
nullptr, TRUE, FALSE,
nullptr)))
465 WLog_ERR(TAG,
"CreateEvent failed!");
466 rc = ERROR_INTERNAL_ERROR;
471 CreateThread(
nullptr, 0, disp_server_thread_func, (
void*)context, 0,
nullptr)))
473 WLog_ERR(TAG,
"CreateEvent failed!");
474 (void)CloseHandle(priv->stopEvent);
475 priv->stopEvent =
nullptr;
476 rc = ERROR_INTERNAL_ERROR;
481 return CHANNEL_RC_OK;
483 (void)WTSVirtualChannelClose(priv->disp_channel);
484 priv->disp_channel =
nullptr;
485 priv->channelEvent =
nullptr;
489static UINT disp_server_packet_send(DispServerContext* context,
wStream* s)
494 WINPR_ASSERT(context);
497 const size_t pos = Stream_GetPosition(s);
499 WINPR_ASSERT(pos <= UINT32_MAX);
500 if (!WTSVirtualChannelWrite(context->priv->disp_channel, Stream_BufferAs(s,
char), (UINT32)pos,
503 WLog_ERR(TAG,
"WTSVirtualChannelWrite failed!");
504 ret = ERROR_INTERNAL_ERROR;
508 if (written < Stream_GetPosition(s))
510 WLog_WARN(TAG,
"Unexpected bytes written: %" PRIu32
"/%" PRIuz
"", written,
511 Stream_GetPosition(s));
516 Stream_Free(s, TRUE);
525static UINT disp_server_send_caps_pdu(DispServerContext* context)
529 WINPR_ASSERT(context);
531 s = disp_server_single_packet_new(DISPLAY_CONTROL_PDU_TYPE_CAPS, 12);
535 WLog_ERR(TAG,
"disp_server_single_packet_new failed!");
536 return CHANNEL_RC_NO_MEMORY;
539 Stream_Write_UINT32(s, context->MaxNumMonitors);
540 Stream_Write_UINT32(s, context->MaxMonitorAreaFactorA);
541 Stream_Write_UINT32(s, context->MaxMonitorAreaFactorB);
542 return disp_server_packet_send(context, s);
550static UINT disp_server_close(DispServerContext* context)
552 UINT error = CHANNEL_RC_OK;
553 DispServerPrivate* priv =
nullptr;
555 WINPR_ASSERT(context);
557 priv = context->priv;
562 (void)SetEvent(priv->stopEvent);
564 if (WaitForSingleObject(priv->thread, INFINITE) == WAIT_FAILED)
566 error = GetLastError();
567 WLog_ERR(TAG,
"WaitForSingleObject failed with error %" PRIu32
"", error);
571 (void)CloseHandle(priv->thread);
572 (void)CloseHandle(priv->stopEvent);
573 priv->thread =
nullptr;
574 priv->stopEvent =
nullptr;
577 if (priv->disp_channel)
579 (void)WTSVirtualChannelClose(priv->disp_channel);
580 priv->disp_channel =
nullptr;
586DispServerContext* disp_server_context_new(HANDLE vcm)
588 DispServerContext* context =
nullptr;
589 DispServerPrivate* priv =
nullptr;
590 context = (DispServerContext*)calloc(1,
sizeof(DispServerContext));
594 WLog_ERR(TAG,
"disp_server_context_new(): calloc DispServerContext failed!");
598 priv = context->priv = (DispServerPrivate*)calloc(1,
sizeof(DispServerPrivate));
602 WLog_ERR(TAG,
"disp_server_context_new(): calloc DispServerPrivate failed!");
606 priv->input_stream = Stream_New(
nullptr, 4);
608 if (!priv->input_stream)
610 WLog_ERR(TAG,
"Stream_New failed!");
615 context->Open = disp_server_open;
616 context->Close = disp_server_close;
617 context->DisplayControlCaps = disp_server_send_caps_pdu;
618 priv->isReady = FALSE;
621 WINPR_PRAGMA_DIAG_PUSH
622 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
623 disp_server_context_free(context);
624 WINPR_PRAGMA_DIAG_POP
628void disp_server_context_free(DispServerContext* context)
635 disp_server_close(context);
636 Stream_Free(context->priv->input_stream, TRUE);