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(NULL, 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 NULL;
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;
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 %" PRIu32 "",
143 pdu.MonitorLayoutSize, 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 = { 0 };
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 Stream_SetPosition(s, (beg + header.length));
255 }
256
257 return error;
258}
259
260static UINT disp_server_handle_messages(DispServerContext* context)
261{
262 DWORD BytesReturned = 0;
263 void* buffer = NULL;
264 UINT ret = CHANNEL_RC_OK;
265 DispServerPrivate* priv = NULL;
266 wStream* s = NULL;
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_SetPosition(s, 0);
295
296 if (!WTSVirtualChannelRead(priv->disp_channel, 0, NULL, 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 Stream_SetLength(s, BytesReturned);
326 Stream_SetPosition(s, 0);
327
328 while (Stream_GetPosition(s) < Stream_Length(s))
329 {
330 if ((ret = disp_server_receive_pdu(context, s)))
331 {
332 WLog_ERR(TAG,
333 "disp_server_receive_pdu "
334 "failed with error %" PRIu32 "!",
335 ret);
336 return ret;
337 }
338 }
339
340 return ret;
341}
342
343static DWORD WINAPI disp_server_thread_func(LPVOID arg)
344{
345 DispServerContext* context = (DispServerContext*)arg;
346 DispServerPrivate* priv = NULL;
347 DWORD status = 0;
348 DWORD nCount = 0;
349 HANDLE events[8] = { 0 };
350 UINT error = CHANNEL_RC_OK;
351
352 WINPR_ASSERT(context);
353
354 priv = context->priv;
355 WINPR_ASSERT(priv);
356
357 events[nCount++] = priv->stopEvent;
358 events[nCount++] = priv->channelEvent;
359
360 /* Main virtual channel loop. RDPEDISP do not need version negotiation */
361 while (TRUE)
362 {
363 status = WaitForMultipleObjects(nCount, events, FALSE, INFINITE);
364
365 if (status == WAIT_FAILED)
366 {
367 error = GetLastError();
368 WLog_ERR(TAG, "WaitForMultipleObjects failed with error %" PRIu32 "", error);
369 break;
370 }
371
372 /* Stop Event */
373 if (status == WAIT_OBJECT_0)
374 break;
375
376 if ((error = disp_server_handle_messages(context)))
377 {
378 WLog_ERR(TAG, "disp_server_handle_messages failed with error %" PRIu32 "", error);
379 break;
380 }
381 }
382
383 ExitThread(error);
384 return error;
385}
386
392static UINT disp_server_open(DispServerContext* context)
393{
394 UINT rc = ERROR_INTERNAL_ERROR;
395 DispServerPrivate* priv = NULL;
396 DWORD BytesReturned = 0;
397 PULONG pSessionId = NULL;
398 void* buffer = NULL;
399 UINT32 channelId = 0;
400 BOOL status = TRUE;
401
402 WINPR_ASSERT(context);
403
404 priv = context->priv;
405 WINPR_ASSERT(priv);
406
407 priv->SessionId = WTS_CURRENT_SESSION;
408
409 if (WTSQuerySessionInformationA(context->vcm, WTS_CURRENT_SESSION, WTSSessionId,
410 (LPSTR*)&pSessionId, &BytesReturned) == FALSE)
411 {
412 WLog_ERR(TAG, "WTSQuerySessionInformationA failed!");
413 rc = ERROR_INTERNAL_ERROR;
414 goto out_close;
415 }
416
417 priv->SessionId = (DWORD)*pSessionId;
418 WTSFreeMemory(pSessionId);
419 priv->disp_channel =
420 WTSVirtualChannelOpenEx(priv->SessionId, DISP_DVC_CHANNEL_NAME, WTS_CHANNEL_OPTION_DYNAMIC);
421
422 if (!priv->disp_channel)
423 {
424 WLog_ERR(TAG, "WTSVirtualChannelOpenEx failed!");
425 rc = GetLastError();
426 goto out_close;
427 }
428
429 channelId = WTSChannelGetIdByHandle(priv->disp_channel);
430
431 IFCALLRET(context->ChannelIdAssigned, status, context, channelId);
432 if (!status)
433 {
434 WLog_ERR(TAG, "context->ChannelIdAssigned failed!");
435 rc = ERROR_INTERNAL_ERROR;
436 goto out_close;
437 }
438
439 /* Query for channel event handle */
440 if (!WTSVirtualChannelQuery(priv->disp_channel, WTSVirtualEventHandle, &buffer,
441 &BytesReturned) ||
442 (BytesReturned != sizeof(HANDLE)))
443 {
444 WLog_ERR(TAG,
445 "WTSVirtualChannelQuery failed "
446 "or invalid returned size(%" PRIu32 ")",
447 BytesReturned);
448
449 if (buffer)
450 WTSFreeMemory(buffer);
451
452 rc = ERROR_INTERNAL_ERROR;
453 goto out_close;
454 }
455
456 priv->channelEvent = *(HANDLE*)buffer;
457 WTSFreeMemory(buffer);
458
459 if (priv->thread == NULL)
460 {
461 if (!(priv->stopEvent = CreateEvent(NULL, TRUE, FALSE, NULL)))
462 {
463 WLog_ERR(TAG, "CreateEvent failed!");
464 rc = ERROR_INTERNAL_ERROR;
465 goto out_close;
466 }
467
468 if (!(priv->thread =
469 CreateThread(NULL, 0, disp_server_thread_func, (void*)context, 0, NULL)))
470 {
471 WLog_ERR(TAG, "CreateEvent failed!");
472 (void)CloseHandle(priv->stopEvent);
473 priv->stopEvent = NULL;
474 rc = ERROR_INTERNAL_ERROR;
475 goto out_close;
476 }
477 }
478
479 return CHANNEL_RC_OK;
480out_close:
481 (void)WTSVirtualChannelClose(priv->disp_channel);
482 priv->disp_channel = NULL;
483 priv->channelEvent = NULL;
484 return rc;
485}
486
487static UINT disp_server_packet_send(DispServerContext* context, wStream* s)
488{
489 UINT ret = 0;
490 ULONG written = 0;
491
492 WINPR_ASSERT(context);
493 WINPR_ASSERT(s);
494
495 const size_t pos = Stream_GetPosition(s);
496
497 WINPR_ASSERT(pos <= UINT32_MAX);
498 if (!WTSVirtualChannelWrite(context->priv->disp_channel, Stream_BufferAs(s, char), (UINT32)pos,
499 &written))
500 {
501 WLog_ERR(TAG, "WTSVirtualChannelWrite failed!");
502 ret = ERROR_INTERNAL_ERROR;
503 goto out;
504 }
505
506 if (written < Stream_GetPosition(s))
507 {
508 WLog_WARN(TAG, "Unexpected bytes written: %" PRIu32 "/%" PRIuz "", written,
509 Stream_GetPosition(s));
510 }
511
512 ret = CHANNEL_RC_OK;
513out:
514 Stream_Free(s, TRUE);
515 return ret;
516}
517
523static UINT disp_server_send_caps_pdu(DispServerContext* context)
524{
525 wStream* s = NULL;
526
527 WINPR_ASSERT(context);
528
529 s = disp_server_single_packet_new(DISPLAY_CONTROL_PDU_TYPE_CAPS, 12);
530
531 if (!s)
532 {
533 WLog_ERR(TAG, "disp_server_single_packet_new failed!");
534 return CHANNEL_RC_NO_MEMORY;
535 }
536
537 Stream_Write_UINT32(s, context->MaxNumMonitors); /* MaxNumMonitors (4 bytes) */
538 Stream_Write_UINT32(s, context->MaxMonitorAreaFactorA); /* MaxMonitorAreaFactorA (4 bytes) */
539 Stream_Write_UINT32(s, context->MaxMonitorAreaFactorB); /* MaxMonitorAreaFactorB (4 bytes) */
540 return disp_server_packet_send(context, s);
541}
542
548static UINT disp_server_close(DispServerContext* context)
549{
550 UINT error = CHANNEL_RC_OK;
551 DispServerPrivate* priv = NULL;
552
553 WINPR_ASSERT(context);
554
555 priv = context->priv;
556 WINPR_ASSERT(priv);
557
558 if (priv->thread)
559 {
560 (void)SetEvent(priv->stopEvent);
561
562 if (WaitForSingleObject(priv->thread, INFINITE) == WAIT_FAILED)
563 {
564 error = GetLastError();
565 WLog_ERR(TAG, "WaitForSingleObject failed with error %" PRIu32 "", error);
566 return error;
567 }
568
569 (void)CloseHandle(priv->thread);
570 (void)CloseHandle(priv->stopEvent);
571 priv->thread = NULL;
572 priv->stopEvent = NULL;
573 }
574
575 if (priv->disp_channel)
576 {
577 (void)WTSVirtualChannelClose(priv->disp_channel);
578 priv->disp_channel = NULL;
579 }
580
581 return error;
582}
583
584DispServerContext* disp_server_context_new(HANDLE vcm)
585{
586 DispServerContext* context = NULL;
587 DispServerPrivate* priv = NULL;
588 context = (DispServerContext*)calloc(1, sizeof(DispServerContext));
589
590 if (!context)
591 {
592 WLog_ERR(TAG, "disp_server_context_new(): calloc DispServerContext failed!");
593 goto fail;
594 }
595
596 priv = context->priv = (DispServerPrivate*)calloc(1, sizeof(DispServerPrivate));
597
598 if (!context->priv)
599 {
600 WLog_ERR(TAG, "disp_server_context_new(): calloc DispServerPrivate failed!");
601 goto fail;
602 }
603
604 priv->input_stream = Stream_New(NULL, 4);
605
606 if (!priv->input_stream)
607 {
608 WLog_ERR(TAG, "Stream_New failed!");
609 goto fail;
610 }
611
612 context->vcm = vcm;
613 context->Open = disp_server_open;
614 context->Close = disp_server_close;
615 context->DisplayControlCaps = disp_server_send_caps_pdu;
616 priv->isReady = FALSE;
617 return context;
618fail:
619 WINPR_PRAGMA_DIAG_PUSH
620 WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
621 disp_server_context_free(context);
622 WINPR_PRAGMA_DIAG_POP
623 return NULL;
624}
625
626void disp_server_context_free(DispServerContext* context)
627{
628 if (!context)
629 return;
630
631 if (context->priv)
632 {
633 disp_server_close(context);
634 Stream_Free(context->priv->input_stream, TRUE);
635 free(context->priv);
636 }
637
638 free(context);
639}