FreeRDP
client/drdynvc_main.c
1 
22 #include <freerdp/config.h>
23 
24 #include <winpr/crt.h>
25 #include <winpr/stream.h>
26 #include <winpr/interlocked.h>
27 
28 #include <freerdp/freerdp.h>
29 #include <freerdp/channels/drdynvc.h>
30 #include <freerdp/utils/drdynvc.h>
31 
32 #include "drdynvc_main.h"
33 
34 #define TAG CHANNELS_TAG("drdynvc.client")
35 
36 static void dvcman_channel_free(DVCMAN_CHANNEL* channel);
37 static UINT dvcman_channel_close(DVCMAN_CHANNEL* channel, BOOL perRequest, BOOL fromHashTableFn);
38 static void dvcman_free(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr);
39 static UINT drdynvc_write_data(drdynvcPlugin* drdynvc, UINT32 ChannelId, const BYTE* data,
40  UINT32 dataSize, BOOL* close);
41 static UINT drdynvc_send(drdynvcPlugin* drdynvc, wStream* s);
42 
43 static void dvcman_wtslistener_free(DVCMAN_LISTENER* listener)
44 {
45  if (listener)
46  free(listener->channel_name);
47  free(listener);
48 }
49 
55 static UINT dvcman_get_configuration(IWTSListener* pListener, void** ppPropertyBag)
56 {
57  WINPR_ASSERT(ppPropertyBag);
58  WINPR_UNUSED(pListener);
59  *ppPropertyBag = NULL;
60  return ERROR_INTERNAL_ERROR;
61 }
62 
68 static UINT dvcman_create_listener(IWTSVirtualChannelManager* pChannelMgr,
69  const char* pszChannelName, ULONG ulFlags,
70  IWTSListenerCallback* pListenerCallback,
71  IWTSListener** ppListener)
72 {
73  DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
74  DVCMAN_LISTENER* listener = NULL;
75 
76  WINPR_ASSERT(dvcman);
77  WLog_DBG(TAG, "create_listener: %" PRIuz ".%s.", HashTable_Count(dvcman->listeners) + 1,
78  pszChannelName);
79  listener = (DVCMAN_LISTENER*)calloc(1, sizeof(DVCMAN_LISTENER));
80 
81  if (!listener)
82  {
83  WLog_ERR(TAG, "calloc failed!");
84  return CHANNEL_RC_NO_MEMORY;
85  }
86 
87  listener->iface.GetConfiguration = dvcman_get_configuration;
88  listener->iface.pInterface = NULL;
89  listener->dvcman = dvcman;
90  listener->channel_name = _strdup(pszChannelName);
91 
92  if (!listener->channel_name)
93  {
94  WLog_ERR(TAG, "_strdup failed!");
95  dvcman_wtslistener_free(listener);
96  return CHANNEL_RC_NO_MEMORY;
97  }
98 
99  listener->flags = ulFlags;
100  listener->listener_callback = pListenerCallback;
101 
102  if (ppListener)
103  *ppListener = (IWTSListener*)listener;
104 
105  if (!HashTable_Insert(dvcman->listeners, listener->channel_name, listener))
106  {
107  dvcman_wtslistener_free(listener);
108  return ERROR_INTERNAL_ERROR;
109  }
110 
111  // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): HashTable_Insert takes ownership of listener
112  return CHANNEL_RC_OK;
113 }
114 
115 static UINT dvcman_destroy_listener(IWTSVirtualChannelManager* pChannelMgr, IWTSListener* pListener)
116 {
117  DVCMAN_LISTENER* listener = (DVCMAN_LISTENER*)pListener;
118 
119  WINPR_UNUSED(pChannelMgr);
120 
121  if (listener)
122  {
123  DVCMAN* dvcman = listener->dvcman;
124  if (dvcman)
125  HashTable_Remove(dvcman->listeners, listener->channel_name);
126  }
127 
128  return CHANNEL_RC_OK;
129 }
130 
136 static UINT dvcman_register_plugin(IDRDYNVC_ENTRY_POINTS* pEntryPoints, const char* name,
137  IWTSPlugin* pPlugin)
138 {
139  WINPR_ASSERT(pEntryPoints);
140  DVCMAN* dvcman = ((DVCMAN_ENTRY_POINTS*)pEntryPoints)->dvcman;
141 
142  WINPR_ASSERT(dvcman);
143  if (!ArrayList_Append(dvcman->plugin_names, name))
144  return ERROR_INTERNAL_ERROR;
145  if (!ArrayList_Append(dvcman->plugins, pPlugin))
146  return ERROR_INTERNAL_ERROR;
147 
148  WLog_DBG(TAG, "register_plugin: num_plugins %" PRIuz, ArrayList_Count(dvcman->plugins));
149  return CHANNEL_RC_OK;
150 }
151 
152 static IWTSPlugin* dvcman_get_plugin(IDRDYNVC_ENTRY_POINTS* pEntryPoints, const char* name)
153 {
154  IWTSPlugin* plugin = NULL;
155  size_t nc = 0;
156  size_t pc = 0;
157  WINPR_ASSERT(pEntryPoints);
158  DVCMAN* dvcman = ((DVCMAN_ENTRY_POINTS*)pEntryPoints)->dvcman;
159  if (!dvcman || !pEntryPoints || !name)
160  return NULL;
161 
162  nc = ArrayList_Count(dvcman->plugin_names);
163  pc = ArrayList_Count(dvcman->plugins);
164  if (nc != pc)
165  return NULL;
166 
167  ArrayList_Lock(dvcman->plugin_names);
168  ArrayList_Lock(dvcman->plugins);
169  for (size_t i = 0; i < pc; i++)
170  {
171  const char* cur = ArrayList_GetItem(dvcman->plugin_names, i);
172  if (strcmp(cur, name) == 0)
173  {
174  plugin = ArrayList_GetItem(dvcman->plugins, i);
175  break;
176  }
177  }
178  ArrayList_Unlock(dvcman->plugin_names);
179  ArrayList_Unlock(dvcman->plugins);
180  return plugin;
181 }
182 
183 static const ADDIN_ARGV* dvcman_get_plugin_data(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
184 {
185  WINPR_ASSERT(pEntryPoints);
186  return ((DVCMAN_ENTRY_POINTS*)pEntryPoints)->args;
187 }
188 
189 static rdpContext* dvcman_get_rdp_context(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
190 {
191  DVCMAN_ENTRY_POINTS* entry = (DVCMAN_ENTRY_POINTS*)pEntryPoints;
192  WINPR_ASSERT(entry);
193  return entry->context;
194 }
195 
196 static rdpSettings* dvcman_get_rdp_settings(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
197 {
198  rdpContext* context = dvcman_get_rdp_context(pEntryPoints);
199  WINPR_ASSERT(context);
200 
201  return context->settings;
202 }
203 
204 static UINT32 dvcman_get_channel_id(IWTSVirtualChannel* channel)
205 {
206  DVCMAN_CHANNEL* dvc = (DVCMAN_CHANNEL*)channel;
207  WINPR_ASSERT(dvc);
208  return dvc->channel_id;
209 }
210 
211 static const char* dvcman_get_channel_name(IWTSVirtualChannel* channel)
212 {
213  DVCMAN_CHANNEL* dvc = (DVCMAN_CHANNEL*)channel;
214  WINPR_ASSERT(dvc);
215  return dvc->channel_name;
216 }
217 
218 static DVCMAN_CHANNEL* dvcman_get_channel_by_id(IWTSVirtualChannelManager* pChannelMgr,
219  UINT32 ChannelId, BOOL doRef)
220 {
221  DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
222  DVCMAN_CHANNEL* dvcChannel = NULL;
223 
224  WINPR_ASSERT(dvcman);
225  HashTable_Lock(dvcman->channelsById);
226  dvcChannel = HashTable_GetItemValue(dvcman->channelsById, &ChannelId);
227  if (dvcChannel)
228  {
229  if (doRef)
230  InterlockedIncrement(&dvcChannel->refCounter);
231  }
232 
233  HashTable_Unlock(dvcman->channelsById);
234  return dvcChannel;
235 }
236 
237 static IWTSVirtualChannel* dvcman_find_channel_by_id(IWTSVirtualChannelManager* pChannelMgr,
238  UINT32 ChannelId)
239 {
240  DVCMAN_CHANNEL* channel = dvcman_get_channel_by_id(pChannelMgr, ChannelId, FALSE);
241  if (!channel)
242  return NULL;
243 
244  return &channel->iface;
245 }
246 
247 static void dvcman_plugin_terminate(void* plugin)
248 {
249  IWTSPlugin* pPlugin = plugin;
250 
251  WINPR_ASSERT(pPlugin);
252  UINT error = IFCALLRESULT(CHANNEL_RC_OK, pPlugin->Terminated, pPlugin);
253  if (error != CHANNEL_RC_OK)
254  WLog_ERR(TAG, "Terminated failed with error %" PRIu32 "!", error);
255 }
256 
257 static void wts_listener_free(void* arg)
258 {
259  DVCMAN_LISTENER* listener = (DVCMAN_LISTENER*)arg;
260  dvcman_wtslistener_free(listener);
261 }
262 
263 static BOOL channelIdMatch(const void* k1, const void* k2)
264 {
265  WINPR_ASSERT(k1);
266  WINPR_ASSERT(k2);
267  return *((const UINT32*)k1) == *((const UINT32*)k2);
268 }
269 
270 static UINT32 channelIdHash(const void* id)
271 {
272  WINPR_ASSERT(id);
273  return *((const UINT32*)id);
274 }
275 
276 static void channelByIdCleanerFn(void* value)
277 {
278  DVCMAN_CHANNEL* channel = (DVCMAN_CHANNEL*)value;
279  if (channel)
280  {
281  dvcman_channel_close(channel, FALSE, TRUE);
282  dvcman_channel_free(channel);
283  }
284 }
285 
286 static IWTSVirtualChannelManager* dvcman_new(drdynvcPlugin* plugin)
287 {
288  wObject* obj = NULL;
289  DVCMAN* dvcman = (DVCMAN*)calloc(1, sizeof(DVCMAN));
290 
291  if (!dvcman)
292  return NULL;
293 
294  dvcman->iface.CreateListener = dvcman_create_listener;
295  dvcman->iface.DestroyListener = dvcman_destroy_listener;
296  dvcman->iface.FindChannelById = dvcman_find_channel_by_id;
297  dvcman->iface.GetChannelId = dvcman_get_channel_id;
298  dvcman->iface.GetChannelName = dvcman_get_channel_name;
299  dvcman->drdynvc = plugin;
300  dvcman->channelsById = HashTable_New(TRUE);
301 
302  if (!dvcman->channelsById)
303  goto fail;
304 
305  HashTable_SetHashFunction(dvcman->channelsById, channelIdHash);
306  obj = HashTable_KeyObject(dvcman->channelsById);
307  WINPR_ASSERT(obj);
308  obj->fnObjectEquals = channelIdMatch;
309 
310  obj = HashTable_ValueObject(dvcman->channelsById);
311  WINPR_ASSERT(obj);
312  obj->fnObjectFree = channelByIdCleanerFn;
313 
314  dvcman->pool = StreamPool_New(TRUE, 10);
315  if (!dvcman->pool)
316  goto fail;
317 
318  dvcman->listeners = HashTable_New(TRUE);
319  if (!dvcman->listeners)
320  goto fail;
321  HashTable_SetHashFunction(dvcman->listeners, HashTable_StringHash);
322 
323  obj = HashTable_KeyObject(dvcman->listeners);
324  obj->fnObjectEquals = HashTable_StringCompare;
325 
326  obj = HashTable_ValueObject(dvcman->listeners);
327  obj->fnObjectFree = wts_listener_free;
328 
329  dvcman->plugin_names = ArrayList_New(TRUE);
330  if (!dvcman->plugin_names)
331  goto fail;
332  obj = ArrayList_Object(dvcman->plugin_names);
333  obj->fnObjectNew = winpr_ObjectStringClone;
334  obj->fnObjectFree = winpr_ObjectStringFree;
335 
336  dvcman->plugins = ArrayList_New(TRUE);
337  if (!dvcman->plugins)
338  goto fail;
339  obj = ArrayList_Object(dvcman->plugins);
340  obj->fnObjectFree = dvcman_plugin_terminate;
341  return &dvcman->iface;
342 fail:
343  dvcman_free(plugin, &dvcman->iface);
344  return NULL;
345 }
346 
352 static UINT dvcman_load_addin(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr,
353  const ADDIN_ARGV* args, rdpContext* context)
354 {
355  WINPR_ASSERT(drdynvc);
356  WINPR_ASSERT(pChannelMgr);
357  WINPR_ASSERT(args);
358  WINPR_ASSERT(context);
359 
360  WLog_Print(drdynvc->log, WLOG_INFO, "Loading Dynamic Virtual Channel %s", args->argv[0]);
361 
362  PVIRTUALCHANNELENTRY pvce =
363  freerdp_load_channel_addin_entry(args->argv[0], NULL, NULL, FREERDP_ADDIN_CHANNEL_DYNAMIC);
364  PDVC_PLUGIN_ENTRY pDVCPluginEntry = WINPR_FUNC_PTR_CAST(pvce, PDVC_PLUGIN_ENTRY);
365 
366  if (pDVCPluginEntry)
367  {
368  DVCMAN_ENTRY_POINTS entryPoints = { 0 };
369 
370  entryPoints.iface.RegisterPlugin = dvcman_register_plugin;
371  entryPoints.iface.GetPlugin = dvcman_get_plugin;
372  entryPoints.iface.GetPluginData = dvcman_get_plugin_data;
373  entryPoints.iface.GetRdpSettings = dvcman_get_rdp_settings;
374  entryPoints.iface.GetRdpContext = dvcman_get_rdp_context;
375  entryPoints.dvcman = (DVCMAN*)pChannelMgr;
376  entryPoints.args = args;
377  entryPoints.context = context;
378  return pDVCPluginEntry(&entryPoints.iface);
379  }
380 
381  return ERROR_INVALID_FUNCTION;
382 }
383 
384 static void dvcman_channel_free(DVCMAN_CHANNEL* channel)
385 {
386  if (!channel)
387  return;
388 
389  if (channel->dvc_data)
390  Stream_Release(channel->dvc_data);
391 
392  DeleteCriticalSection(&(channel->lock));
393  free(channel->channel_name);
394  free(channel);
395 }
396 
397 static void dvcman_channel_unref(DVCMAN_CHANNEL* channel)
398 {
399  WINPR_ASSERT(channel);
400  if (InterlockedDecrement(&channel->refCounter))
401  return;
402 
403  DVCMAN* dvcman = channel->dvcman;
404  if (dvcman)
405  HashTable_Remove(dvcman->channelsById, &channel->channel_id);
406 }
407 
408 static UINT dvcchannel_send_close(DVCMAN_CHANNEL* channel)
409 {
410  WINPR_ASSERT(channel);
411  DVCMAN* dvcman = channel->dvcman;
412  drdynvcPlugin* drdynvc = dvcman->drdynvc;
413  wStream* s = StreamPool_Take(dvcman->pool, 5);
414 
415  if (!s)
416  {
417  WLog_Print(drdynvc->log, WLOG_ERROR, "StreamPool_Take failed!");
418  return CHANNEL_RC_NO_MEMORY;
419  }
420 
421  Stream_Write_UINT8(s, (CLOSE_REQUEST_PDU << 4) | 0x02);
422  Stream_Write_UINT32(s, channel->channel_id);
423  return drdynvc_send(drdynvc, s);
424 }
425 
426 static void check_open_close_receive(DVCMAN_CHANNEL* channel)
427 {
428  WINPR_ASSERT(channel);
429 
430  IWTSVirtualChannelCallback* cb = channel->channel_callback;
431  const char* name = channel->channel_name;
432  const UINT32 id = channel->channel_id;
433 
434  WINPR_ASSERT(cb);
435  if (cb->OnOpen || cb->OnClose)
436  {
437  if (!cb->OnOpen || !cb->OnClose)
438  WLog_WARN(TAG, "{%s:%" PRIu32 "} OnOpen=%p, OnClose=%p", name, id, cb->OnOpen,
439  cb->OnClose);
440  }
441 }
442 
443 static UINT dvcman_call_on_receive(DVCMAN_CHANNEL* channel, wStream* data)
444 {
445  WINPR_ASSERT(channel);
446  WINPR_ASSERT(data);
447 
448  IWTSVirtualChannelCallback* cb = channel->channel_callback;
449  WINPR_ASSERT(cb);
450 
451  check_open_close_receive(channel);
452  WINPR_ASSERT(cb->OnDataReceived);
453  return cb->OnDataReceived(cb, data);
454 }
455 
456 static UINT dvcman_channel_close(DVCMAN_CHANNEL* channel, BOOL perRequest, BOOL fromHashTableFn)
457 {
458  UINT error = CHANNEL_RC_OK;
459  DrdynvcClientContext* context = NULL;
460 
461  WINPR_ASSERT(channel);
462  switch (channel->state)
463  {
464  case DVC_CHANNEL_INIT:
465  break;
466  case DVC_CHANNEL_RUNNING:
467  if (channel->dvcman)
468  {
469  drdynvcPlugin* drdynvc = channel->dvcman->drdynvc;
470  WINPR_ASSERT(drdynvc);
471  context = drdynvc->context;
472  if (perRequest)
473  WLog_Print(drdynvc->log, WLOG_DEBUG, "sending close confirm for '%s'",
474  channel->channel_name);
475 
476  error = dvcchannel_send_close(channel);
477  if (error != CHANNEL_RC_OK)
478  {
479  const char* msg = "error when sending close confirm for '%s'";
480  if (perRequest)
481  msg = "error when sending closeRequest for '%s'";
482 
483  WLog_Print(drdynvc->log, WLOG_DEBUG, msg, channel->channel_name);
484  }
485  }
486 
487  channel->state = DVC_CHANNEL_CLOSED;
488 
489  IWTSVirtualChannelCallback* cb = channel->channel_callback;
490  if (cb)
491  {
492  check_open_close_receive(channel);
493  IFCALL(cb->OnClose, cb);
494  }
495 
496  channel->channel_callback = NULL;
497 
498  if (channel->dvcman && channel->dvcman->drdynvc)
499  {
500  if (context)
501  {
502  IFCALLRET(context->OnChannelDisconnected, error, context, channel->channel_name,
503  channel->pInterface);
504  }
505  }
506 
507  if (!fromHashTableFn)
508  dvcman_channel_unref(channel);
509  break;
510  case DVC_CHANNEL_CLOSED:
511  break;
512  default:
513  break;
514  }
515 
516  return error;
517 }
518 
519 static DVCMAN_CHANNEL* dvcman_channel_new(drdynvcPlugin* drdynvc,
520  IWTSVirtualChannelManager* pChannelMgr, UINT32 ChannelId,
521  const char* ChannelName)
522 {
523  DVCMAN_CHANNEL* channel = NULL;
524 
525  WINPR_ASSERT(drdynvc);
526  WINPR_ASSERT(pChannelMgr);
527  channel = (DVCMAN_CHANNEL*)calloc(1, sizeof(DVCMAN_CHANNEL));
528 
529  if (!channel)
530  return NULL;
531 
532  channel->dvcman = (DVCMAN*)pChannelMgr;
533  channel->channel_id = ChannelId;
534  channel->refCounter = 1;
535  channel->state = DVC_CHANNEL_INIT;
536  channel->channel_name = _strdup(ChannelName);
537 
538  if (!channel->channel_name)
539  goto fail;
540 
541  if (!InitializeCriticalSectionEx(&(channel->lock), 0, 0))
542  goto fail;
543 
544  return channel;
545 fail:
546  dvcman_channel_free(channel);
547  return NULL;
548 }
549 
550 static void dvcman_clear(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr)
551 {
552  DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
553 
554  WINPR_ASSERT(dvcman);
555  WINPR_UNUSED(drdynvc);
556 
557  HashTable_Clear(dvcman->channelsById);
558  ArrayList_Clear(dvcman->plugins);
559  ArrayList_Clear(dvcman->plugin_names);
560  HashTable_Clear(dvcman->listeners);
561 }
562 static void dvcman_free(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr)
563 {
564  DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
565 
566  WINPR_ASSERT(dvcman);
567  WINPR_UNUSED(drdynvc);
568 
569  HashTable_Free(dvcman->channelsById);
570  ArrayList_Free(dvcman->plugins);
571  ArrayList_Free(dvcman->plugin_names);
572  HashTable_Free(dvcman->listeners);
573 
574  StreamPool_Free(dvcman->pool);
575  free(dvcman);
576 }
577 
583 static UINT dvcman_init(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr)
584 {
585  DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
586  UINT error = CHANNEL_RC_OK;
587 
588  WINPR_ASSERT(dvcman);
589  ArrayList_Lock(dvcman->plugins);
590  for (size_t i = 0; i < ArrayList_Count(dvcman->plugins); i++)
591  {
592  IWTSPlugin* pPlugin = ArrayList_GetItem(dvcman->plugins, i);
593 
594  error = IFCALLRESULT(CHANNEL_RC_OK, pPlugin->Initialize, pPlugin, pChannelMgr);
595  if (error != CHANNEL_RC_OK)
596  {
597  WLog_Print(drdynvc->log, WLOG_ERROR, "Initialize failed with error %" PRIu32 "!",
598  error);
599  goto fail;
600  }
601  }
602 
603 fail:
604  ArrayList_Unlock(dvcman->plugins);
605  return error;
606 }
607 
613 static UINT dvcman_write_channel(IWTSVirtualChannel* pChannel, ULONG cbSize, const BYTE* pBuffer,
614  void* pReserved)
615 {
616  BOOL close = FALSE;
617  UINT status = 0;
618  DVCMAN_CHANNEL* channel = (DVCMAN_CHANNEL*)pChannel;
619 
620  WINPR_UNUSED(pReserved);
621  if (!channel || !channel->dvcman)
622  return CHANNEL_RC_BAD_CHANNEL;
623 
624  EnterCriticalSection(&(channel->lock));
625  status =
626  drdynvc_write_data(channel->dvcman->drdynvc, channel->channel_id, pBuffer, cbSize, &close);
627  LeaveCriticalSection(&(channel->lock));
628  /* Close delayed, it removes the channel struct */
629  if (close)
630  dvcman_channel_close(channel, FALSE, FALSE);
631 
632  return status;
633 }
634 
640 static UINT dvcman_close_channel_iface(IWTSVirtualChannel* pChannel)
641 {
642  DVCMAN_CHANNEL* channel = (DVCMAN_CHANNEL*)pChannel;
643 
644  if (!channel)
645  return CHANNEL_RC_BAD_CHANNEL;
646 
647  WLog_DBG(TAG, "close_channel_iface: id=%" PRIu32 "", channel->channel_id);
648  return dvcman_channel_close(channel, FALSE, FALSE);
649 }
650 
656 static DVCMAN_CHANNEL* dvcman_create_channel(drdynvcPlugin* drdynvc,
657  IWTSVirtualChannelManager* pChannelMgr,
658  UINT32 ChannelId, const char* ChannelName, UINT* res)
659 {
660  BOOL bAccept = 0;
661  DVCMAN_CHANNEL* channel = NULL;
662  DrdynvcClientContext* context = NULL;
663  DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
664  DVCMAN_LISTENER* listener = NULL;
665  IWTSVirtualChannelCallback* pCallback = NULL;
666 
667  WINPR_ASSERT(dvcman);
668  WINPR_ASSERT(res);
669 
670  HashTable_Lock(dvcman->listeners);
671  listener = (DVCMAN_LISTENER*)HashTable_GetItemValue(dvcman->listeners, ChannelName);
672  if (!listener)
673  {
674  *res = ERROR_NOT_FOUND;
675  goto out;
676  }
677 
678  channel = dvcman_get_channel_by_id(pChannelMgr, ChannelId, FALSE);
679  if (channel)
680  {
681  switch (channel->state)
682  {
683  case DVC_CHANNEL_RUNNING:
684  WLog_Print(drdynvc->log, WLOG_ERROR,
685  "Protocol error: Duplicated ChannelId %" PRIu32 " (%s)!", ChannelId,
686  ChannelName);
687  *res = CHANNEL_RC_ALREADY_OPEN;
688  goto out;
689 
690  case DVC_CHANNEL_CLOSED:
691  case DVC_CHANNEL_INIT:
692  default:
693  WLog_Print(drdynvc->log, WLOG_ERROR, "not expecting a createChannel from state %d",
694  channel->state);
695  *res = CHANNEL_RC_INITIALIZATION_ERROR;
696  goto out;
697  }
698  }
699  else
700  {
701  if (!(channel = dvcman_channel_new(drdynvc, pChannelMgr, ChannelId, ChannelName)))
702  {
703  WLog_Print(drdynvc->log, WLOG_ERROR, "dvcman_channel_new failed!");
704  *res = CHANNEL_RC_NO_MEMORY;
705  goto out;
706  }
707  }
708 
709  if (!HashTable_Insert(dvcman->channelsById, &channel->channel_id, channel))
710  {
711  WLog_Print(drdynvc->log, WLOG_ERROR, "unable to register channel in our channel list");
712  *res = ERROR_INTERNAL_ERROR;
713  dvcman_channel_free(channel);
714  channel = NULL;
715  goto out;
716  }
717 
718  channel->iface.Write = dvcman_write_channel;
719  channel->iface.Close = dvcman_close_channel_iface;
720  bAccept = TRUE;
721 
722  *res = listener->listener_callback->OnNewChannelConnection(
723  listener->listener_callback, &channel->iface, NULL, &bAccept, &pCallback);
724 
725  if (*res != CHANNEL_RC_OK)
726  {
727  WLog_Print(drdynvc->log, WLOG_ERROR,
728  "OnNewChannelConnection failed with error %" PRIu32 "!", *res);
729  *res = ERROR_INTERNAL_ERROR;
730  dvcman_channel_unref(channel);
731  goto out;
732  }
733 
734  if (!bAccept)
735  {
736  WLog_Print(drdynvc->log, WLOG_ERROR, "OnNewChannelConnection returned with bAccept FALSE!");
737  *res = ERROR_INTERNAL_ERROR;
738  dvcman_channel_unref(channel);
739  channel = NULL;
740  goto out;
741  }
742 
743  WLog_Print(drdynvc->log, WLOG_DEBUG, "listener %s created new channel %" PRIu32 "",
744  listener->channel_name, channel->channel_id);
745  channel->state = DVC_CHANNEL_RUNNING;
746  channel->channel_callback = pCallback;
747  channel->pInterface = listener->iface.pInterface;
748  context = dvcman->drdynvc->context;
749 
750  IFCALLRET(context->OnChannelConnected, *res, context, ChannelName, listener->iface.pInterface);
751  if (*res != CHANNEL_RC_OK)
752  {
753  WLog_Print(drdynvc->log, WLOG_ERROR,
754  "context.OnChannelConnected failed with error %" PRIu32 "", *res);
755  }
756 
757 out:
758  HashTable_Unlock(dvcman->listeners);
759 
760  return channel;
761 }
762 
768 static UINT dvcman_open_channel(drdynvcPlugin* drdynvc, DVCMAN_CHANNEL* channel)
769 {
770  IWTSVirtualChannelCallback* pCallback = NULL;
771  UINT error = CHANNEL_RC_OK;
772 
773  WINPR_ASSERT(drdynvc);
774  WINPR_ASSERT(channel);
775  if (channel->state == DVC_CHANNEL_RUNNING)
776  {
777  pCallback = channel->channel_callback;
778 
779  if (pCallback->OnOpen)
780  {
781  check_open_close_receive(channel);
782  error = pCallback->OnOpen(pCallback);
783  if (error)
784  {
785  WLog_Print(drdynvc->log, WLOG_ERROR, "OnOpen failed with error %" PRIu32 "!",
786  error);
787  goto out;
788  }
789  }
790 
791  WLog_Print(drdynvc->log, WLOG_DEBUG, "open_channel: ChannelId %" PRIu32 "",
792  channel->channel_id);
793  }
794 
795 out:
796  return error;
797 }
798 
804 static UINT dvcman_receive_channel_data_first(DVCMAN_CHANNEL* channel, UINT32 length)
805 {
806  WINPR_ASSERT(channel);
807  WINPR_ASSERT(channel->dvcman);
808  if (channel->dvc_data)
809  Stream_Release(channel->dvc_data);
810 
811  channel->dvc_data = StreamPool_Take(channel->dvcman->pool, length);
812 
813  if (!channel->dvc_data)
814  {
815  drdynvcPlugin* drdynvc = channel->dvcman->drdynvc;
816  WLog_Print(drdynvc->log, WLOG_ERROR, "StreamPool_Take failed!");
817  return CHANNEL_RC_NO_MEMORY;
818  }
819 
820  channel->dvc_data_length = length;
821  return CHANNEL_RC_OK;
822 }
823 
829 static UINT dvcman_receive_channel_data(DVCMAN_CHANNEL* channel, wStream* data,
830  UINT32 ThreadingFlags)
831 {
832  UINT status = CHANNEL_RC_OK;
833  size_t dataSize = Stream_GetRemainingLength(data);
834 
835  WINPR_ASSERT(channel);
836  WINPR_ASSERT(channel->dvcman);
837  if (channel->dvc_data)
838  {
839  drdynvcPlugin* drdynvc = channel->dvcman->drdynvc;
840 
841  /* Fragmented data */
842  if (Stream_GetPosition(channel->dvc_data) + dataSize > channel->dvc_data_length)
843  {
844  WLog_Print(drdynvc->log, WLOG_ERROR, "data exceeding declared length!");
845  Stream_Release(channel->dvc_data);
846  channel->dvc_data = NULL;
847  status = ERROR_INVALID_DATA;
848  goto out;
849  }
850 
851  Stream_Copy(data, channel->dvc_data, dataSize);
852 
853  if (Stream_GetPosition(channel->dvc_data) >= channel->dvc_data_length)
854  {
855  Stream_SealLength(channel->dvc_data);
856  Stream_SetPosition(channel->dvc_data, 0);
857 
858  status = dvcman_call_on_receive(channel, channel->dvc_data);
859  Stream_Release(channel->dvc_data);
860  channel->dvc_data = NULL;
861  }
862  }
863  else
864  status = dvcman_call_on_receive(channel, data);
865 
866 out:
867  return status;
868 }
869 
870 static UINT8 drdynvc_write_variable_uint(wStream* s, UINT32 val)
871 {
872  UINT8 cb = 0;
873 
874  if (val <= 0xFF)
875  {
876  cb = 0;
877  Stream_Write_UINT8(s, (UINT8)val);
878  }
879  else if (val <= 0xFFFF)
880  {
881  cb = 1;
882  Stream_Write_UINT16(s, (UINT16)val);
883  }
884  else
885  {
886  cb = 2;
887  Stream_Write_UINT32(s, val);
888  }
889 
890  return cb;
891 }
892 
898 static UINT drdynvc_send(drdynvcPlugin* drdynvc, wStream* s)
899 {
900  UINT status = 0;
901 
902  if (!drdynvc)
903  status = CHANNEL_RC_BAD_CHANNEL_HANDLE;
904  else
905  {
906  WINPR_ASSERT(drdynvc->channelEntryPoints.pVirtualChannelWriteEx);
907  status = drdynvc->channelEntryPoints.pVirtualChannelWriteEx(
908  drdynvc->InitHandle, drdynvc->OpenHandle, Stream_Buffer(s),
909  (UINT32)Stream_GetPosition(s), s);
910  }
911 
912  switch (status)
913  {
914  case CHANNEL_RC_OK:
915  return CHANNEL_RC_OK;
916 
917  case CHANNEL_RC_NOT_CONNECTED:
918  Stream_Release(s);
919  return CHANNEL_RC_OK;
920 
921  case CHANNEL_RC_BAD_CHANNEL_HANDLE:
922  Stream_Release(s);
923  WLog_ERR(TAG, "VirtualChannelWriteEx failed with CHANNEL_RC_BAD_CHANNEL_HANDLE");
924  return status;
925 
926  default:
927  Stream_Release(s);
928  WLog_Print(drdynvc->log, WLOG_ERROR,
929  "VirtualChannelWriteEx failed with %s [%08" PRIX32 "]",
930  WTSErrorToString(status), status);
931  return status;
932  }
933 }
934 
940 static UINT drdynvc_write_data(drdynvcPlugin* drdynvc, UINT32 ChannelId, const BYTE* data,
941  UINT32 dataSize, BOOL* close)
942 {
943  wStream* data_out = NULL;
944  size_t pos = 0;
945  UINT8 cbChId = 0;
946  UINT8 cbLen = 0;
947  unsigned long chunkLength = 0;
948  UINT status = CHANNEL_RC_BAD_INIT_HANDLE;
949  DVCMAN* dvcman = NULL;
950 
951  if (!drdynvc)
952  return CHANNEL_RC_BAD_CHANNEL_HANDLE;
953 
954  dvcman = (DVCMAN*)drdynvc->channel_mgr;
955  WINPR_ASSERT(dvcman);
956 
957  WLog_Print(drdynvc->log, WLOG_TRACE, "write_data: ChannelId=%" PRIu32 " size=%" PRIu32 "",
958  ChannelId, dataSize);
959  data_out = StreamPool_Take(dvcman->pool, CHANNEL_CHUNK_LENGTH);
960 
961  if (!data_out)
962  {
963  WLog_Print(drdynvc->log, WLOG_ERROR, "StreamPool_Take failed!");
964  return CHANNEL_RC_NO_MEMORY;
965  }
966 
967  Stream_SetPosition(data_out, 1);
968  cbChId = drdynvc_write_variable_uint(data_out, ChannelId);
969  pos = Stream_GetPosition(data_out);
970 
971  if (dataSize == 0)
972  {
973  /* TODO: shall treat that case with write(0) that do a close */
974  *close = TRUE;
975  Stream_Release(data_out);
976  }
977  else if (dataSize <= CHANNEL_CHUNK_LENGTH - pos)
978  {
979  Stream_SetPosition(data_out, 0);
980  Stream_Write_UINT8(data_out, (DATA_PDU << 4) | cbChId);
981  Stream_SetPosition(data_out, pos);
982  Stream_Write(data_out, data, dataSize);
983  status = drdynvc_send(drdynvc, data_out);
984  }
985  else
986  {
987  /* Fragment the data */
988  cbLen = drdynvc_write_variable_uint(data_out, dataSize);
989  pos = Stream_GetPosition(data_out);
990  Stream_SetPosition(data_out, 0);
991  Stream_Write_UINT8(data_out, (DATA_FIRST_PDU << 4) | cbChId | (cbLen << 2));
992  Stream_SetPosition(data_out, pos);
993  chunkLength = CHANNEL_CHUNK_LENGTH - pos;
994  Stream_Write(data_out, data, chunkLength);
995  data += chunkLength;
996  dataSize -= chunkLength;
997  status = drdynvc_send(drdynvc, data_out);
998 
999  while (status == CHANNEL_RC_OK && dataSize > 0)
1000  {
1001  data_out = StreamPool_Take(dvcman->pool, CHANNEL_CHUNK_LENGTH);
1002 
1003  if (!data_out)
1004  {
1005  WLog_Print(drdynvc->log, WLOG_ERROR, "StreamPool_Take failed!");
1006  return CHANNEL_RC_NO_MEMORY;
1007  }
1008 
1009  Stream_SetPosition(data_out, 1);
1010  cbChId = drdynvc_write_variable_uint(data_out, ChannelId);
1011  pos = Stream_GetPosition(data_out);
1012  Stream_SetPosition(data_out, 0);
1013  Stream_Write_UINT8(data_out, (DATA_PDU << 4) | cbChId);
1014  Stream_SetPosition(data_out, pos);
1015  chunkLength = dataSize;
1016 
1017  if (chunkLength > CHANNEL_CHUNK_LENGTH - pos)
1018  chunkLength = CHANNEL_CHUNK_LENGTH - pos;
1019 
1020  Stream_Write(data_out, data, chunkLength);
1021  data += chunkLength;
1022  dataSize -= chunkLength;
1023  status = drdynvc_send(drdynvc, data_out);
1024  }
1025  }
1026 
1027  if (status != CHANNEL_RC_OK)
1028  {
1029  WLog_Print(drdynvc->log, WLOG_ERROR, "VirtualChannelWriteEx failed with %s [%08" PRIX32 "]",
1030  WTSErrorToString(status), status);
1031  return status;
1032  }
1033 
1034  return CHANNEL_RC_OK;
1035 }
1036 
1042 static UINT drdynvc_send_capability_response(drdynvcPlugin* drdynvc)
1043 {
1044  UINT status = 0;
1045  wStream* s = NULL;
1046  DVCMAN* dvcman = NULL;
1047 
1048  if (!drdynvc)
1049  return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1050 
1051  dvcman = (DVCMAN*)drdynvc->channel_mgr;
1052  WINPR_ASSERT(dvcman);
1053 
1054  WLog_Print(drdynvc->log, WLOG_TRACE, "capability_response");
1055  s = StreamPool_Take(dvcman->pool, 4);
1056 
1057  if (!s)
1058  {
1059  WLog_Print(drdynvc->log, WLOG_ERROR, "Stream_Ndrdynvc_write_variable_uintew failed!");
1060  return CHANNEL_RC_NO_MEMORY;
1061  }
1062 
1063  Stream_Write_UINT16(s, 0x0050); /* Cmd+Sp+cbChId+Pad. Note: MSTSC sends 0x005c */
1064  Stream_Write_UINT16(s, drdynvc->version);
1065  status = drdynvc_send(drdynvc, s);
1066 
1067  if (status != CHANNEL_RC_OK)
1068  {
1069  WLog_Print(drdynvc->log, WLOG_ERROR, "VirtualChannelWriteEx failed with %s [%08" PRIX32 "]",
1070  WTSErrorToString(status), status);
1071  }
1072 
1073  return status;
1074 }
1075 
1081 static UINT drdynvc_process_capability_request(drdynvcPlugin* drdynvc, int Sp, int cbChId,
1082  wStream* s)
1083 {
1084  UINT status = 0;
1085 
1086  if (!drdynvc)
1087  return CHANNEL_RC_BAD_INIT_HANDLE;
1088 
1089  if (!Stream_CheckAndLogRequiredLength(TAG, s, 3))
1090  return ERROR_INVALID_DATA;
1091 
1092  WLog_Print(drdynvc->log, WLOG_TRACE, "capability_request Sp=%d cbChId=%d", Sp, cbChId);
1093  Stream_Seek(s, 1); /* pad */
1094  Stream_Read_UINT16(s, drdynvc->version);
1095 
1096  /* RDP8 servers offer version 3, though Microsoft forgot to document it
1097  * in their early documents. It behaves the same as version 2.
1098  */
1099  if ((drdynvc->version == 2) || (drdynvc->version == 3))
1100  {
1101  if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
1102  return ERROR_INVALID_DATA;
1103 
1104  Stream_Read_UINT16(s, drdynvc->PriorityCharge0);
1105  Stream_Read_UINT16(s, drdynvc->PriorityCharge1);
1106  Stream_Read_UINT16(s, drdynvc->PriorityCharge2);
1107  Stream_Read_UINT16(s, drdynvc->PriorityCharge3);
1108  }
1109 
1110  status = drdynvc_send_capability_response(drdynvc);
1111  drdynvc->state = DRDYNVC_STATE_READY;
1112  return status;
1113 }
1114 
1115 static UINT32 drdynvc_cblen_to_bytes(int cbLen)
1116 {
1117  switch (cbLen)
1118  {
1119  case 0:
1120  return 1;
1121 
1122  case 1:
1123  return 2;
1124 
1125  default:
1126  return 4;
1127  }
1128 }
1129 
1130 static UINT32 drdynvc_read_variable_uint(wStream* s, int cbLen)
1131 {
1132  UINT32 val = 0;
1133 
1134  switch (cbLen)
1135  {
1136  case 0:
1137  Stream_Read_UINT8(s, val);
1138  break;
1139 
1140  case 1:
1141  Stream_Read_UINT16(s, val);
1142  break;
1143 
1144  default:
1145  Stream_Read_UINT32(s, val);
1146  break;
1147  }
1148 
1149  return val;
1150 }
1151 
1157 static UINT drdynvc_process_create_request(drdynvcPlugin* drdynvc, int Sp, int cbChId, wStream* s)
1158 {
1159  UINT status = 0;
1160  wStream* data_out = NULL;
1161  UINT channel_status = 0;
1162  DVCMAN* dvcman = NULL;
1163  DVCMAN_CHANNEL* channel = NULL;
1164  UINT32 retStatus = 0;
1165 
1166  WINPR_UNUSED(Sp);
1167  if (!drdynvc)
1168  return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1169 
1170  dvcman = (DVCMAN*)drdynvc->channel_mgr;
1171  WINPR_ASSERT(dvcman);
1172 
1173  if (drdynvc->state == DRDYNVC_STATE_CAPABILITIES)
1174  {
1180  drdynvc->version = 3;
1181 
1182  if ((status = drdynvc_send_capability_response(drdynvc)))
1183  {
1184  WLog_Print(drdynvc->log, WLOG_ERROR, "drdynvc_send_capability_response failed!");
1185  return status;
1186  }
1187 
1188  drdynvc->state = DRDYNVC_STATE_READY;
1189  }
1190 
1191  if (!Stream_CheckAndLogRequiredLength(TAG, s, drdynvc_cblen_to_bytes(cbChId)))
1192  return ERROR_INVALID_DATA;
1193 
1194  const UINT32 ChannelId = drdynvc_read_variable_uint(s, cbChId);
1195  const size_t pos = Stream_GetPosition(s);
1196  const char* name = Stream_ConstPointer(s);
1197  const size_t length = Stream_GetRemainingLength(s);
1198 
1199  if (strnlen(name, length) >= length)
1200  return ERROR_INVALID_DATA;
1201 
1202  WLog_Print(drdynvc->log, WLOG_DEBUG,
1203  "process_create_request: ChannelId=%" PRIu32 " ChannelName=%s", ChannelId, name);
1204 
1205  data_out = StreamPool_Take(dvcman->pool, pos + 4);
1206  if (!data_out)
1207  {
1208  WLog_Print(drdynvc->log, WLOG_ERROR, "StreamPool_Take failed!");
1209  return CHANNEL_RC_NO_MEMORY;
1210  }
1211 
1212  Stream_Write_UINT8(data_out, (CREATE_REQUEST_PDU << 4) | cbChId);
1213  Stream_SetPosition(s, 1);
1214  Stream_Copy(s, data_out, pos - 1);
1215 
1216  channel =
1217  dvcman_create_channel(drdynvc, drdynvc->channel_mgr, ChannelId, name, &channel_status);
1218  switch (channel_status)
1219  {
1220  case CHANNEL_RC_OK:
1221  WLog_Print(drdynvc->log, WLOG_DEBUG, "channel created");
1222  retStatus = 0;
1223  break;
1224  case CHANNEL_RC_NO_MEMORY:
1225  WLog_Print(drdynvc->log, WLOG_DEBUG, "not enough memory for channel creation");
1226  retStatus = STATUS_NO_MEMORY;
1227  break;
1228  case ERROR_NOT_FOUND:
1229  WLog_Print(drdynvc->log, WLOG_DEBUG, "no listener for '%s'", name);
1230  retStatus = (UINT32)0xC0000001; /* same code used by mstsc, STATUS_UNSUCCESSFUL */
1231  break;
1232  default:
1233  WLog_Print(drdynvc->log, WLOG_DEBUG, "channel creation error");
1234  retStatus = (UINT32)0xC0000001; /* same code used by mstsc, STATUS_UNSUCCESSFUL */
1235  break;
1236  }
1237  Stream_Write_UINT32(data_out, retStatus);
1238 
1239  status = drdynvc_send(drdynvc, data_out);
1240  if (status != CHANNEL_RC_OK)
1241  {
1242  WLog_Print(drdynvc->log, WLOG_ERROR, "VirtualChannelWriteEx failed with %s [%08" PRIX32 "]",
1243  WTSErrorToString(status), status);
1244  dvcman_channel_unref(channel);
1245  return status;
1246  }
1247 
1248  if (channel_status == CHANNEL_RC_OK)
1249  {
1250  if ((status = dvcman_open_channel(drdynvc, channel)))
1251  {
1252  WLog_Print(drdynvc->log, WLOG_ERROR,
1253  "dvcman_open_channel failed with error %" PRIu32 "!", status);
1254  return status;
1255  }
1256  }
1257 
1258  return status;
1259 }
1260 
1266 static UINT drdynvc_process_data_first(drdynvcPlugin* drdynvc, int Sp, int cbChId, wStream* s,
1267  UINT32 ThreadingFlags)
1268 {
1269  UINT status = CHANNEL_RC_OK;
1270  UINT32 Length = 0;
1271  UINT32 ChannelId = 0;
1272  DVCMAN_CHANNEL* channel = NULL;
1273 
1274  WINPR_ASSERT(drdynvc);
1275  if (!Stream_CheckAndLogRequiredLength(
1276  TAG, s, drdynvc_cblen_to_bytes(cbChId) + drdynvc_cblen_to_bytes(Sp)))
1277  return ERROR_INVALID_DATA;
1278 
1279  ChannelId = drdynvc_read_variable_uint(s, cbChId);
1280  Length = drdynvc_read_variable_uint(s, Sp);
1281  WLog_Print(drdynvc->log, WLOG_TRACE,
1282  "process_data_first: Sp=%d cbChId=%d, ChannelId=%" PRIu32 " Length=%" PRIu32 "", Sp,
1283  cbChId, ChannelId, Length);
1284 
1285  channel = dvcman_get_channel_by_id(drdynvc->channel_mgr, ChannelId, TRUE);
1286  if (!channel)
1287  {
1293  WLog_Print(drdynvc->log, WLOG_ERROR, "ChannelId %" PRIu32 " not found!", ChannelId);
1294  return CHANNEL_RC_OK;
1295  }
1296 
1297  if (channel->state != DVC_CHANNEL_RUNNING)
1298  goto out;
1299 
1300  status = dvcman_receive_channel_data_first(channel, Length);
1301 
1302  if (status == CHANNEL_RC_OK)
1303  status = dvcman_receive_channel_data(channel, s, ThreadingFlags);
1304 
1305  if (status != CHANNEL_RC_OK)
1306  status = dvcman_channel_close(channel, FALSE, FALSE);
1307 
1308 out:
1309  dvcman_channel_unref(channel);
1310  return status;
1311 }
1312 
1318 static UINT drdynvc_process_data(drdynvcPlugin* drdynvc, int Sp, int cbChId, wStream* s,
1319  UINT32 ThreadingFlags)
1320 {
1321  UINT32 ChannelId = 0;
1322  DVCMAN_CHANNEL* channel = NULL;
1323  UINT status = CHANNEL_RC_OK;
1324 
1325  WINPR_ASSERT(drdynvc);
1326  if (!Stream_CheckAndLogRequiredLength(TAG, s, drdynvc_cblen_to_bytes(cbChId)))
1327  return ERROR_INVALID_DATA;
1328 
1329  ChannelId = drdynvc_read_variable_uint(s, cbChId);
1330  WLog_Print(drdynvc->log, WLOG_TRACE, "process_data: Sp=%d cbChId=%d, ChannelId=%" PRIu32 "", Sp,
1331  cbChId, ChannelId);
1332 
1333  channel = dvcman_get_channel_by_id(drdynvc->channel_mgr, ChannelId, TRUE);
1334  if (!channel)
1335  {
1341  WLog_Print(drdynvc->log, WLOG_ERROR, "ChannelId %" PRIu32 " not found!", ChannelId);
1342  return CHANNEL_RC_OK;
1343  }
1344 
1345  if (channel->state != DVC_CHANNEL_RUNNING)
1346  goto out;
1347 
1348  status = dvcman_receive_channel_data(channel, s, ThreadingFlags);
1349  if (status != CHANNEL_RC_OK)
1350  status = dvcman_channel_close(channel, FALSE, FALSE);
1351 
1352 out:
1353  dvcman_channel_unref(channel);
1354  return status;
1355 }
1356 
1362 static UINT drdynvc_process_close_request(drdynvcPlugin* drdynvc, int Sp, int cbChId, wStream* s)
1363 {
1364  UINT32 ChannelId = 0;
1365  DVCMAN_CHANNEL* channel = NULL;
1366 
1367  WINPR_ASSERT(drdynvc);
1368  if (!Stream_CheckAndLogRequiredLength(TAG, s, drdynvc_cblen_to_bytes(cbChId)))
1369  return ERROR_INVALID_DATA;
1370 
1371  ChannelId = drdynvc_read_variable_uint(s, cbChId);
1372  WLog_Print(drdynvc->log, WLOG_DEBUG,
1373  "process_close_request: Sp=%d cbChId=%d, ChannelId=%" PRIu32 "", Sp, cbChId,
1374  ChannelId);
1375 
1376  channel = dvcman_get_channel_by_id(drdynvc->channel_mgr, ChannelId, TRUE);
1377  if (!channel)
1378  {
1379  WLog_Print(drdynvc->log, WLOG_ERROR, "dvcman_close_request channel %" PRIu32 " not present",
1380  ChannelId);
1381  return CHANNEL_RC_OK;
1382  }
1383 
1384  dvcman_channel_close(channel, TRUE, FALSE);
1385  dvcman_channel_unref(channel);
1386  return CHANNEL_RC_OK;
1387 }
1388 
1394 static UINT drdynvc_order_recv(drdynvcPlugin* drdynvc, wStream* s, UINT32 ThreadingFlags)
1395 {
1396  UINT8 value = 0;
1397 
1398  WINPR_ASSERT(drdynvc);
1399  if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
1400  return ERROR_INVALID_DATA;
1401 
1402  Stream_Read_UINT8(s, value);
1403  const UINT8 Cmd = (value & 0xf0) >> 4;
1404  const UINT8 Sp = (value & 0x0c) >> 2;
1405  const UINT8 cbChId = (value & 0x03) >> 0;
1406  WLog_Print(drdynvc->log, WLOG_TRACE, "order_recv: Cmd=%s, Sp=%" PRIu8 " cbChId=%" PRIu8,
1407  drdynvc_get_packet_type(Cmd), Sp, cbChId);
1408 
1409  switch (Cmd)
1410  {
1411  case CAPABILITY_REQUEST_PDU:
1412  return drdynvc_process_capability_request(drdynvc, Sp, cbChId, s);
1413 
1414  case CREATE_REQUEST_PDU:
1415  return drdynvc_process_create_request(drdynvc, Sp, cbChId, s);
1416 
1417  case DATA_FIRST_PDU:
1418  return drdynvc_process_data_first(drdynvc, Sp, cbChId, s, ThreadingFlags);
1419 
1420  case DATA_PDU:
1421  return drdynvc_process_data(drdynvc, Sp, cbChId, s, ThreadingFlags);
1422 
1423  case CLOSE_REQUEST_PDU:
1424  return drdynvc_process_close_request(drdynvc, Sp, cbChId, s);
1425 
1426  default:
1427  WLog_Print(drdynvc->log, WLOG_ERROR, "unknown drdynvc cmd 0x%x", Cmd);
1428  return ERROR_INTERNAL_ERROR;
1429  }
1430 }
1431 
1437 static UINT drdynvc_virtual_channel_event_data_received(drdynvcPlugin* drdynvc, void* pData,
1438  UINT32 dataLength, UINT32 totalLength,
1439  UINT32 dataFlags)
1440 {
1441  wStream* data_in = NULL;
1442 
1443  WINPR_ASSERT(drdynvc);
1444  if ((dataFlags & CHANNEL_FLAG_SUSPEND) || (dataFlags & CHANNEL_FLAG_RESUME))
1445  {
1446  return CHANNEL_RC_OK;
1447  }
1448 
1449  if (dataFlags & CHANNEL_FLAG_FIRST)
1450  {
1451  DVCMAN* mgr = (DVCMAN*)drdynvc->channel_mgr;
1452  if (drdynvc->data_in)
1453  Stream_Release(drdynvc->data_in);
1454 
1455  drdynvc->data_in = StreamPool_Take(mgr->pool, totalLength);
1456  }
1457 
1458  if (!(data_in = drdynvc->data_in))
1459  {
1460  WLog_Print(drdynvc->log, WLOG_ERROR, "StreamPool_Take failed!");
1461  return CHANNEL_RC_NO_MEMORY;
1462  }
1463 
1464  if (!Stream_EnsureRemainingCapacity(data_in, dataLength))
1465  {
1466  WLog_Print(drdynvc->log, WLOG_ERROR, "Stream_EnsureRemainingCapacity failed!");
1467  Stream_Release(drdynvc->data_in);
1468  drdynvc->data_in = NULL;
1469  return ERROR_INTERNAL_ERROR;
1470  }
1471 
1472  Stream_Write(data_in, pData, dataLength);
1473 
1474  if (dataFlags & CHANNEL_FLAG_LAST)
1475  {
1476  const size_t cap = Stream_Capacity(data_in);
1477  const size_t pos = Stream_GetPosition(data_in);
1478  if (cap < pos)
1479  {
1480  WLog_Print(drdynvc->log, WLOG_ERROR, "drdynvc_plugin_process_received: read error");
1481  return ERROR_INVALID_DATA;
1482  }
1483 
1484  drdynvc->data_in = NULL;
1485  Stream_SealLength(data_in);
1486  Stream_SetPosition(data_in, 0);
1487 
1488  if (drdynvc->async)
1489  {
1490  if (!MessageQueue_Post(drdynvc->queue, NULL, 0, (void*)data_in, NULL))
1491  {
1492  WLog_Print(drdynvc->log, WLOG_ERROR, "MessageQueue_Post failed!");
1493  return ERROR_INTERNAL_ERROR;
1494  }
1495  }
1496  else
1497  {
1498  UINT error = drdynvc_order_recv(drdynvc, data_in, TRUE);
1499  Stream_Release(data_in);
1500 
1501  if (error)
1502  {
1503  WLog_Print(drdynvc->log, WLOG_WARN,
1504  "drdynvc_order_recv failed with error %" PRIu32 "!", error);
1505  }
1506  }
1507  }
1508 
1509  return CHANNEL_RC_OK;
1510 }
1511 
1512 static void VCAPITYPE drdynvc_virtual_channel_open_event_ex(LPVOID lpUserParam, DWORD openHandle,
1513  UINT event, LPVOID pData,
1514  UINT32 dataLength, UINT32 totalLength,
1515  UINT32 dataFlags)
1516 {
1517  UINT error = CHANNEL_RC_OK;
1518  drdynvcPlugin* drdynvc = (drdynvcPlugin*)lpUserParam;
1519 
1520  WINPR_ASSERT(drdynvc);
1521  switch (event)
1522  {
1523  case CHANNEL_EVENT_DATA_RECEIVED:
1524  if (!drdynvc || (drdynvc->OpenHandle != openHandle))
1525  {
1526  WLog_ERR(TAG, "drdynvc_virtual_channel_open_event: error no match");
1527  return;
1528  }
1529  if ((error = drdynvc_virtual_channel_event_data_received(drdynvc, pData, dataLength,
1530  totalLength, dataFlags)))
1531  WLog_Print(drdynvc->log, WLOG_ERROR,
1532  "drdynvc_virtual_channel_event_data_received failed with error %" PRIu32
1533  "",
1534  error);
1535 
1536  break;
1537 
1538  case CHANNEL_EVENT_WRITE_CANCELLED:
1539  case CHANNEL_EVENT_WRITE_COMPLETE:
1540  {
1541  wStream* s = (wStream*)pData;
1542  Stream_Release(s);
1543  }
1544  break;
1545 
1546  case CHANNEL_EVENT_USER:
1547  break;
1548  default:
1549  break;
1550  }
1551 
1552  if (error && drdynvc && drdynvc->rdpcontext)
1553  setChannelError(drdynvc->rdpcontext, error,
1554  "drdynvc_virtual_channel_open_event reported an error");
1555 }
1556 
1557 static DWORD WINAPI drdynvc_virtual_channel_client_thread(LPVOID arg)
1558 {
1559  /* TODO: rewrite this */
1560  wStream* data = NULL;
1561  wMessage message = { 0 };
1562  UINT error = CHANNEL_RC_OK;
1563  drdynvcPlugin* drdynvc = (drdynvcPlugin*)arg;
1564 
1565  if (!drdynvc)
1566  {
1567  ExitThread((DWORD)CHANNEL_RC_BAD_CHANNEL_HANDLE);
1568  return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1569  }
1570 
1571  while (1)
1572  {
1573  if (!MessageQueue_Wait(drdynvc->queue))
1574  {
1575  WLog_Print(drdynvc->log, WLOG_ERROR, "MessageQueue_Wait failed!");
1576  error = ERROR_INTERNAL_ERROR;
1577  break;
1578  }
1579 
1580  if (!MessageQueue_Peek(drdynvc->queue, &message, TRUE))
1581  {
1582  WLog_Print(drdynvc->log, WLOG_ERROR, "MessageQueue_Peek failed!");
1583  error = ERROR_INTERNAL_ERROR;
1584  break;
1585  }
1586 
1587  if (message.id == WMQ_QUIT)
1588  break;
1589 
1590  if (message.id == 0)
1591  {
1592  UINT32 ThreadingFlags = TRUE;
1593  data = (wStream*)message.wParam;
1594 
1595  if ((error = drdynvc_order_recv(drdynvc, data, ThreadingFlags)))
1596  {
1597  WLog_Print(drdynvc->log, WLOG_WARN,
1598  "drdynvc_order_recv failed with error %" PRIu32 "!", error);
1599  }
1600 
1601  Stream_Release(data);
1602  }
1603  }
1604 
1605  {
1606  /* Disconnect remaining dynamic channels that the server did not.
1607  * This is required to properly shut down channels by calling the appropriate
1608  * event handlers. */
1609  DVCMAN* drdynvcMgr = (DVCMAN*)drdynvc->channel_mgr;
1610 
1611  HashTable_Clear(drdynvcMgr->channelsById);
1612  }
1613 
1614  if (error && drdynvc->rdpcontext)
1615  setChannelError(drdynvc->rdpcontext, error,
1616  "drdynvc_virtual_channel_client_thread reported an error");
1617 
1618  ExitThread((DWORD)error);
1619  return error;
1620 }
1621 
1622 static void drdynvc_queue_object_free(void* obj)
1623 {
1624  wStream* s = NULL;
1625  wMessage* msg = (wMessage*)obj;
1626 
1627  if (!msg || (msg->id != 0))
1628  return;
1629 
1630  s = (wStream*)msg->wParam;
1631 
1632  if (s)
1633  Stream_Release(s);
1634 }
1635 
1636 static UINT drdynvc_virtual_channel_event_initialized(drdynvcPlugin* drdynvc, LPVOID pData,
1637  UINT32 dataLength)
1638 {
1639  wObject* obj = NULL;
1640  WINPR_UNUSED(pData);
1641  WINPR_UNUSED(dataLength);
1642 
1643  if (!drdynvc)
1644  goto error;
1645 
1646  drdynvc->queue = MessageQueue_New(NULL);
1647 
1648  if (!drdynvc->queue)
1649  {
1650  WLog_Print(drdynvc->log, WLOG_ERROR, "MessageQueue_New failed!");
1651  goto error;
1652  }
1653 
1654  obj = MessageQueue_Object(drdynvc->queue);
1655  obj->fnObjectFree = drdynvc_queue_object_free;
1656  drdynvc->channel_mgr = dvcman_new(drdynvc);
1657 
1658  if (!drdynvc->channel_mgr)
1659  {
1660  WLog_Print(drdynvc->log, WLOG_ERROR, "dvcman_new failed!");
1661  goto error;
1662  }
1663 
1664  return CHANNEL_RC_OK;
1665 error:
1666  return ERROR_INTERNAL_ERROR;
1667 }
1668 
1674 static UINT drdynvc_virtual_channel_event_connected(drdynvcPlugin* drdynvc, LPVOID pData,
1675  UINT32 dataLength)
1676 {
1677  UINT error = 0;
1678  UINT32 status = 0;
1679  rdpSettings* settings = NULL;
1680 
1681  WINPR_ASSERT(drdynvc);
1682  WINPR_UNUSED(pData);
1683  WINPR_UNUSED(dataLength);
1684 
1685  if (!drdynvc)
1686  return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1687 
1688  WINPR_ASSERT(drdynvc->channelEntryPoints.pVirtualChannelOpenEx);
1689  status = drdynvc->channelEntryPoints.pVirtualChannelOpenEx(
1690  drdynvc->InitHandle, &drdynvc->OpenHandle, drdynvc->channelDef.name,
1691  drdynvc_virtual_channel_open_event_ex);
1692 
1693  if (status != CHANNEL_RC_OK)
1694  {
1695  WLog_Print(drdynvc->log, WLOG_ERROR, "pVirtualChannelOpen failed with %s [%08" PRIX32 "]",
1696  WTSErrorToString(status), status);
1697  return status;
1698  }
1699 
1700  WINPR_ASSERT(drdynvc->rdpcontext);
1701  settings = drdynvc->rdpcontext->settings;
1702  WINPR_ASSERT(settings);
1703 
1704  for (UINT32 index = 0;
1705  index < freerdp_settings_get_uint32(settings, FreeRDP_DynamicChannelCount); index++)
1706  {
1707  const ADDIN_ARGV* args =
1708  freerdp_settings_get_pointer_array(settings, FreeRDP_DynamicChannelArray, index);
1709  error = dvcman_load_addin(drdynvc, drdynvc->channel_mgr, args, drdynvc->rdpcontext);
1710 
1711  if (CHANNEL_RC_OK != error)
1712  goto error;
1713  }
1714 
1715  if ((error = dvcman_init(drdynvc, drdynvc->channel_mgr)))
1716  {
1717  WLog_Print(drdynvc->log, WLOG_ERROR, "dvcman_init failed with error %" PRIu32 "!", error);
1718  goto error;
1719  }
1720 
1721  drdynvc->state = DRDYNVC_STATE_CAPABILITIES;
1722 
1723  if (drdynvc->async)
1724  {
1725  if (!(drdynvc->thread = CreateThread(NULL, 0, drdynvc_virtual_channel_client_thread,
1726  (void*)drdynvc, 0, NULL)))
1727  {
1728  error = ERROR_INTERNAL_ERROR;
1729  WLog_Print(drdynvc->log, WLOG_ERROR, "CreateThread failed!");
1730  goto error;
1731  }
1732 
1733  if (!SetThreadPriority(drdynvc->thread, THREAD_PRIORITY_HIGHEST))
1734  WLog_Print(drdynvc->log, WLOG_WARN, "SetThreadPriority failed, ignoring.");
1735  }
1736 
1737 error:
1738  return error;
1739 }
1740 
1746 static UINT drdynvc_virtual_channel_event_disconnected(drdynvcPlugin* drdynvc)
1747 {
1748  UINT status = 0;
1749 
1750  if (!drdynvc)
1751  return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1752 
1753  if (drdynvc->OpenHandle == 0)
1754  return CHANNEL_RC_OK;
1755 
1756  if (drdynvc->queue)
1757  {
1758  if (!MessageQueue_PostQuit(drdynvc->queue, 0))
1759  {
1760  status = GetLastError();
1761  WLog_Print(drdynvc->log, WLOG_ERROR,
1762  "MessageQueue_PostQuit failed with error %" PRIu32 "", status);
1763  return status;
1764  }
1765  }
1766 
1767  if (drdynvc->thread)
1768  {
1769  if (WaitForSingleObject(drdynvc->thread, INFINITE) != WAIT_OBJECT_0)
1770  {
1771  status = GetLastError();
1772  WLog_Print(drdynvc->log, WLOG_ERROR,
1773  "WaitForSingleObject failed with error %" PRIu32 "", status);
1774  return status;
1775  }
1776 
1777  (void)CloseHandle(drdynvc->thread);
1778  drdynvc->thread = NULL;
1779  }
1780  else
1781  {
1782  {
1783  /* Disconnect remaining dynamic channels that the server did not.
1784  * This is required to properly shut down channels by calling the appropriate
1785  * event handlers. */
1786  DVCMAN* drdynvcMgr = (DVCMAN*)drdynvc->channel_mgr;
1787 
1788  HashTable_Clear(drdynvcMgr->channelsById);
1789  }
1790  }
1791 
1792  WINPR_ASSERT(drdynvc->channelEntryPoints.pVirtualChannelCloseEx);
1793  status = drdynvc->channelEntryPoints.pVirtualChannelCloseEx(drdynvc->InitHandle,
1794  drdynvc->OpenHandle);
1795 
1796  if (status != CHANNEL_RC_OK)
1797  {
1798  WLog_Print(drdynvc->log, WLOG_ERROR, "pVirtualChannelClose failed with %s [%08" PRIX32 "]",
1799  WTSErrorToString(status), status);
1800  }
1801 
1802  dvcman_clear(drdynvc, drdynvc->channel_mgr);
1803  if (drdynvc->queue)
1804  MessageQueue_Clear(drdynvc->queue);
1805  drdynvc->OpenHandle = 0;
1806 
1807  if (drdynvc->data_in)
1808  {
1809  Stream_Release(drdynvc->data_in);
1810  drdynvc->data_in = NULL;
1811  }
1812 
1813  return status;
1814 }
1815 
1821 static UINT drdynvc_virtual_channel_event_terminated(drdynvcPlugin* drdynvc)
1822 {
1823  if (!drdynvc)
1824  return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1825 
1826  MessageQueue_Free(drdynvc->queue);
1827  drdynvc->queue = NULL;
1828 
1829  if (drdynvc->channel_mgr)
1830  {
1831  dvcman_free(drdynvc, drdynvc->channel_mgr);
1832  drdynvc->channel_mgr = NULL;
1833  }
1834  drdynvc->InitHandle = 0;
1835  free(drdynvc->context);
1836  free(drdynvc);
1837  return CHANNEL_RC_OK;
1838 }
1839 
1840 static UINT drdynvc_virtual_channel_event_attached(drdynvcPlugin* drdynvc)
1841 {
1842  UINT error = CHANNEL_RC_OK;
1843  DVCMAN* dvcman = NULL;
1844 
1845  if (!drdynvc)
1846  return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1847 
1848  dvcman = (DVCMAN*)drdynvc->channel_mgr;
1849 
1850  if (!dvcman)
1851  return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1852 
1853  ArrayList_Lock(dvcman->plugins);
1854  for (size_t i = 0; i < ArrayList_Count(dvcman->plugins); i++)
1855  {
1856  IWTSPlugin* pPlugin = ArrayList_GetItem(dvcman->plugins, i);
1857 
1858  error = IFCALLRESULT(CHANNEL_RC_OK, pPlugin->Attached, pPlugin);
1859  if (error != CHANNEL_RC_OK)
1860  {
1861  WLog_Print(drdynvc->log, WLOG_ERROR, "Attach failed with error %" PRIu32 "!", error);
1862  goto fail;
1863  }
1864  }
1865 
1866 fail:
1867  ArrayList_Unlock(dvcman->plugins);
1868  return error;
1869 }
1870 
1871 static UINT drdynvc_virtual_channel_event_detached(drdynvcPlugin* drdynvc)
1872 {
1873  UINT error = CHANNEL_RC_OK;
1874  DVCMAN* dvcman = NULL;
1875 
1876  if (!drdynvc)
1877  return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1878 
1879  dvcman = (DVCMAN*)drdynvc->channel_mgr;
1880 
1881  if (!dvcman)
1882  return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1883 
1884  ArrayList_Lock(dvcman->plugins);
1885  for (size_t i = 0; i < ArrayList_Count(dvcman->plugins); i++)
1886  {
1887  IWTSPlugin* pPlugin = ArrayList_GetItem(dvcman->plugins, i);
1888 
1889  error = IFCALLRESULT(CHANNEL_RC_OK, pPlugin->Detached, pPlugin);
1890  if (error != CHANNEL_RC_OK)
1891  {
1892  WLog_Print(drdynvc->log, WLOG_ERROR, "Detach failed with error %" PRIu32 "!", error);
1893  goto fail;
1894  }
1895  }
1896 
1897 fail:
1898  ArrayList_Unlock(dvcman->plugins);
1899 
1900  return error;
1901 }
1902 
1903 static VOID VCAPITYPE drdynvc_virtual_channel_init_event_ex(LPVOID lpUserParam, LPVOID pInitHandle,
1904  UINT event, LPVOID pData,
1905  UINT dataLength)
1906 {
1907  UINT error = CHANNEL_RC_OK;
1908  drdynvcPlugin* drdynvc = (drdynvcPlugin*)lpUserParam;
1909 
1910  if (!drdynvc || (drdynvc->InitHandle != pInitHandle))
1911  {
1912  WLog_ERR(TAG, "drdynvc_virtual_channel_init_event: error no match");
1913  return;
1914  }
1915 
1916  switch (event)
1917  {
1918  case CHANNEL_EVENT_INITIALIZED:
1919  error = drdynvc_virtual_channel_event_initialized(drdynvc, pData, dataLength);
1920  break;
1921  case CHANNEL_EVENT_CONNECTED:
1922  if ((error = drdynvc_virtual_channel_event_connected(drdynvc, pData, dataLength)))
1923  WLog_Print(drdynvc->log, WLOG_ERROR,
1924  "drdynvc_virtual_channel_event_connected failed with error %" PRIu32 "",
1925  error);
1926 
1927  break;
1928 
1929  case CHANNEL_EVENT_DISCONNECTED:
1930  if ((error = drdynvc_virtual_channel_event_disconnected(drdynvc)))
1931  WLog_Print(drdynvc->log, WLOG_ERROR,
1932  "drdynvc_virtual_channel_event_disconnected failed with error %" PRIu32
1933  "",
1934  error);
1935 
1936  break;
1937 
1938  case CHANNEL_EVENT_TERMINATED:
1939  if ((error = drdynvc_virtual_channel_event_terminated(drdynvc)))
1940  WLog_Print(drdynvc->log, WLOG_ERROR,
1941  "drdynvc_virtual_channel_event_terminated failed with error %" PRIu32 "",
1942  error);
1943 
1944  break;
1945 
1946  case CHANNEL_EVENT_ATTACHED:
1947  if ((error = drdynvc_virtual_channel_event_attached(drdynvc)))
1948  WLog_Print(drdynvc->log, WLOG_ERROR,
1949  "drdynvc_virtual_channel_event_attached failed with error %" PRIu32 "",
1950  error);
1951 
1952  break;
1953 
1954  case CHANNEL_EVENT_DETACHED:
1955  if ((error = drdynvc_virtual_channel_event_detached(drdynvc)))
1956  WLog_Print(drdynvc->log, WLOG_ERROR,
1957  "drdynvc_virtual_channel_event_detached failed with error %" PRIu32 "",
1958  error);
1959 
1960  break;
1961 
1962  default:
1963  break;
1964  }
1965 
1966  if (error && drdynvc->rdpcontext)
1967  setChannelError(drdynvc->rdpcontext, error,
1968  "drdynvc_virtual_channel_init_event_ex reported an error");
1969 }
1970 
1975 static int drdynvc_get_version(DrdynvcClientContext* context)
1976 {
1977  WINPR_ASSERT(context);
1978  drdynvcPlugin* drdynvc = (drdynvcPlugin*)context->handle;
1979  WINPR_ASSERT(drdynvc);
1980  return drdynvc->version;
1981 }
1982 
1983 /* drdynvc is always built-in */
1984 #define VirtualChannelEntryEx drdynvc_VirtualChannelEntryEx
1985 
1986 FREERDP_ENTRY_POINT(BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS_EX pEntryPoints,
1987  PVOID pInitHandle))
1988 {
1989  UINT rc = 0;
1990  drdynvcPlugin* drdynvc = NULL;
1991  DrdynvcClientContext* context = NULL;
1992  CHANNEL_ENTRY_POINTS_FREERDP_EX* pEntryPointsEx = NULL;
1993  drdynvc = (drdynvcPlugin*)calloc(1, sizeof(drdynvcPlugin));
1994 
1995  WINPR_ASSERT(pEntryPoints);
1996  if (!drdynvc)
1997  {
1998  WLog_ERR(TAG, "calloc failed!");
1999  return FALSE;
2000  }
2001 
2002  drdynvc->channelDef.options =
2003  CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP | CHANNEL_OPTION_COMPRESS_RDP;
2004  (void)sprintf_s(drdynvc->channelDef.name, ARRAYSIZE(drdynvc->channelDef.name),
2005  DRDYNVC_SVC_CHANNEL_NAME);
2006  drdynvc->state = DRDYNVC_STATE_INITIAL;
2007  pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP_EX*)pEntryPoints;
2008 
2009  if ((pEntryPointsEx->cbSize >= sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX)) &&
2010  (pEntryPointsEx->MagicNumber == FREERDP_CHANNEL_MAGIC_NUMBER))
2011  {
2012  context = (DrdynvcClientContext*)calloc(1, sizeof(DrdynvcClientContext));
2013 
2014  if (!context)
2015  {
2016  WLog_Print(drdynvc->log, WLOG_ERROR, "calloc failed!");
2017  free(drdynvc);
2018  return FALSE;
2019  }
2020 
2021  context->handle = (void*)drdynvc;
2022  context->custom = NULL;
2023  drdynvc->context = context;
2024  context->GetVersion = drdynvc_get_version;
2025  drdynvc->rdpcontext = pEntryPointsEx->context;
2026  if (!freerdp_settings_get_bool(drdynvc->rdpcontext->settings,
2027  FreeRDP_TransportDumpReplay) &&
2028  !freerdp_settings_get_bool(drdynvc->rdpcontext->settings,
2029  FreeRDP_SynchronousDynamicChannels))
2030  drdynvc->async = TRUE;
2031  }
2032 
2033  drdynvc->log = WLog_Get(TAG);
2034  WLog_Print(drdynvc->log, WLOG_DEBUG, "VirtualChannelEntryEx");
2035  CopyMemory(&(drdynvc->channelEntryPoints), pEntryPoints,
2037  drdynvc->InitHandle = pInitHandle;
2038 
2039  WINPR_ASSERT(drdynvc->channelEntryPoints.pVirtualChannelInitEx);
2040  rc = drdynvc->channelEntryPoints.pVirtualChannelInitEx(
2041  drdynvc, context, pInitHandle, &drdynvc->channelDef, 1, VIRTUAL_CHANNEL_VERSION_WIN2000,
2042  drdynvc_virtual_channel_init_event_ex);
2043 
2044  if (CHANNEL_RC_OK != rc)
2045  {
2046  WLog_Print(drdynvc->log, WLOG_ERROR, "pVirtualChannelInit failed with %s [%08" PRIX32 "]",
2047  WTSErrorToString(rc), rc);
2048  free(drdynvc->context);
2049  free(drdynvc);
2050  return FALSE;
2051  }
2052 
2053  drdynvc->channelEntryPoints.pInterface = context;
2054  return TRUE;
2055 }
FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
Definition: svc.h:61
This struct contains function pointer to initialize/free objects.
Definition: collections.h:57