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* s)
214{
215 UINT error = CHANNEL_RC_OK;
216 size_t beg = 0;
217 size_t end = 0;
218 DISPLAY_CONTROL_HEADER header = WINPR_C_ARRAY_INIT;
219
220 WINPR_ASSERT(s);
221 WINPR_ASSERT(context);
222
223 beg = Stream_GetPosition(s);
224
225 if ((error = disp_read_header(s, &header)))
226 {
227 WLog_ERR(TAG, "disp_read_header failed with error %" PRIu32 "!", error);
228 return error;
229 }
230
231 switch (header.type)
232 {
233 case DISPLAY_CONTROL_PDU_TYPE_MONITOR_LAYOUT:
234 if ((error = disp_recv_display_control_monitor_layout_pdu(s, context)))
235 WLog_ERR(TAG,
236 "disp_recv_display_control_monitor_layout_pdu "
237 "failed with error %" PRIu32 "!",
238 error);
239
240 break;
241
242 default:
243 error = CHANNEL_RC_BAD_PROC;
244 WLog_WARN(TAG, "Received unknown PDU type: %" PRIu32 "", header.type);
245 break;
246 }
247
248 end = Stream_GetPosition(s);
249
250 if (end != (beg + header.length))
251 {
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;
256 }
257
258 return error;
259}
260
261static UINT disp_server_handle_messages(DispServerContext* context)
262{
263 DWORD BytesReturned = 0;
264 void* buffer = nullptr;
265 UINT ret = CHANNEL_RC_OK;
266 DispServerPrivate* priv = nullptr;
267 wStream* s = nullptr;
268
269 WINPR_ASSERT(context);
270
271 priv = context->priv;
272 WINPR_ASSERT(priv);
273
274 s = priv->input_stream;
275 WINPR_ASSERT(s);
276
277 /* Check whether the dynamic channel is ready */
278 if (!priv->isReady)
279 {
280 if (WTSVirtualChannelQuery(priv->disp_channel, WTSVirtualChannelReady, &buffer,
281 &BytesReturned) == FALSE)
282 {
283 if (GetLastError() == ERROR_NO_DATA)
284 return ERROR_NO_DATA;
285
286 WLog_ERR(TAG, "WTSVirtualChannelQuery failed");
287 return ERROR_INTERNAL_ERROR;
288 }
289
290 priv->isReady = *((BOOL*)buffer);
291 WTSFreeMemory(buffer);
292 }
293
294 /* Consume channel event only after the disp dynamic channel is ready */
295 Stream_ResetPosition(s);
296
297 if (!WTSVirtualChannelRead(priv->disp_channel, 0, nullptr, 0, &BytesReturned))
298 {
299 if (GetLastError() == ERROR_NO_DATA)
300 return ERROR_NO_DATA;
301
302 WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
303 return ERROR_INTERNAL_ERROR;
304 }
305
306 if (BytesReturned < 1)
307 return CHANNEL_RC_OK;
308
309 if (!Stream_EnsureRemainingCapacity(s, BytesReturned))
310 {
311 WLog_ERR(TAG, "Stream_EnsureRemainingCapacity failed!");
312 return CHANNEL_RC_NO_MEMORY;
313 }
314
315 const size_t cap = Stream_Capacity(s);
316 if (cap > UINT32_MAX)
317 return CHANNEL_RC_NO_BUFFER;
318
319 if (WTSVirtualChannelRead(priv->disp_channel, 0, Stream_BufferAs(s, char), (ULONG)cap,
320 &BytesReturned) == FALSE)
321 {
322 WLog_ERR(TAG, "WTSVirtualChannelRead failed!");
323 return ERROR_INTERNAL_ERROR;
324 }
325
326 if (!Stream_SetLength(s, BytesReturned))
327 return ERROR_INTERNAL_ERROR;
328
329 Stream_ResetPosition(s);
330
331 while (Stream_GetPosition(s) < Stream_Length(s))
332 {
333 if ((ret = disp_server_receive_pdu(context, s)))
334 {
335 WLog_ERR(TAG,
336 "disp_server_receive_pdu "
337 "failed with error %" PRIu32 "!",
338 ret);
339 return ret;
340 }
341 }
342
343 return ret;
344}
345
346static DWORD WINAPI disp_server_thread_func(LPVOID arg)
347{
348 DispServerContext* context = (DispServerContext*)arg;
349 DispServerPrivate* priv = nullptr;
350 DWORD status = 0;
351 DWORD nCount = 0;
352 HANDLE events[8] = WINPR_C_ARRAY_INIT;
353 UINT error = CHANNEL_RC_OK;
354
355 WINPR_ASSERT(context);
356
357 priv = context->priv;
358 WINPR_ASSERT(priv);
359
360 events[nCount++] = priv->stopEvent;
361 events[nCount++] = priv->channelEvent;
362
363 /* Main virtual channel loop. RDPEDISP do not need version negotiation */
364 while (TRUE)
365 {
366 status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
367
368 if (status == WAIT_FAILED)
369 {
370 error = GetLastError();
371 WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "", error);
372 break;
373 }
374
375 /* Stop Event */
376 if (status == WAIT_OBJECT_0)
377 break;
378
379 if ((error = disp_server_handle_messages(context)))
380 {
381 WLog_ERR(TAG, "disp_server_handle_messages failed with error %" PRIu32 "", error);
382 break;
383 }
384 }
385
386 ExitThread(error);
387 return error;
388}
389
395static UINT disp_server_open(DispServerContext* context)
396{
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;
403 BOOL status = TRUE;
404
405 WINPR_ASSERT(context);
406
407 priv = context->priv;
408 WINPR_ASSERT(priv);
409
410 priv->SessionId = WTS_CURRENT_SESSION;
411
412 if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
413 (LPSTR*)&pSessionId, &BytesReturned) == FALSE)
414 {
415 WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
416 rc = ERROR_INTERNAL_ERROR;
417 goto out_close;
418 }
419
420 priv->SessionId = (DWORD)*pSessionId;
421 WTSFreeMemory(pSessionId);
422 priv->disp_channel =
423 WTSVirtualChannelOpenEx(priv->SessionId, DISP_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC);
424
425 if (!priv->disp_channel)
426 {
427 WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed!");
428 rc = GetLastError();
429 goto out_close;
430 }
431
432 channelId = WTSChannelGetIdByHandle(priv->disp_channel);
433
434 IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
435 if (!status)
436 {
437 WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
438 rc = ERROR_INTERNAL_ERROR;
439 goto out_close;
440 }
441
442 /* Query for channel event handle */
443 if (!WTSVirtualChannelQuery(priv->disp_channel, WTSVirtualEventHandle, &buffer,
444 &BytesReturned) ||
445 (BytesReturned != sizeof(HANDLE)))
446 {
447 WLog_ERR(TAG,
448 "WTSVirtualChannelQuery failed "
449 "or invalid returned size(%" PRIu32 ")",
450 BytesReturned);
451
452 if (buffer)
453 WTSFreeMemory(buffer);
454
455 rc = ERROR_INTERNAL_ERROR;
456 goto out_close;
457 }
458
459 priv->channelEvent = *(HANDLE*)buffer;
460 WTSFreeMemory(buffer);
461
462 if (priv->thread == nullptr)
463 {
464 if (!(priv->stopEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
465 {
466 WLog_ERR(TAG, "CreateEvent failed!");
467 rc = ERROR_INTERNAL_ERROR;
468 goto out_close;
469 }
470
471 if (!(priv->thread =
472 CreateThread(nullptr, 0, disp_server_thread_func, (void*)context, 0, nullptr)))
473 {
474 WLog_ERR(TAG, "CreateEvent failed!");
475 (void)CloseHandle(priv->stopEvent);
476 priv->stopEvent = nullptr;
477 rc = ERROR_INTERNAL_ERROR;
478 goto out_close;
479 }
480 }
481
482 return CHANNEL_RC_OK;
483out_close:
484 (void)WTSVirtualChannelClose(priv->disp_channel);
485 priv->disp_channel = nullptr;
486 priv->channelEvent = nullptr;
487 return rc;
488}
489
490static UINT disp_server_packet_send(DispServerContext* context, wStream* s)
491{
492 UINT ret = 0;
493 ULONG written = 0;
494
495 WINPR_ASSERT(context);
496 WINPR_ASSERT(s);
497
498 const size_t pos = Stream_GetPosition(s);
499
500 WINPR_ASSERT(pos <= UINT32_MAX);
501 if (!WTSVirtualChannelWrite(context->priv->disp_channel, Stream_BufferAs(s, char), (UINT32)pos,
502 &written))
503 {
504 WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
505 ret = ERROR_INTERNAL_ERROR;
506 goto out;
507 }
508
509 if (written < Stream_GetPosition(s))
510 {
511 WLog_WARN(TAG, "Unexpected bytes written: %" PRIu32 "/%" PRIuz "", written,
512 Stream_GetPosition(s));
513 }
514
515 ret = CHANNEL_RC_OK;
516out:
517 Stream_Free(s, TRUE);
518 return ret;
519}
520
526static UINT disp_server_send_caps_pdu(DispServerContext* context)
527{
528 wStream* s = nullptr;
529
530 WINPR_ASSERT(context);
531
532 s = disp_server_single_packet_new(DISPLAY_CONTROL_PDU_TYPE_CAPS, 12);
533
534 if (!s)
535 {
536 WLog_ERR(TAG, "disp_server_single_packet_new failed!");
537 return CHANNEL_RC_NO_MEMORY;
538 }
539
540 Stream_Write_UINT32(s, context->MaxNumMonitors); /* MaxNumMonitors (4 bytes) */
541 Stream_Write_UINT32(s, context->MaxMonitorAreaFactorA); /* MaxMonitorAreaFactorA (4 bytes) */
542 Stream_Write_UINT32(s, context->MaxMonitorAreaFactorB); /* MaxMonitorAreaFactorB (4 bytes) */
543 return disp_server_packet_send(context, s);
544}
545
551static UINT disp_server_close(DispServerContext* context)
552{
553 UINT error = CHANNEL_RC_OK;
554 DispServerPrivate* priv = nullptr;
555
556 WINPR_ASSERT(context);
557
558 priv = context->priv;
559 WINPR_ASSERT(priv);
560
561 if (priv->thread)
562 {
563 (void)SetEvent(priv->stopEvent);
564
565 if (WaitForSingleObject(priv->thread, INFINITE) == WAIT_FAILED)
566 {
567 error = GetLastError();
568 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
569 return error;
570 }
571
572 (void)CloseHandle(priv->thread);
573 (void)CloseHandle(priv->stopEvent);
574 priv->thread = nullptr;
575 priv->stopEvent = nullptr;
576 }
577
578 if (priv->disp_channel)
579 {
580 (void)WTSVirtualChannelClose(priv->disp_channel);
581 priv->disp_channel = nullptr;
582 }
583
584 return error;
585}
586
587DispServerContext* disp_server_context_new(HANDLE vcm)
588{
589 DispServerContext* context = nullptr;
590 DispServerPrivate* priv = nullptr;
591 context = (DispServerContext*)calloc(1, sizeof(DispServerContext));
592
593 if (!context)
594 {
595 WLog_ERR(TAG, "disp_server_context_new(): calloc DispServerContext failed!");
596 goto fail;
597 }
598
599 priv = context->priv = (DispServerPrivate*)calloc(1, sizeof(DispServerPrivate));
600
601 if (!context->priv)
602 {
603 WLog_ERR(TAG, "disp_server_context_new(): calloc DispServerPrivate failed!");
604 goto fail;
605 }
606
607 priv->input_stream = Stream_New(nullptr, 4);
608
609 if (!priv->input_stream)
610 {
611 WLog_ERR(TAG, "Stream_New failed!");
612 goto fail;
613 }
614
615 context->vcm = vcm;
616 context->Open = disp_server_open;
617 context->Close = disp_server_close;
618 context->DisplayControlCaps = disp_server_send_caps_pdu;
619 priv->isReady = FALSE;
620 return context;
621fail:
622 WINPR_PRAGMA_DIAG_PUSH
623 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
624 disp_server_context_free(context);
625 WINPR_PRAGMA_DIAG_POP
626 return nullptr;
627}
628
629void disp_server_context_free(DispServerContext* context)
630{
631 if (!context)
632 return;
633
634 if (context->priv)
635 {
636 disp_server_close(context);
637 Stream_Free(context->priv->input_stream, TRUE);
638 free(context->priv);
639 }
640
641 free(context);
642}