FreeRDP
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 
47 static 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;
69 error:
70  Stream_Free(s, TRUE);
71  return NULL;
72 }
73 
74 static 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 
92 static 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 
127 static 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_UINT32(s, monitor->Left); /* Left (4 bytes) */
178  Stream_Read_UINT32(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 
208 out:
209  free(pdu.Monitors);
210  return error;
211 }
212 
213 static 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 
260 static 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 
343 static 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 
392 static 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  CopyMemory(&priv->channelEvent, buffer, sizeof(HANDLE));
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;
480 out_close:
481  (void)WTSVirtualChannelClose(priv->disp_channel);
482  priv->disp_channel = NULL;
483  priv->channelEvent = NULL;
484  return rc;
485 }
486 
487 static 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;
513 out:
514  Stream_Free(s, TRUE);
515  return ret;
516 }
517 
523 static 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 
548 static 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 
584 DispServerContext* 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;
618 fail:
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 
626 void 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 }