20#include <freerdp/config.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") 
   47static 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);
 
  127static 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_INT32(s, monitor->Left);                
 
  178    Stream_Read_INT32(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);
 
  213static 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));
 
  260static 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 
"!",
 
  343static 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);
 
  392static 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  priv->channelEvent = *(HANDLE*)buffer;
 
  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;
 
  487static 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);
 
  523static 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);
 
  548static 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;
 
  584DispServerContext* 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
 
  626void disp_server_context_free(DispServerContext* context)
 
  633    disp_server_close(context);
 
  634    Stream_Free(context->priv->input_stream, TRUE);