FreeRDP
Loading...
Searching...
No Matches
server/disp_main.c
1
20#include <freerdp/config.h>
21
22#include "disp_main.h"
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26
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>
35
36#include <freerdp/server/disp.h>
37#include "../disp_common.h"
38
39#define TAG CHANNELS_TAG("rdpedisp.server")
40
47static wStream* disp_server_single_packet_new(UINT32 type, UINT32 length)
48{
49 UINT error = 0;
51 wStream* s = Stream_New(nullptr, DISPLAY_CONTROL_HEADER_LENGTH + length);
52
53 if (!s)
54 {
55 WLog_ERR(TAG, "Stream_New failed!");
56 goto error;
57 }
58
59 header.type = type;
60 header.length = DISPLAY_CONTROL_HEADER_LENGTH + length;
61
62 if ((error = disp_write_header(s, &header)))
63 {
64 WLog_ERR(TAG, "Failed to write header with error %" PRIu32 "!", error);
65 goto error;
66 }
67
68 return s;
69error:
70 Stream_Free(s, TRUE);
71 return nullptr;
72}
73
74static void disp_server_sanitize_monitor_layout(DISPLAY_CONTROL_MONITOR_LAYOUT* monitor)
75{
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)
80 {
81 if (monitor->PhysicalWidth != 0 || monitor->PhysicalHeight != 0)
82 WLog_DBG(
83 TAG,
84 "Sanitizing invalid physical monitor size. Old physical monitor size: [%" PRIu32
85 ", %" PRIu32 "]",
86 monitor->PhysicalWidth, monitor->PhysicalHeight);
87
88 monitor->PhysicalWidth = monitor->PhysicalHeight = 0;
89 }
90}
91
92static BOOL disp_server_is_monitor_layout_valid(const DISPLAY_CONTROL_MONITOR_LAYOUT* monitor)
93{
94 WINPR_ASSERT(monitor);
95
96 if (monitor->Width < DISPLAY_CONTROL_MIN_MONITOR_WIDTH ||
97 monitor->Width > DISPLAY_CONTROL_MAX_MONITOR_WIDTH)
98 {
99 WLog_WARN(TAG, "Received invalid value for monitor->Width: %" PRIu32 "", monitor->Width);
100 return FALSE;
101 }
102
103 if (monitor->Height < DISPLAY_CONTROL_MIN_MONITOR_HEIGHT ||
104 monitor->Height > DISPLAY_CONTROL_MAX_MONITOR_HEIGHT)
105 {
106 WLog_WARN(TAG, "Received invalid value for monitor->Height: %" PRIu32 "", monitor->Height);
107 return FALSE;
108 }
109
110 switch (monitor->Orientation)
111 {
112 case ORIENTATION_LANDSCAPE:
113 case ORIENTATION_PORTRAIT:
114 case ORIENTATION_LANDSCAPE_FLIPPED:
115 case ORIENTATION_PORTRAIT_FLIPPED:
116 break;
117
118 default:
119 WLog_WARN(TAG, "Received incorrect value for monitor->Orientation: %" PRIu32 "",
120 monitor->Orientation);
121 return FALSE;
122 }
123
124 return TRUE;
125}
126
127static UINT disp_recv_display_control_monitor_layout_pdu(wStream* s, DispServerContext* context)
128{
129 UINT32 error = CHANNEL_RC_OK;
130 DISPLAY_CONTROL_MONITOR_LAYOUT_PDU pdu = WINPR_C_ARRAY_INIT;
131
132 WINPR_ASSERT(s);
133 WINPR_ASSERT(context);
134
135 if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
136 return ERROR_INVALID_DATA;
137
138 Stream_Read_UINT32(s, pdu.MonitorLayoutSize); /* MonitorLayoutSize (4 bytes) */
139
140 if (pdu.MonitorLayoutSize != DISPLAY_CONTROL_MONITOR_LAYOUT_SIZE)
141 {
142 WLog_ERR(TAG, "MonitorLayoutSize is set to %" PRIu32 ". expected %d", pdu.MonitorLayoutSize,
143 DISPLAY_CONTROL_MONITOR_LAYOUT_SIZE);
144 return ERROR_INVALID_DATA;
145 }
146
147 Stream_Read_UINT32(s, pdu.NumMonitors); /* NumMonitors (4 bytes) */
148
149 if (pdu.NumMonitors > context->MaxNumMonitors)
150 {
151 WLog_ERR(TAG, "NumMonitors (%" PRIu32 ")> server MaxNumMonitors (%" PRIu32 ")",
152 pdu.NumMonitors, context->MaxNumMonitors);
153 return ERROR_INVALID_DATA;
154 }
155
156 if (!Stream_CheckAndLogRequiredLengthOfSize(TAG, s, pdu.NumMonitors,
157 DISPLAY_CONTROL_MONITOR_LAYOUT_SIZE))
158 return ERROR_INVALID_DATA;
159
160 pdu.Monitors = (DISPLAY_CONTROL_MONITOR_LAYOUT*)calloc(pdu.NumMonitors,
162
163 if (!pdu.Monitors)
164 {
165 WLog_ERR(TAG, "disp_recv_display_control_monitor_layout_pdu(): calloc failed!");
166 return CHANNEL_RC_NO_MEMORY;
167 }
168
169 WLog_DBG(TAG, "disp_recv_display_control_monitor_layout_pdu: NumMonitors=%" PRIu32 "",
170 pdu.NumMonitors);
171
172 for (UINT32 index = 0; index < pdu.NumMonitors; index++)
173 {
174 DISPLAY_CONTROL_MONITOR_LAYOUT* monitor = &(pdu.Monitors[index]);
175
176 Stream_Read_UINT32(s, monitor->Flags); /* Flags (4 bytes) */
177 Stream_Read_INT32(s, monitor->Left); /* Left (4 bytes) */
178 Stream_Read_INT32(s, monitor->Top); /* Top (4 bytes) */
179 Stream_Read_UINT32(s, monitor->Width); /* Width (4 bytes) */
180 Stream_Read_UINT32(s, monitor->Height); /* Height (4 bytes) */
181 Stream_Read_UINT32(s, monitor->PhysicalWidth); /* PhysicalWidth (4 bytes) */
182 Stream_Read_UINT32(s, monitor->PhysicalHeight); /* PhysicalHeight (4 bytes) */
183 Stream_Read_UINT32(s, monitor->Orientation); /* Orientation (4 bytes) */
184 Stream_Read_UINT32(s, monitor->DesktopScaleFactor); /* DesktopScaleFactor (4 bytes) */
185 Stream_Read_UINT32(s, monitor->DeviceScaleFactor); /* DeviceScaleFactor (4 bytes) */
186
187 disp_server_sanitize_monitor_layout(monitor);
188 WLog_DBG(TAG,
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,
192 monitor->Height);
193 WLog_DBG(TAG,
194 "\t PhysicalWidth: %" PRIu32 " PhysicalHeight: %" PRIu32 " Orientation: %" PRIu32
195 "",
196 monitor->PhysicalWidth, monitor->PhysicalHeight, monitor->Orientation);
197
198 if (!disp_server_is_monitor_layout_valid(monitor))
199 {
200 error = ERROR_INVALID_DATA;
201 goto out;
202 }
203 }
204
205 if (context)
206 IFCALLRET(context->DispMonitorLayout, error, context, &pdu);
207
208out:
209 free(pdu.Monitors);
210 return error;
211}
212
213static UINT disp_server_receive_pdu(DispServerContext* context, wStream* data)
214{
215 UINT error = CHANNEL_RC_OK;
216 DISPLAY_CONTROL_HEADER header = WINPR_C_ARRAY_INIT;
217
218 WINPR_ASSERT(data);
219 WINPR_ASSERT(context);
220
221 size_t packetLen = 0;
222 if ((error = disp_read_header(data, &header, &packetLen)))
223 {
224 WLog_ERR(TAG, "disp_read_header failed with error %" PRIu32 "!", error);
225 return error;
226 }
227
228 wStream sbuffer = WINPR_C_ARRAY_INIT;
229 wStream* s = Stream_StaticConstInit(&sbuffer, Stream_Pointer(data), packetLen);
230 Stream_Seek(data, packetLen);
231
232 switch (header.type)
233 {
234 case DISPLAY_CONTROL_PDU_TYPE_MONITOR_LAYOUT:
235 if ((error = disp_recv_display_control_monitor_layout_pdu(s, context)))
236 WLog_ERR(TAG,
237 "disp_recv_display_control_monitor_layout_pdu "
238 "failed with error %" PRIu32 "!",
239 error);
240
241 break;
242
243 default:
244 error = CHANNEL_RC_BAD_PROC;
245 WLog_WARN(TAG, "Received unknown PDU type: %" PRIu32 "", header.type);
246 break;
247 }
248
249 const size_t end = Stream_GetPosition(s);
250
251 if (end != packetLen)
252 {
253 WLog_ERR(TAG, "Unexpected DISP pdu end: Actual: %" PRIuz ", Expected: %" PRIuz "", end,
254 packetLen);
255 }
256
257 return error;
258}
259
260static UINT disp_server_handle_messages(DispServerContext* context)
261{
262 DWORD BytesReturned = 0;
263 void* buffer = nullptr;
264 UINT ret = CHANNEL_RC_OK;
265 DispServerPrivate* priv = nullptr;
266 wStream* s = nullptr;
267
268 WINPR_ASSERT(context);
269
270 priv = context->priv;
271 WINPR_ASSERT(priv);
272
273 s = priv->input_stream;
274 WINPR_ASSERT(s);
275
276 /* Check whether the dynamic channel is ready */
277 if (!priv->isReady)
278 {
279 if (WTSVirtualChannelQuery(priv->disp_channel, WTSVirtualChannelReady, &buffer,
280 &BytesReturned) == FALSE)
281 {
282 if (GetLastError() == ERROR_NO_DATA)
283 return ERROR_NO_DATA;
284
285 WLog_ERR(TAG, "WTSVirtualChannelQuery failed");
286 return ERROR_INTERNAL_ERROR;
287 }
288
289 priv->isReady = *((BOOL*)buffer);
290 WTSFreeMemory(buffer);
291 }
292
293 /* Consume channel event only after the disp dynamic channel is ready */
294 Stream_ResetPosition(s);
295
296 if (!WTSVirtualChannelRead(priv->disp_channel, 0, nullptr, 0, &BytesReturned))
297 {
298 if (GetLastError() == ERROR_NO_DATA)
299 return ERROR_NO_DATA;
300
301 WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
302 return ERROR_INTERNAL_ERROR;
303 }
304
305 if (BytesReturned < 1)
306 return CHANNEL_RC_OK;
307
308 if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
309 {
310 WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
311 return CHANNEL_RC_NO_MEMORY;
312 }
313
314 const size_t cap = Stream_Capacity(s);
315 if (cap > UINT32_MAX)
316 return CHANNEL_RC_NO_BUFFER;
317
318 if (WTSVirtualChannelRead(priv->disp_channel, 0, Stream_BufferAs(s, char), (ULONG)cap,
319 &BytesReturned) == FALSE)
320 {
321 WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
322 return ERROR_INTERNAL_ERROR;
323 }
324
325 if (!Stream_SetLength(s, BytesReturned))
326 return ERROR_INTERNAL_ERROR;
327
328 Stream_ResetPosition(s);
329
330 while (Stream_GetPosition(s) < Stream_Length(s))
331 {
332 if ((ret = disp_server_receive_pdu(context, s)))
333 {
334 WLog_ERR(TAG,
335 "disp_server_receive_pdu "
336 "failed with error %" PRIu32 "!",
337 ret);
338 return ret;
339 }
340 }
341
342 return ret;
343}
344
345static DWORD WINAPI disp_server_thread_func(LPVOID arg)
346{
347 DispServerContext* context = (DispServerContext*)arg;
348 DispServerPrivate* priv = nullptr;
349 DWORD status = 0;
350 DWORD nCount = 0;
351 HANDLE events[8] = WINPR_C_ARRAY_INIT;
352 UINT error = CHANNEL_RC_OK;
353
354 WINPR_ASSERT(context);
355
356 priv = context->priv;
357 WINPR_ASSERT(priv);
358
359 events[nCount++] = priv->stopEvent;
360 events[nCount++] = priv->channelEvent;
361
362 /* Main virtual channel loop. RDPEDISP do not need version negotiation */
363 while (TRUE)
364 {
365 status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
366
367 if (status == WAIT_FAILED)
368 {
369 error = GetLastError();
370 WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "", error);
371 break;
372 }
373
374 /* Stop Event */
375 if (status == WAIT_OBJECT_0)
376 break;
377
378 if ((error = disp_server_handle_messages(context)))
379 {
380 WLog_ERR(TAG, "disp_server_handle_messages failed with error %" PRIu32 "", error);
381 break;
382 }
383 }
384
385 ExitThread(error);
386 return error;
387}
388
394static UINT disp_server_open(DispServerContext* context)
395{
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;
402 BOOL status = TRUE;
403
404 WINPR_ASSERT(context);
405
406 priv = context->priv;
407 WINPR_ASSERT(priv);
408
409 priv->SessionId = WTS_CURRENT_SESSION;
410
411 if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
412 (LPSTR*)&pSessionId, &BytesReturned) == FALSE)
413 {
414 WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
415 rc = ERROR_INTERNAL_ERROR;
416 goto out_close;
417 }
418
419 priv->SessionId = (DWORD)*pSessionId;
420 WTSFreeMemory(pSessionId);
421 priv->disp_channel =
422 WTSVirtualChannelOpenEx(priv->SessionId, DISP_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC);
423
424 if (!priv->disp_channel)
425 {
426 WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed!");
427 rc = GetLastError();
428 goto out_close;
429 }
430
431 channelId = WTSChannelGetIdByHandle(priv->disp_channel);
432
433 IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
434 if (!status)
435 {
436 WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
437 rc = ERROR_INTERNAL_ERROR;
438 goto out_close;
439 }
440
441 /* Query for channel event handle */
442 if (!WTSVirtualChannelQuery(priv->disp_channel, WTSVirtualEventHandle, &buffer,
443 &BytesReturned) ||
444 (BytesReturned != sizeof(HANDLE)))
445 {
446 WLog_ERR(TAG,
447 "WTSVirtualChannelQuery failed "
448 "or invalid returned size(%" PRIu32 ")",
449 BytesReturned);
450
451 if (buffer)
452 WTSFreeMemory(buffer);
453
454 rc = ERROR_INTERNAL_ERROR;
455 goto out_close;
456 }
457
458 priv->channelEvent = *(HANDLE*)buffer;
459 WTSFreeMemory(buffer);
460
461 if (priv->thread == nullptr)
462 {
463 if (!(priv->stopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
464 {
465 WLog_ERR(TAG, "CreateEvent failed!");
466 rc = ERROR_INTERNAL_ERROR;
467 goto out_close;
468 }
469
470 if (!(priv->thread =
471 CreateThread(nullptr, 0, disp_server_thread_func, (void*)context, 0, nullptr)))
472 {
473 WLog_ERR(TAG, "CreateEvent failed!");
474 (void)CloseHandle(priv->stopEvent);
475 priv->stopEvent = nullptr;
476 rc = ERROR_INTERNAL_ERROR;
477 goto out_close;
478 }
479 }
480
481 return CHANNEL_RC_OK;
482out_close:
483 (void)WTSVirtualChannelClose(priv->disp_channel);
484 priv->disp_channel = nullptr;
485 priv->channelEvent = nullptr;
486 return rc;
487}
488
489static UINT disp_server_packet_send(DispServerContext* context, wStream* s)
490{
491 UINT ret = 0;
492 ULONG written = 0;
493
494 WINPR_ASSERT(context);
495 WINPR_ASSERT(s);
496
497 const size_t pos = Stream_GetPosition(s);
498
499 WINPR_ASSERT(pos <= UINT32_MAX);
500 if (!WTSVirtualChannelWrite(context->priv->disp_channel, Stream_BufferAs(s, char), (UINT32)pos,
501 &written))
502 {
503 WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
504 ret = ERROR_INTERNAL_ERROR;
505 goto out;
506 }
507
508 if (written < Stream_GetPosition(s))
509 {
510 WLog_WARN(TAG, "Unexpected bytes written: %" PRIu32 "/%" PRIuz "", written,
511 Stream_GetPosition(s));
512 }
513
514 ret = CHANNEL_RC_OK;
515out:
516 Stream_Free(s, TRUE);
517 return ret;
518}
519
525static UINT disp_server_send_caps_pdu(DispServerContext* context)
526{
527 wStream* s = nullptr;
528
529 WINPR_ASSERT(context);
530
531 s = disp_server_single_packet_new(DISPLAY_CONTROL_PDU_TYPE_CAPS, 12);
532
533 if (!s)
534 {
535 WLog_ERR(TAG, "disp_server_single_packet_new failed!");
536 return CHANNEL_RC_NO_MEMORY;
537 }
538
539 Stream_Write_UINT32(s, context->MaxNumMonitors); /* MaxNumMonitors (4 bytes) */
540 Stream_Write_UINT32(s, context->MaxMonitorAreaFactorA); /* MaxMonitorAreaFactorA (4 bytes) */
541 Stream_Write_UINT32(s, context->MaxMonitorAreaFactorB); /* MaxMonitorAreaFactorB (4 bytes) */
542 return disp_server_packet_send(context, s);
543}
544
550static UINT disp_server_close(DispServerContext* context)
551{
552 UINT error = CHANNEL_RC_OK;
553 DispServerPrivate* priv = nullptr;
554
555 WINPR_ASSERT(context);
556
557 priv = context->priv;
558 WINPR_ASSERT(priv);
559
560 if (priv->thread)
561 {
562 (void)SetEvent(priv->stopEvent);
563
564 if (WaitForSingleObject(priv->thread, INFINITE) == WAIT_FAILED)
565 {
566 error = GetLastError();
567 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
568 return error;
569 }
570
571 (void)CloseHandle(priv->thread);
572 (void)CloseHandle(priv->stopEvent);
573 priv->thread = nullptr;
574 priv->stopEvent = nullptr;
575 }
576
577 if (priv->disp_channel)
578 {
579 (void)WTSVirtualChannelClose(priv->disp_channel);
580 priv->disp_channel = nullptr;
581 }
582
583 return error;
584}
585
586DispServerContext* disp_server_context_new(HANDLE vcm)
587{
588 DispServerContext* context = nullptr;
589 DispServerPrivate* priv = nullptr;
590 context = (DispServerContext*)calloc(1, sizeof(DispServerContext));
591
592 if (!context)
593 {
594 WLog_ERR(TAG, "disp_server_context_new(): calloc DispServerContext failed!");
595 goto fail;
596 }
597
598 priv = context->priv = (DispServerPrivate*)calloc(1, sizeof(DispServerPrivate));
599
600 if (!context->priv)
601 {
602 WLog_ERR(TAG, "disp_server_context_new(): calloc DispServerPrivate failed!");
603 goto fail;
604 }
605
606 priv->input_stream = Stream_New(nullptr, 4);
607
608 if (!priv->input_stream)
609 {
610 WLog_ERR(TAG, "Stream_New failed!");
611 goto fail;
612 }
613
614 context->vcm = vcm;
615 context->Open = disp_server_open;
616 context->Close = disp_server_close;
617 context->DisplayControlCaps = disp_server_send_caps_pdu;
618 priv->isReady = FALSE;
619 return context;
620fail:
621 WINPR_PRAGMA_DIAG_PUSH
622 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
623 disp_server_context_free(context);
624 WINPR_PRAGMA_DIAG_POP
625 return nullptr;
626}
627
628void disp_server_context_free(DispServerContext* context)
629{
630 if (!context)
631 return;
632
633 if (context->priv)
634 {
635 disp_server_close(context);
636 Stream_Free(context->priv->input_stream, TRUE);
637 free(context->priv);
638 }
639
640 free(context);
641}