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