20 #include <freerdp/config.h>
22 #include "disp_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/disp.h>
37 #include "../disp_common.h"
39 #define TAG CHANNELS_TAG("rdpedisp.server")
47 static wStream* disp_server_single_packet_new(UINT32 type, UINT32 length)
51 wStream* s = Stream_New(NULL, 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);
127 static 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 %" PRIu32
"",
143 pdu.MonitorLayoutSize, 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_UINT32(s, monitor->Left);
178 Stream_Read_UINT32(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);
213 static 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 Stream_SetPosition(s, (beg + header.length));
260 static UINT disp_server_handle_messages(DispServerContext* context)
262 DWORD BytesReturned = 0;
264 UINT ret = CHANNEL_RC_OK;
265 DispServerPrivate* priv = NULL;
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_SetPosition(s, 0);
296 if (!WTSVirtualChannelRead(priv->disp_channel, 0, NULL, 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 Stream_SetLength(s, BytesReturned);
326 Stream_SetPosition(s, 0);
328 while (Stream_GetPosition(s) < Stream_Length(s))
330 if ((ret = disp_server_receive_pdu(context, s)))
333 "disp_server_receive_pdu "
334 "failed with error %" PRIu32
"!",
343 static DWORD WINAPI disp_server_thread_func(LPVOID arg)
345 DispServerContext* context = (DispServerContext*)arg;
346 DispServerPrivate* priv = NULL;
349 HANDLE events[8] = { 0 };
350 UINT error = CHANNEL_RC_OK;
352 WINPR_ASSERT(context);
354 priv = context->priv;
357 events[nCount++] = priv->stopEvent;
358 events[nCount++] = priv->channelEvent;
363 status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
365 if (status == WAIT_FAILED)
367 error = GetLastError();
368 WLog_ERR(TAG,
"WaitForMultipleObjects failed with error %" PRIu32
"", error);
373 if (status == WAIT_OBJECT_0)
376 if ((error = disp_server_handle_messages(context)))
378 WLog_ERR(TAG,
"disp_server_handle_messages failed with error %" PRIu32
"", error);
392 static UINT disp_server_open(DispServerContext* context)
394 UINT rc = ERROR_INTERNAL_ERROR;
395 DispServerPrivate* priv = NULL;
396 DWORD BytesReturned = 0;
397 PULONG pSessionId = NULL;
399 UINT32 channelId = 0;
402 WINPR_ASSERT(context);
404 priv = context->priv;
407 priv->SessionId = WTS_CURRENT_SESSION;
409 if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
410 (LPSTR*)&pSessionId, &BytesReturned) == FALSE)
412 WLog_ERR(TAG,
"WTSQuerySessionInformationA failed!");
413 rc = ERROR_INTERNAL_ERROR;
417 priv->SessionId = (DWORD)*pSessionId;
418 WTSFreeMemory(pSessionId);
420 WTSVirtualChannelOpenEx(priv->SessionId, DISP_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC);
422 if (!priv->disp_channel)
424 WLog_ERR(TAG,
"WTSVirtualChannelOpenEx failed!");
429 channelId = WTSChannelGetIdByHandle(priv->disp_channel);
431 IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
434 WLog_ERR(TAG,
"context->ChannelIdAssigned failed!");
435 rc = ERROR_INTERNAL_ERROR;
440 if (!WTSVirtualChannelQuery(priv->disp_channel, WTSVirtualEventHandle, &buffer,
442 (BytesReturned !=
sizeof(HANDLE)))
445 "WTSVirtualChannelQuery failed "
446 "or invalid returned size(%" PRIu32
")",
450 WTSFreeMemory(buffer);
452 rc = ERROR_INTERNAL_ERROR;
456 CopyMemory(&priv->channelEvent, buffer,
sizeof(HANDLE));
457 WTSFreeMemory(buffer);
459 if (priv->thread == NULL)
461 if (!(priv->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
463 WLog_ERR(TAG,
"CreateEvent failed!");
464 rc = ERROR_INTERNAL_ERROR;
469 CreateThread(NULL, 0, disp_server_thread_func, (
void*)context, 0, NULL)))
471 WLog_ERR(TAG,
"CreateEvent failed!");
472 (void)CloseHandle(priv->stopEvent);
473 priv->stopEvent = NULL;
474 rc = ERROR_INTERNAL_ERROR;
479 return CHANNEL_RC_OK;
481 (void)WTSVirtualChannelClose(priv->disp_channel);
482 priv->disp_channel = NULL;
483 priv->channelEvent = NULL;
487 static UINT disp_server_packet_send(DispServerContext* context,
wStream* s)
492 WINPR_ASSERT(context);
495 const size_t pos = Stream_GetPosition(s);
497 WINPR_ASSERT(pos <= UINT32_MAX);
498 if (!WTSVirtualChannelWrite(context->priv->disp_channel, Stream_BufferAs(s,
char), (UINT32)pos,
501 WLog_ERR(TAG,
"WTSVirtualChannelWrite failed!");
502 ret = ERROR_INTERNAL_ERROR;
506 if (written < Stream_GetPosition(s))
508 WLog_WARN(TAG,
"Unexpected bytes written: %" PRIu32
"/%" PRIuz
"", written,
509 Stream_GetPosition(s));
514 Stream_Free(s, TRUE);
523 static UINT disp_server_send_caps_pdu(DispServerContext* context)
527 WINPR_ASSERT(context);
529 s = disp_server_single_packet_new(DISPLAY_CONTROL_PDU_TYPE_CAPS, 12);
533 WLog_ERR(TAG,
"disp_server_single_packet_new failed!");
534 return CHANNEL_RC_NO_MEMORY;
537 Stream_Write_UINT32(s, context->MaxNumMonitors);
538 Stream_Write_UINT32(s, context->MaxMonitorAreaFactorA);
539 Stream_Write_UINT32(s, context->MaxMonitorAreaFactorB);
540 return disp_server_packet_send(context, s);
548 static UINT disp_server_close(DispServerContext* context)
550 UINT error = CHANNEL_RC_OK;
551 DispServerPrivate* priv = NULL;
553 WINPR_ASSERT(context);
555 priv = context->priv;
560 (void)SetEvent(priv->stopEvent);
562 if (WaitForSingleObject(priv->thread, INFINITE) == WAIT_FAILED)
564 error = GetLastError();
565 WLog_ERR(TAG,
"WaitForSingleObject failed with error %" PRIu32
"", error);
569 (void)CloseHandle(priv->thread);
570 (void)CloseHandle(priv->stopEvent);
572 priv->stopEvent = NULL;
575 if (priv->disp_channel)
577 (void)WTSVirtualChannelClose(priv->disp_channel);
578 priv->disp_channel = NULL;
584 DispServerContext* disp_server_context_new(HANDLE vcm)
586 DispServerContext* context = NULL;
587 DispServerPrivate* priv = NULL;
588 context = (DispServerContext*)calloc(1,
sizeof(DispServerContext));
592 WLog_ERR(TAG,
"disp_server_context_new(): calloc DispServerContext failed!");
596 priv = context->priv = (DispServerPrivate*)calloc(1,
sizeof(DispServerPrivate));
600 WLog_ERR(TAG,
"disp_server_context_new(): calloc DispServerPrivate failed!");
604 priv->input_stream = Stream_New(NULL, 4);
606 if (!priv->input_stream)
608 WLog_ERR(TAG,
"Stream_New failed!");
613 context->Open = disp_server_open;
614 context->Close = disp_server_close;
615 context->DisplayControlCaps = disp_server_send_caps_pdu;
616 priv->isReady = FALSE;
619 WINPR_PRAGMA_DIAG_PUSH
620 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
621 disp_server_context_free(context);
622 WINPR_PRAGMA_DIAG_POP
626 void disp_server_context_free(DispServerContext* context)
633 disp_server_close(context);
634 Stream_Free(context->priv->input_stream, TRUE);