FreeRDP
Loading...
Searching...
No Matches
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#include <freerdp/codec/zgfx.h>
33
34#include "drdynvc_main.h"
35
36#define TAG CHANNELS_TAG("drdynvc.client")
37
38WINPR_ATTR_NODISCARD
39static const char* channel_state2str(DVC_CHANNEL_STATE state)
40{
41 switch (state)
42 {
43 case DVC_CHANNEL_INIT:
44 return "DVC_CHANNEL_INIT";
45 case DVC_CHANNEL_RUNNING:
46 return "DVC_CHANNEL_RUNNING";
47 case DVC_CHANNEL_CLOSED:
48 return "DVC_CHANNEL_CLOSED";
49 default:
50 return "DVC_CHANNEL_UNKNOWN";
51 }
52}
53
54static void dvcman_channel_free(DVCMAN_CHANNEL* channel);
55
56static UINT dvcman_channel_close(DVCMAN_CHANNEL* channel, BOOL perRequest, BOOL fromHashTableFn);
57static void dvcman_free(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr);
58
59WINPR_ATTR_NODISCARD
60static UINT drdynvc_write_data(drdynvcPlugin* drdynvc, UINT32 ChannelId, const BYTE* data,
61 UINT32 dataSize, BOOL* close, DVCMAN_CHANNEL_STATS* stats);
62
63WINPR_ATTR_NODISCARD
64static UINT drdynvc_send(drdynvcPlugin* drdynvc, wStream* s, DVCMAN_CHANNEL_STATS* stats);
65
66static void dvcman_wtslistener_free(DVCMAN_LISTENER* listener)
67{
68 if (listener)
69 free(listener->channel_name);
70 free(listener);
71}
72
78WINPR_ATTR_NODISCARD
79static UINT dvcman_get_configuration(IWTSListener* pListener, void** ppPropertyBag)
80{
81 WINPR_ASSERT(ppPropertyBag);
82 WINPR_UNUSED(pListener);
83 *ppPropertyBag = nullptr;
84 return ERROR_INTERNAL_ERROR;
85}
86
92WINPR_ATTR_NODISCARD
93static UINT dvcman_create_listener(IWTSVirtualChannelManager* pChannelMgr,
94 const char* pszChannelName, ULONG ulFlags,
95 IWTSListenerCallback* pListenerCallback,
96 IWTSListener** ppListener)
97{
98 DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
99 DVCMAN_LISTENER* listener = nullptr;
100
101 WINPR_ASSERT(dvcman);
102 WLog_DBG(TAG, "create_listener: %" PRIuz ".%s.", HashTable_Count(dvcman->listeners) + 1,
103 pszChannelName);
104 listener = (DVCMAN_LISTENER*)calloc(1, sizeof(DVCMAN_LISTENER));
105
106 if (!listener)
107 {
108 WLog_ERR(TAG, "calloc failed!");
109 return CHANNEL_RC_NO_MEMORY;
110 }
111
112 listener->iface.GetConfiguration = dvcman_get_configuration;
113 listener->iface.pInterface = nullptr;
114 listener->dvcman = dvcman;
115 listener->channel_name = _strdup(pszChannelName);
116
117 if (!listener->channel_name)
118 {
119 WLog_ERR(TAG, "_strdup failed!");
120 dvcman_wtslistener_free(listener);
121 return CHANNEL_RC_NO_MEMORY;
122 }
123
124 listener->flags = ulFlags;
125 listener->listener_callback = pListenerCallback;
126
127 if (ppListener)
128 *ppListener = (IWTSListener*)listener;
129
130 if (!HashTable_Insert(dvcman->listeners, listener->channel_name, listener))
131 {
132 dvcman_wtslistener_free(listener);
133 return ERROR_INTERNAL_ERROR;
134 }
135
136 // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): HashTable_Insert takes ownership of listener
137 return CHANNEL_RC_OK;
138}
139
140WINPR_ATTR_NODISCARD
141static UINT dvcman_destroy_listener(IWTSVirtualChannelManager* pChannelMgr, IWTSListener* pListener)
142{
143 DVCMAN_LISTENER* listener = (DVCMAN_LISTENER*)pListener;
144
145 WINPR_UNUSED(pChannelMgr);
146
147 if (listener)
148 {
149 DVCMAN* dvcman = listener->dvcman;
150 if (dvcman)
151 HashTable_Remove(dvcman->listeners, listener->channel_name);
152 }
153
154 return CHANNEL_RC_OK;
155}
156
162WINPR_ATTR_NODISCARD
163static UINT dvcman_register_plugin(IDRDYNVC_ENTRY_POINTS* pEntryPoints, const char* name,
164 IWTSPlugin* pPlugin)
165{
166 WINPR_ASSERT(pEntryPoints);
167 DVCMAN* dvcman = ((DVCMAN_ENTRY_POINTS*)pEntryPoints)->dvcman;
168
169 WINPR_ASSERT(dvcman);
170 if (!ArrayList_Append(dvcman->plugin_names, name))
171 return ERROR_INTERNAL_ERROR;
172 if (!ArrayList_Append(dvcman->plugins, pPlugin))
173 return ERROR_INTERNAL_ERROR;
174
175 WLog_DBG(TAG, "register_plugin: num_plugins %" PRIuz, ArrayList_Count(dvcman->plugins));
176 return CHANNEL_RC_OK;
177}
178
179WINPR_ATTR_NODISCARD
180static IWTSPlugin* dvcman_get_plugin(IDRDYNVC_ENTRY_POINTS* pEntryPoints, const char* name)
181{
182 IWTSPlugin* plugin = nullptr;
183 size_t nc = 0;
184 size_t pc = 0;
185 WINPR_ASSERT(pEntryPoints);
186 DVCMAN* dvcman = ((DVCMAN_ENTRY_POINTS*)pEntryPoints)->dvcman;
187 if (!dvcman || !pEntryPoints || !name)
188 return nullptr;
189
190 nc = ArrayList_Count(dvcman->plugin_names);
191 pc = ArrayList_Count(dvcman->plugins);
192 if (nc != pc)
193 return nullptr;
194
195 ArrayList_Lock(dvcman->plugin_names);
196 ArrayList_Lock(dvcman->plugins);
197 for (size_t i = 0; i < pc; i++)
198 {
199 const char* cur = ArrayList_GetItem(dvcman->plugin_names, i);
200 if (strcmp(cur, name) == 0)
201 {
202 plugin = ArrayList_GetItem(dvcman->plugins, i);
203 break;
204 }
205 }
206 ArrayList_Unlock(dvcman->plugin_names);
207 ArrayList_Unlock(dvcman->plugins);
208 return plugin;
209}
210
211WINPR_ATTR_NODISCARD
212static const ADDIN_ARGV* dvcman_get_plugin_data(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
213{
214 WINPR_ASSERT(pEntryPoints);
215 return ((DVCMAN_ENTRY_POINTS*)pEntryPoints)->args;
216}
217
218WINPR_ATTR_NODISCARD
219static rdpContext* dvcman_get_rdp_context(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
220{
221 DVCMAN_ENTRY_POINTS* entry = (DVCMAN_ENTRY_POINTS*)pEntryPoints;
222 WINPR_ASSERT(entry);
223 return entry->context;
224}
225
226WINPR_ATTR_NODISCARD
227static rdpSettings* dvcman_get_rdp_settings(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
228{
229 rdpContext* context = dvcman_get_rdp_context(pEntryPoints);
230 WINPR_ASSERT(context);
231
232 return context->settings;
233}
234
235WINPR_ATTR_NODISCARD
236static UINT32 dvcman_get_channel_id(IWTSVirtualChannel* channel)
237{
238 DVCMAN_CHANNEL* dvc = (DVCMAN_CHANNEL*)channel;
239 WINPR_ASSERT(dvc);
240 return dvc->channel_id;
241}
242
243WINPR_ATTR_NODISCARD
244static const char* dvcman_get_channel_name(IWTSVirtualChannel* channel)
245{
246 DVCMAN_CHANNEL* dvc = (DVCMAN_CHANNEL*)channel;
247 WINPR_ASSERT(dvc);
248 return dvc->channel_name;
249}
250
251WINPR_ATTR_NODISCARD
252static DVCMAN_CHANNEL* dvcman_get_channel_by_id(IWTSVirtualChannelManager* pChannelMgr,
253 UINT32 ChannelId, BOOL doRef)
254{
255 DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
256 DVCMAN_CHANNEL* dvcChannel = nullptr;
257
258 WINPR_ASSERT(dvcman);
259 HashTable_Lock(dvcman->channelsById);
260 dvcChannel = HashTable_GetItemValue(dvcman->channelsById, &ChannelId);
261 if (dvcChannel)
262 {
263 if (doRef)
264 InterlockedIncrement(&dvcChannel->refCounter);
265 }
266 else
267 HashTable_Unlock(dvcman->channelsById);
268
269 return dvcChannel;
270}
271
272static void dvcman_return_channel(IWTSVirtualChannelManager* pChannelMgr, DVCMAN_CHANNEL* channel)
273{
274 DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
275
276 if (!channel)
277 return;
278
279 WINPR_ASSERT(dvcman);
280 HashTable_Unlock(dvcman->channelsById);
281}
282
283WINPR_ATTR_NODISCARD
284static IWTSVirtualChannel* dvcman_find_channel_by_id(IWTSVirtualChannelManager* pChannelMgr,
285 UINT32 ChannelId)
286{
287 DVCMAN_CHANNEL* channel = dvcman_get_channel_by_id(pChannelMgr, ChannelId, FALSE);
288 if (!channel)
289 return nullptr;
290 dvcman_return_channel(pChannelMgr, channel);
291
292 return &channel->iface;
293}
294
295static void dvcman_plugin_terminate(void* plugin)
296{
297 IWTSPlugin* pPlugin = plugin;
298
299 WINPR_ASSERT(pPlugin);
300 UINT error = IFCALLRESULT(CHANNEL_RC_OK, pPlugin->Terminated, pPlugin);
301 if (error != CHANNEL_RC_OK)
302 WLog_ERR(TAG, "Terminated failed with error %" PRIu32 "!", error);
303}
304
305static void wts_listener_free(void* arg)
306{
307 DVCMAN_LISTENER* listener = (DVCMAN_LISTENER*)arg;
308 dvcman_wtslistener_free(listener);
309}
310
311WINPR_ATTR_NODISCARD
312static BOOL channelIdMatch(const void* k1, const void* k2)
313{
314 WINPR_ASSERT(k1);
315 WINPR_ASSERT(k2);
316 return *((const UINT32*)k1) == *((const UINT32*)k2);
317}
318
319WINPR_ATTR_NODISCARD
320static UINT32 channelIdHash(const void* id)
321{
322 WINPR_ASSERT(id);
323 return *((const UINT32*)id);
324}
325
326static void channelByIdCleanerFn(void* value)
327{
328 DVCMAN_CHANNEL* channel = (DVCMAN_CHANNEL*)value;
329 if (channel)
330 {
331 dvcman_channel_close(channel, FALSE, TRUE);
332 dvcman_channel_free(channel);
333 }
334}
335
336WINPR_ATTR_MALLOC(dvcman_free, 2)
337static IWTSVirtualChannelManager* dvcman_new(drdynvcPlugin* plugin)
338{
339 wObject* obj = nullptr;
340 DVCMAN* dvcman = (DVCMAN*)calloc(1, sizeof(DVCMAN));
341
342 if (!dvcman)
343 return nullptr;
344
345 dvcman->iface.CreateListener = dvcman_create_listener;
346 dvcman->iface.DestroyListener = dvcman_destroy_listener;
347 dvcman->iface.FindChannelById = dvcman_find_channel_by_id;
348 dvcman->iface.GetChannelId = dvcman_get_channel_id;
349 dvcman->iface.GetChannelName = dvcman_get_channel_name;
350 dvcman->drdynvc = plugin;
351 dvcman->channelsById = HashTable_New(TRUE);
352
353 if (!dvcman->channelsById)
354 goto fail;
355
356 if (!HashTable_SetHashFunction(dvcman->channelsById, channelIdHash))
357 goto fail;
358
359 obj = HashTable_KeyObject(dvcman->channelsById);
360 WINPR_ASSERT(obj);
361 obj->fnObjectEquals = channelIdMatch;
362
363 obj = HashTable_ValueObject(dvcman->channelsById);
364 WINPR_ASSERT(obj);
365 obj->fnObjectFree = channelByIdCleanerFn;
366
367 dvcman->pool = StreamPool_New(TRUE, 10);
368 if (!dvcman->pool)
369 goto fail;
370
371 dvcman->listeners = HashTable_New(TRUE);
372 if (!dvcman->listeners)
373 goto fail;
374
375 if (!HashTable_SetHashFunction(dvcman->listeners, HashTable_StringHash))
376 goto fail;
377
378 obj = HashTable_KeyObject(dvcman->listeners);
379 obj->fnObjectEquals = HashTable_StringCompare;
380
381 obj = HashTable_ValueObject(dvcman->listeners);
382 obj->fnObjectFree = wts_listener_free;
383
384 dvcman->plugin_names = ArrayList_New(TRUE);
385 if (!dvcman->plugin_names)
386 goto fail;
387 obj = ArrayList_Object(dvcman->plugin_names);
388 obj->fnObjectNew = winpr_ObjectStringClone;
389 obj->fnObjectFree = winpr_ObjectStringFree;
390
391 dvcman->plugins = ArrayList_New(TRUE);
392 if (!dvcman->plugins)
393 goto fail;
394 obj = ArrayList_Object(dvcman->plugins);
395 obj->fnObjectFree = dvcman_plugin_terminate;
396 return &dvcman->iface;
397fail:
398 dvcman_free(plugin, &dvcman->iface);
399 return nullptr;
400}
401
407WINPR_ATTR_NODISCARD
408static UINT dvcman_load_addin(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr,
409 const ADDIN_ARGV* args, rdpContext* context)
410{
411 WINPR_ASSERT(drdynvc);
412 WINPR_ASSERT(pChannelMgr);
413 WINPR_ASSERT(args);
414 WINPR_ASSERT(context);
415
416 WLog_Print(drdynvc->log, WLOG_INFO, "Loading Dynamic Virtual Channel %s", args->argv[0]);
417
418 PVIRTUALCHANNELENTRY pvce = freerdp_load_channel_addin_entry(args->argv[0], nullptr, nullptr,
419 FREERDP_ADDIN_CHANNEL_DYNAMIC);
420 PDVC_PLUGIN_ENTRY pDVCPluginEntry = WINPR_FUNC_PTR_CAST(pvce, PDVC_PLUGIN_ENTRY);
421
422 if (pDVCPluginEntry)
423 {
424 DVCMAN_ENTRY_POINTS entryPoints = WINPR_C_ARRAY_INIT;
425
426 entryPoints.iface.RegisterPlugin = dvcman_register_plugin;
427 entryPoints.iface.GetPlugin = dvcman_get_plugin;
428 entryPoints.iface.GetPluginData = dvcman_get_plugin_data;
429 entryPoints.iface.GetRdpSettings = dvcman_get_rdp_settings;
430 entryPoints.iface.GetRdpContext = dvcman_get_rdp_context;
431 entryPoints.dvcman = (DVCMAN*)pChannelMgr;
432 entryPoints.args = args;
433 entryPoints.context = context;
434 return pDVCPluginEntry(&entryPoints.iface);
435 }
436
437 return ERROR_INVALID_FUNCTION;
438}
439
440static void dvcman_channel_free(DVCMAN_CHANNEL* channel)
441{
442 if (!channel)
443 return;
444
445 if (channel->dvcman)
446 {
447 drdynvcPlugin* plugin = channel->dvcman->drdynvc;
448 if (plugin)
449 {
450 rdpContext* context = plugin->rdpcontext;
451 if (context)
452 {
453 ChannelTerminatedEventArgs e = WINPR_C_ARRAY_INIT;
454 EventArgsInit(&e, "freerdp");
455 e.name = channel->channel_name;
456 e.pInterface = channel->pInterface;
457
458 const int rc = PubSub_OnChannelTerminated(context->pubSub, context, &e);
459 if (rc < 0)
460 WLog_WARN(TAG, "PubSub_OnChannelTerminated(%s) failed", channel->channel_name);
461 }
462 }
463 }
464
465 if (channel->dvc_data)
466 Stream_Release(channel->dvc_data);
467
468 zgfx_context_free(channel->decompressor);
469 DeleteCriticalSection(&(channel->lock));
470 free(channel->channel_name);
471 free(channel);
472}
473
474static void dvcman_channel_unref(DVCMAN_CHANNEL* channel)
475{
476 WINPR_ASSERT(channel);
477 if (InterlockedDecrement(&channel->refCounter))
478 return;
479
480 DVCMAN* dvcman = channel->dvcman;
481 if (dvcman)
482 HashTable_Remove(dvcman->channelsById, &channel->channel_id);
483}
484
485WINPR_ATTR_NODISCARD
486static UINT dvcchannel_send_close(DVCMAN_CHANNEL* channel)
487{
488 WINPR_ASSERT(channel);
489 DVCMAN* dvcman = channel->dvcman;
490 drdynvcPlugin* drdynvc = dvcman->drdynvc;
491 wStream* s = StreamPool_Take(dvcman->pool, 5);
492
493 if (!s)
494 {
495 WLog_Print(drdynvc->log, WLOG_ERROR, "StreamPool_Take failed!");
496 return CHANNEL_RC_NO_MEMORY;
497 }
498
499 Stream_Write_UINT8(s, (CLOSE_REQUEST_PDU << 4) | 0x02);
500 Stream_Write_UINT32(s, channel->channel_id);
501 return drdynvc_send(drdynvc, s, &channel->stats);
502}
503
504static void check_open_close_receive(DVCMAN_CHANNEL* channel)
505{
506 WINPR_ASSERT(channel);
507
508 IWTSVirtualChannelCallback* cb = channel->channel_callback;
509 const char* name = channel->channel_name;
510 const UINT32 id = channel->channel_id;
511
512 WINPR_ASSERT(cb);
513 if (!cb->OnOpen || !cb->OnClose || !cb->OnDataReceived)
514 WLog_VRB(TAG, "{%s:%" PRIu32 "} OnOpen=%p, OnClose=%p, OnDataReceived=%p", name, id,
515 WINPR_FUNC_PTR_CAST(cb->OnOpen, const void*),
516 WINPR_FUNC_PTR_CAST(cb->OnClose, const void*),
517 WINPR_FUNC_PTR_CAST(cb->OnDataReceived, const void*));
518}
519
520WINPR_ATTR_NODISCARD
521static UINT dvcman_call_on_receive(DVCMAN_CHANNEL* channel, wStream* data)
522{
523 WINPR_ASSERT(channel);
524 WINPR_ASSERT(data);
525
526 channel->stats.packetsIn++;
527
528 IWTSVirtualChannelCallback* cb = channel->channel_callback;
529 WINPR_ASSERT(cb);
530
531 check_open_close_receive(channel);
532 WINPR_ASSERT(cb->OnDataReceived);
533 return cb->OnDataReceived(cb, data);
534}
535
536static UINT dvcman_channel_close(DVCMAN_CHANNEL* channel, BOOL perRequest, BOOL fromHashTableFn)
537{
538 UINT error = CHANNEL_RC_OK;
539 DrdynvcClientContext* context = nullptr;
540
541 WINPR_ASSERT(channel);
542 switch (channel->state)
543 {
544 case DVC_CHANNEL_INIT:
545 break;
546 case DVC_CHANNEL_RUNNING:
547 if (channel->dvcman)
548 {
549 drdynvcPlugin* drdynvc = channel->dvcman->drdynvc;
550 WINPR_ASSERT(drdynvc);
551 context = drdynvc->context;
552 if (perRequest)
553 WLog_Print(drdynvc->log, WLOG_DEBUG, "sending close confirm for '%s'",
554 channel->channel_name);
555
556 error = dvcchannel_send_close(channel);
557 if (error != CHANNEL_RC_OK)
558 {
559 if (perRequest)
560 WLog_Print(drdynvc->log, WLOG_DEBUG,
561 "error when sending closeRequest for '%s'",
562 channel->channel_name);
563 else
564 WLog_Print(drdynvc->log, WLOG_DEBUG,
565 "error when sending close confirm for '%s'",
566 channel->channel_name);
567 }
568 WLog_Print(drdynvc->log, WLOG_DEBUG, "listener %s destroyed channel %" PRIu32 "",
569 channel->channel_name, channel->channel_id);
570 }
571
572 channel->state = DVC_CHANNEL_CLOSED;
573
574 {
575 check_open_close_receive(channel);
576
577 IWTSVirtualChannelCallback* cb = channel->channel_callback;
578 channel->channel_callback = nullptr;
579 if (cb)
580 error = IFCALLRESULT(CHANNEL_RC_OK, cb->OnClose, cb);
581 }
582
583 if (channel->dvcman && channel->dvcman->drdynvc)
584 {
585 if (context)
586 {
587 IFCALLRET(context->OnChannelDisconnected, error, context, channel->channel_name,
588 channel->pInterface);
589 }
590 }
591
592 if (!fromHashTableFn)
593 dvcman_channel_unref(channel);
594 break;
595 case DVC_CHANNEL_CLOSED:
596 break;
597 default:
598 break;
599 }
600
601 return error;
602}
603
604WINPR_ATTR_MALLOC(dvcman_channel_free, 1)
605static DVCMAN_CHANNEL* dvcman_channel_new(drdynvcPlugin* drdynvc,
606 IWTSVirtualChannelManager* pChannelMgr, UINT32 ChannelId,
607 const char* ChannelName)
608{
609 WINPR_ASSERT(drdynvc);
610 WINPR_ASSERT(pChannelMgr);
611 DVCMAN_CHANNEL* channel = (DVCMAN_CHANNEL*)calloc(1, sizeof(DVCMAN_CHANNEL));
612
613 if (!channel)
614 return nullptr;
615
616 channel->dvcman = (DVCMAN*)pChannelMgr;
617 channel->channel_id = ChannelId;
618 channel->refCounter = 1;
619 channel->state = DVC_CHANNEL_INIT;
620 channel->channel_name = _strdup(ChannelName);
621 if (!channel->channel_name)
622 goto fail;
623
624 channel->decompressor = zgfx_context_new(FALSE);
625 if (!channel->decompressor)
626 goto fail;
627
628 if (!InitializeCriticalSectionEx(&(channel->lock), 0, 0))
629 goto fail;
630
631 if (drdynvc)
632 {
633 rdpContext* context = drdynvc->rdpcontext;
634 if (context)
635 {
636 ChannelInitializedEventArgs e = WINPR_C_ARRAY_INIT;
637 EventArgsInit(&e, "freerdp");
638 e.name = channel->channel_name;
639 e.pInterface = channel->pInterface;
640
641 const int rc = PubSub_OnChannelInitialized(context->pubSub, context, &e);
642 if (rc < 0)
643 WLog_WARN(TAG, "PubSub_OnChannelInitialized(%s) failed", channel->channel_name);
644 }
645 }
646
647 return channel;
648fail:
649 dvcman_channel_free(channel);
650 return nullptr;
651}
652
653static void dvcman_clear(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr)
654{
655 DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
656
657 WINPR_ASSERT(dvcman);
658 WINPR_UNUSED(drdynvc);
659
660 HashTable_Clear(dvcman->channelsById);
661 ArrayList_Clear(dvcman->plugins);
662 ArrayList_Clear(dvcman->plugin_names);
663 HashTable_Clear(dvcman->listeners);
664}
665
666static void dvcman_free(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr)
667{
668 DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
669
670 WINPR_ASSERT(dvcman);
671 WINPR_UNUSED(drdynvc);
672
673 HashTable_Free(dvcman->channelsById);
674 ArrayList_Free(dvcman->plugins);
675 ArrayList_Free(dvcman->plugin_names);
676 HashTable_Free(dvcman->listeners);
677
678 StreamPool_Free(dvcman->pool);
679 free(dvcman);
680}
681
687WINPR_ATTR_NODISCARD
688static UINT dvcman_init(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr)
689{
690 DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
691 UINT error = CHANNEL_RC_OK;
692
693 WINPR_ASSERT(dvcman);
694 ArrayList_Lock(dvcman->plugins);
695 for (size_t i = 0; i < ArrayList_Count(dvcman->plugins); i++)
696 {
697 IWTSPlugin* pPlugin = ArrayList_GetItem(dvcman->plugins, i);
698
699 error = IFCALLRESULT(CHANNEL_RC_OK, pPlugin->Initialize, pPlugin, pChannelMgr);
700 if (error != CHANNEL_RC_OK)
701 {
702 WLog_Print(drdynvc->log, WLOG_ERROR, "Initialize failed with error %" PRIu32 "!",
703 error);
704 goto fail;
705 }
706 }
707
708fail:
709 ArrayList_Unlock(dvcman->plugins);
710 return error;
711}
712
718WINPR_ATTR_NODISCARD
719static UINT dvcman_write_channel(IWTSVirtualChannel* pChannel, ULONG cbSize, const BYTE* pBuffer,
720 void* pReserved)
721{
722 BOOL close = FALSE;
723 UINT status = 0;
724 DVCMAN_CHANNEL* channel = (DVCMAN_CHANNEL*)pChannel;
725
726 WINPR_UNUSED(pReserved);
727 if (!channel || !channel->dvcman)
728 return CHANNEL_RC_BAD_CHANNEL;
729
730 EnterCriticalSection(&(channel->lock));
731 status = drdynvc_write_data(channel->dvcman->drdynvc, channel->channel_id, pBuffer, cbSize,
732 &close, &channel->stats);
733 LeaveCriticalSection(&(channel->lock));
734 /* Close delayed, it removes the channel struct */
735 if (close)
736 dvcman_channel_close(channel, FALSE, FALSE);
737
738 return status;
739}
740
746WINPR_ATTR_NODISCARD
747static UINT dvcman_close_channel_iface(IWTSVirtualChannel* pChannel)
748{
749 DVCMAN_CHANNEL* channel = (DVCMAN_CHANNEL*)pChannel;
750
751 if (!channel)
752 return CHANNEL_RC_BAD_CHANNEL;
753
754 WLog_DBG(TAG, "close_channel_iface: id=%" PRIu32 "", channel->channel_id);
755 return dvcman_channel_close(channel, FALSE, FALSE);
756}
757
758struct stats_collector_argument
759{
761 size_t count;
762 size_t used;
763};
764
765WINPR_ATTR_NODISCARD
766static BOOL stats_collector(WINPR_ATTR_UNUSED const void* key, void* value, void* arg)
767{
768 struct stats_collector_argument* args = arg;
769 WINPR_ASSERT(args);
770
771 DVCMAN_CHANNEL* channel = value;
772 WINPR_ASSERT(channel);
773 if (args->used >= args->count)
774 return FALSE;
775
776 DrdynvcClientChannelStat* stat = &args->stats[args->used++];
777
778 if (channel->channel_name)
779 {
780 const size_t len = strnlen(channel->channel_name, ARRAYSIZE(stat->channelName) - 1);
781 strncpy(stat->channelName, channel->channel_name, len);
782 }
783 else
784 memset(stat->channelName, 0, sizeof(stat->channelName));
785 stat->channelId = channel->channel_id;
786 stat->bytesIn = channel->stats.bytesIn;
787 stat->bytesOut = channel->stats.bytesOut;
788 stat->fragmentsIn = channel->stats.fragmentsIn;
789 stat->fragmentsOut = channel->stats.fragmentsOut;
790 stat->packetsIn = channel->stats.packetsIn;
791 stat->packetsOut = channel->stats.packetsOut;
792 return TRUE;
793}
794
795WINPR_ATTR_MALLOC(free, 1)
796static DrdynvcClientChannelStat* drdynvc_get_channel_stats(DrdynvcClientContext* context,
797 size_t* pCount)
798{
799 WINPR_ASSERT(context);
800 WINPR_ASSERT(pCount);
801
802 drdynvcPlugin* drdynvc = (drdynvcPlugin*)context->handle;
803 WINPR_ASSERT(drdynvc);
804
805 DVCMAN* dvcman = (DVCMAN*)drdynvc->channel_mgr;
806 *pCount = 0;
807 if (!dvcman)
808 return nullptr;
809
810 struct stats_collector_argument args = { .stats = nullptr, .count = 0 };
811
812 HashTable_Lock(dvcman->channelsById);
813 const size_t count = HashTable_Count(dvcman->channelsById);
814 if (count > 0)
815 {
816 args.stats = calloc(count, sizeof(DrdynvcClientChannelStat));
817 if (args.stats)
818 {
819 args.count = count;
820 const BOOL rc = HashTable_Foreach(dvcman->channelsById, stats_collector, &args);
821 if (!rc)
822 {
823 HashTable_Unlock(dvcman->channelsById);
824 free(args.stats);
825 return nullptr;
826 }
827 }
828 }
829 HashTable_Unlock(dvcman->channelsById);
830 *pCount = args.used;
831 return args.stats;
832}
833
839WINPR_ATTR_NODISCARD
840static DVCMAN_CHANNEL* dvcman_create_channel(drdynvcPlugin* drdynvc,
841 IWTSVirtualChannelManager* pChannelMgr,
842 UINT32 ChannelId, const char* ChannelName, UINT* res)
843{
844 BOOL bAccept = 0;
845 DVCMAN_CHANNEL* channel = nullptr;
846 DrdynvcClientContext* context = nullptr;
847 DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
848 IWTSVirtualChannelCallback* pCallback = nullptr;
849
850 WINPR_ASSERT(dvcman);
851 WINPR_ASSERT(res);
852
853 HashTable_Lock(dvcman->listeners);
854 DVCMAN_LISTENER* listener =
855 (DVCMAN_LISTENER*)HashTable_GetItemValue(dvcman->listeners, ChannelName);
856 if (!listener)
857 {
858 *res = ERROR_NOT_FOUND;
859 goto out;
860 }
861
862 channel = dvcman_get_channel_by_id(pChannelMgr, ChannelId, FALSE);
863 if (channel)
864 {
865 switch (channel->state)
866 {
867 case DVC_CHANNEL_RUNNING:
868 WLog_Print(drdynvc->log, WLOG_ERROR,
869 "Protocol error: Duplicated ChannelId %" PRIu32 " (%s)!", ChannelId,
870 ChannelName);
871 *res = CHANNEL_RC_ALREADY_OPEN;
872 goto out;
873
874 case DVC_CHANNEL_CLOSED:
875 case DVC_CHANNEL_INIT:
876 default:
877 {
878 WLog_Print(drdynvc->log, WLOG_ERROR, "not expecting a createChannel from state %s",
879 channel_state2str(channel->state));
880 *res = CHANNEL_RC_INITIALIZATION_ERROR;
881 goto out;
882 }
883 }
884 }
885 else
886 {
887 channel = dvcman_channel_new(drdynvc, pChannelMgr, ChannelId, ChannelName);
888 if (!channel)
889 {
890 WLog_Print(drdynvc->log, WLOG_ERROR, "dvcman_channel_new failed!");
891 *res = CHANNEL_RC_NO_MEMORY;
892 goto out;
893 }
894 HashTable_Lock(dvcman->channelsById);
895 }
896
897 if (!HashTable_Insert(dvcman->channelsById, &channel->channel_id, channel))
898 {
899 WLog_Print(drdynvc->log, WLOG_ERROR, "unable to register channel in our channel list");
900 *res = ERROR_INTERNAL_ERROR;
901 dvcman_channel_free(channel);
902 channel = nullptr;
903 goto out;
904 }
905
906 channel->iface.Write = dvcman_write_channel;
907 channel->iface.Close = dvcman_close_channel_iface;
908 bAccept = TRUE;
909
910 *res = listener->listener_callback->OnNewChannelConnection(
911 listener->listener_callback, &channel->iface, nullptr, &bAccept, &pCallback);
912
913 if (*res != CHANNEL_RC_OK)
914 {
915 WLog_Print(drdynvc->log, WLOG_ERROR,
916 "OnNewChannelConnection failed with error %" PRIu32 "!", *res);
917 *res = ERROR_INTERNAL_ERROR;
918 dvcman_channel_unref(channel);
919 channel = nullptr;
920 goto out;
921 }
922
923 if (!bAccept)
924 {
925 WLog_Print(drdynvc->log, WLOG_ERROR, "OnNewChannelConnection returned with bAccept FALSE!");
926 *res = ERROR_INTERNAL_ERROR;
927 dvcman_channel_unref(channel);
928 channel = nullptr;
929 goto out;
930 }
931
932 WLog_Print(drdynvc->log, WLOG_DEBUG, "listener %s created new channel %" PRIu32 "",
933 listener->channel_name, channel->channel_id);
934 channel->state = DVC_CHANNEL_RUNNING;
935 channel->channel_callback = pCallback;
936 channel->pInterface = listener->iface.pInterface;
937 context = dvcman->drdynvc->context;
938
939 IFCALLRET(context->OnChannelConnected, *res, context, ChannelName, listener->iface.pInterface);
940 if (*res != CHANNEL_RC_OK)
941 {
942 WLog_Print(drdynvc->log, WLOG_ERROR,
943 "context.OnChannelConnected failed with error %" PRIu32 "", *res);
944 }
945
946out:
947 HashTable_Unlock(dvcman->listeners);
948 if (channel)
949 dvcman_return_channel(drdynvc->channel_mgr, channel);
950
951 return channel;
952}
953
959WINPR_ATTR_NODISCARD
960static UINT dvcman_open_channel(drdynvcPlugin* drdynvc, DVCMAN_CHANNEL* channel)
961{
962 UINT error = CHANNEL_RC_OK;
963
964 WINPR_ASSERT(drdynvc);
965 WINPR_ASSERT(channel);
966 if (channel->state == DVC_CHANNEL_RUNNING)
967 {
968 IWTSVirtualChannelCallback* pCallback = channel->channel_callback;
969
970 if (pCallback->OnOpen)
971 {
972 check_open_close_receive(channel);
973 error = pCallback->OnOpen(pCallback);
974 if (error)
975 {
976 WLog_Print(drdynvc->log, WLOG_ERROR, "OnOpen failed with error %" PRIu32 "!",
977 error);
978 goto out;
979 }
980 }
981
982 WLog_Print(drdynvc->log, WLOG_DEBUG, "open_channel: ChannelId %" PRIu32 "",
983 channel->channel_id);
984 }
985
986out:
987 return error;
988}
989
995WINPR_ATTR_NODISCARD
996static UINT dvcman_receive_channel_data_first(DVCMAN_CHANNEL* channel, UINT32 length)
997{
998 WINPR_ASSERT(channel);
999 WINPR_ASSERT(channel->dvcman);
1000 if (channel->dvc_data)
1001 Stream_Release(channel->dvc_data);
1002
1003 channel->dvc_data = StreamPool_Take(channel->dvcman->pool, 1600);
1004
1005 if (!channel->dvc_data)
1006 {
1007 drdynvcPlugin* drdynvc = channel->dvcman->drdynvc;
1008 WLog_Print(drdynvc->log, WLOG_ERROR, "StreamPool_Take failed!");
1009 return CHANNEL_RC_NO_MEMORY;
1010 }
1011
1012 channel->dvc_data_length = length;
1013 return CHANNEL_RC_OK;
1014}
1015
1021WINPR_ATTR_NODISCARD
1022static UINT dvcman_receive_channel_data(DVCMAN_CHANNEL* channel, wStream* data,
1023 WINPR_ATTR_UNUSED UINT32 ThreadingFlags)
1024{
1025 UINT status = CHANNEL_RC_OK;
1026 size_t dataSize = Stream_GetRemainingLength(data);
1027
1028 WINPR_ASSERT(channel);
1029 WINPR_ASSERT(channel->dvcman);
1030
1031 channel->stats.bytesIn += Stream_Length(data);
1032 if (channel->dvc_data)
1033 {
1034 drdynvcPlugin* drdynvc = channel->dvcman->drdynvc;
1035
1036 /* Fragmented data */
1037 if (Stream_GetPosition(channel->dvc_data) + dataSize > channel->dvc_data_length)
1038 {
1039 WLog_Print(drdynvc->log, WLOG_ERROR, "data exceeding declared length!");
1040 Stream_Release(channel->dvc_data);
1041 channel->dvc_data = nullptr;
1042 status = ERROR_INVALID_DATA;
1043 goto out;
1044 }
1045 if (!Stream_EnsureRemainingCapacity(channel->dvc_data, dataSize))
1046 {
1047 Stream_Release(channel->dvc_data);
1048 channel->dvc_data = nullptr;
1049 status = ERROR_INVALID_DATA;
1050 goto out;
1051 }
1052
1053 Stream_Copy(data, channel->dvc_data, dataSize);
1054 channel->stats.fragmentsIn++;
1055
1056 if (Stream_GetPosition(channel->dvc_data) >= channel->dvc_data_length)
1057 {
1058 Stream_SealLength(channel->dvc_data);
1059 Stream_ResetPosition(channel->dvc_data);
1060
1061 status = dvcman_call_on_receive(channel, channel->dvc_data);
1062 Stream_Release(channel->dvc_data);
1063 channel->dvc_data = nullptr;
1064 }
1065 }
1066 else
1067 status = dvcman_call_on_receive(channel, data);
1068
1069out:
1070 return status;
1071}
1072
1073WINPR_ATTR_NODISCARD
1074static UINT8 drdynvc_write_variable_uint(wStream* s, UINT32 val)
1075{
1076 UINT8 cb = 0;
1077
1078 if (val <= 0xFF)
1079 {
1080 cb = 0;
1081 Stream_Write_UINT8(s, (UINT8)val);
1082 }
1083 else if (val <= 0xFFFF)
1084 {
1085 cb = 1;
1086 Stream_Write_UINT16(s, (UINT16)val);
1087 }
1088 else
1089 {
1090 cb = 2;
1091 Stream_Write_UINT32(s, val);
1092 }
1093
1094 return cb;
1095}
1096
1102WINPR_ATTR_NODISCARD
1103static UINT drdynvc_send(drdynvcPlugin* drdynvc, wStream* s, DVCMAN_CHANNEL_STATS* stats)
1104{
1105 UINT status = 0;
1106
1107 if (!drdynvc)
1108 status = CHANNEL_RC_BAD_CHANNEL_HANDLE;
1109 else
1110 {
1111 const size_t len = Stream_GetPosition(s);
1112
1113 if (stats)
1114 stats->bytesOut += len;
1115
1116 WINPR_ASSERT(drdynvc->channelEntryPoints.pVirtualChannelWriteEx);
1117 status = drdynvc->channelEntryPoints.pVirtualChannelWriteEx(
1118 drdynvc->InitHandle, drdynvc->OpenHandle, Stream_Buffer(s), (UINT32)len, s);
1119 }
1120
1121 switch (status)
1122 {
1123 case CHANNEL_RC_OK:
1124 return CHANNEL_RC_OK;
1125
1126 case CHANNEL_RC_NOT_CONNECTED:
1127 Stream_Release(s);
1128 return CHANNEL_RC_OK;
1129
1130 case CHANNEL_RC_BAD_CHANNEL_HANDLE:
1131 Stream_Release(s);
1132 WLog_ERR(TAG, "VirtualChannelWriteEx failed with CHANNEL_RC_BAD_CHANNEL_HANDLE");
1133 return status;
1134
1135 default:
1136 Stream_Release(s);
1137 WLog_Print(drdynvc->log, WLOG_ERROR,
1138 "VirtualChannelWriteEx failed with %s [%08" PRIX32 "]",
1139 WTSErrorToString(status), status);
1140 return status;
1141 }
1142}
1143
1149WINPR_ATTR_NODISCARD
1150static UINT drdynvc_write_data(drdynvcPlugin* drdynvc, UINT32 ChannelId, const BYTE* data,
1151 UINT32 dataSize, BOOL* close, DVCMAN_CHANNEL_STATS* stats)
1152{
1153 size_t pos = 0;
1154 UINT8 cbChId = 0;
1155 UINT8 cbLen = 0;
1156 UINT status = CHANNEL_RC_BAD_INIT_HANDLE;
1157 DVCMAN* dvcman = nullptr;
1158
1159 if (!drdynvc)
1160 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1161
1162 dvcman = (DVCMAN*)drdynvc->channel_mgr;
1163 WINPR_ASSERT(dvcman);
1164
1165 WLog_Print(drdynvc->log, WLOG_TRACE, "write_data: ChannelId=%" PRIu32 " size=%" PRIu32 "",
1166 ChannelId, dataSize);
1167 wStream* data_out = StreamPool_Take(dvcman->pool, CHANNEL_CHUNK_LENGTH);
1168
1169 if (!data_out)
1170 {
1171 WLog_Print(drdynvc->log, WLOG_ERROR, "StreamPool_Take failed!");
1172 return CHANNEL_RC_NO_MEMORY;
1173 }
1174
1175 if (!Stream_SetPosition(data_out, 1))
1176 {
1177 Stream_Release(data_out);
1178 return ERROR_INVALID_DATA;
1179 }
1180 cbChId = drdynvc_write_variable_uint(data_out, ChannelId);
1181 pos = Stream_GetPosition(data_out);
1182
1183 if (dataSize == 0)
1184 {
1185 /* TODO: shall treat that case with write(0) that do a close */
1186 *close = TRUE;
1187 Stream_Release(data_out);
1188 }
1189 else if (dataSize <= CHANNEL_CHUNK_LENGTH - pos)
1190 {
1191 Stream_ResetPosition(data_out);
1192 Stream_Write_UINT8(data_out, (DATA_PDU << 4) | cbChId);
1193 if (!Stream_SetPosition(data_out, pos))
1194 {
1195 Stream_Release(data_out);
1196 return ERROR_INVALID_DATA;
1197 }
1198 Stream_Write(data_out, data, dataSize);
1199 stats->packetsOut++;
1200 status = drdynvc_send(drdynvc, data_out, stats);
1201 }
1202 else
1203 {
1204 /* Fragment the data */
1205 cbLen = drdynvc_write_variable_uint(data_out, dataSize);
1206 pos = Stream_GetPosition(data_out);
1207 Stream_ResetPosition(data_out);
1208
1209 const INT32 pdu = (DATA_FIRST_PDU << 4) | cbChId | (cbLen << 2);
1210 Stream_Write_UINT8(data_out, WINPR_ASSERTING_INT_CAST(UINT8, pdu));
1211 if (!Stream_SetPosition(data_out, pos))
1212 {
1213 Stream_Release(data_out);
1214 return ERROR_INVALID_DATA;
1215 }
1216
1217 {
1218 WINPR_ASSERT(pos <= CHANNEL_CHUNK_LENGTH);
1219 const uint32_t chunkLength =
1220 CHANNEL_CHUNK_LENGTH - WINPR_ASSERTING_INT_CAST(uint32_t, pos);
1221 Stream_Write(data_out, data, chunkLength);
1222
1223 data += chunkLength;
1224 dataSize -= chunkLength;
1225 }
1226 if (dataSize > 0)
1227 stats->fragmentsOut++;
1228
1229 status = drdynvc_send(drdynvc, data_out, stats);
1230
1231 while (status == CHANNEL_RC_OK && dataSize > 0)
1232 {
1233 data_out = StreamPool_Take(dvcman->pool, CHANNEL_CHUNK_LENGTH);
1234
1235 if (!data_out)
1236 {
1237 WLog_Print(drdynvc->log, WLOG_ERROR, "StreamPool_Take failed!");
1238 return CHANNEL_RC_NO_MEMORY;
1239 }
1240
1241 if (!Stream_SetPosition(data_out, 1))
1242 {
1243 Stream_Release(data_out);
1244 return ERROR_INVALID_DATA;
1245 }
1246
1247 cbChId = drdynvc_write_variable_uint(data_out, ChannelId);
1248 pos = Stream_GetPosition(data_out);
1249 Stream_ResetPosition(data_out);
1250 Stream_Write_UINT8(data_out, (DATA_PDU << 4) | cbChId);
1251 if (!Stream_SetPosition(data_out, pos))
1252 {
1253 Stream_Release(data_out);
1254 return ERROR_INVALID_DATA;
1255 }
1256
1257 uint32_t chunkLength = dataSize;
1258
1259 WINPR_ASSERT(pos <= CHANNEL_CHUNK_LENGTH);
1260 const uint32_t clen = CHANNEL_CHUNK_LENGTH - WINPR_ASSERTING_INT_CAST(uint32_t, pos);
1261 if (chunkLength > clen)
1262 {
1263 stats->fragmentsOut++;
1264 chunkLength = clen;
1265 }
1266 else
1267 stats->packetsOut++;
1268
1269 Stream_Write(data_out, data, chunkLength);
1270 data += chunkLength;
1271 dataSize -= chunkLength;
1272
1273 status = drdynvc_send(drdynvc, data_out, stats);
1274 }
1275 }
1276
1277 if (status != CHANNEL_RC_OK)
1278 {
1279 WLog_Print(drdynvc->log, WLOG_ERROR, "VirtualChannelWriteEx failed with %s [%08" PRIX32 "]",
1280 WTSErrorToString(status), status);
1281 return status;
1282 }
1283
1284 return CHANNEL_RC_OK;
1285}
1286
1292WINPR_ATTR_NODISCARD
1293static UINT drdynvc_send_capability_response(drdynvcPlugin* drdynvc)
1294{
1295 UINT status = 0;
1296 wStream* s = nullptr;
1297 DVCMAN* dvcman = nullptr;
1298
1299 if (!drdynvc)
1300 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1301
1302 dvcman = (DVCMAN*)drdynvc->channel_mgr;
1303 WINPR_ASSERT(dvcman);
1304
1305 WLog_Print(drdynvc->log, WLOG_TRACE, "capability_response");
1306 s = StreamPool_Take(dvcman->pool, 4);
1307
1308 if (!s)
1309 {
1310 WLog_Print(drdynvc->log, WLOG_ERROR, "Stream_New failed!");
1311 return CHANNEL_RC_NO_MEMORY;
1312 }
1313
1314 Stream_Write_UINT16(s, 0x0050); /* Cmd+Sp+cbChId+Pad. Note: MSTSC sends 0x005c */
1315 Stream_Write_UINT16(s, drdynvc->version);
1316 status = drdynvc_send(drdynvc, s, nullptr);
1317
1318 if (status != CHANNEL_RC_OK)
1319 {
1320 WLog_Print(drdynvc->log, WLOG_ERROR, "VirtualChannelWriteEx failed with %s [%08" PRIX32 "]",
1321 WTSErrorToString(status), status);
1322 }
1323
1324 return status;
1325}
1326
1332WINPR_ATTR_NODISCARD
1333static UINT drdynvc_process_capability_request(drdynvcPlugin* drdynvc, int Sp, int cbChId,
1334 wStream* s)
1335{
1336 UINT status = 0;
1337
1338 if (!drdynvc)
1339 return CHANNEL_RC_BAD_INIT_HANDLE;
1340
1341 if (!Stream_CheckAndLogRequiredLength(TAG, s, 3))
1342 return ERROR_INVALID_DATA;
1343
1344 WLog_Print(drdynvc->log, WLOG_TRACE, "capability_request Sp=%d cbChId=%d", Sp, cbChId);
1345 Stream_Seek(s, 1); /* pad */
1346 Stream_Read_UINT16(s, drdynvc->version);
1347
1348 /* RDP8 servers offer version 3, though Microsoft forgot to document it
1349 * in their early documents. It behaves the same as version 2.
1350 */
1351 if ((drdynvc->version == 2) || (drdynvc->version == 3))
1352 {
1353 if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
1354 return ERROR_INVALID_DATA;
1355
1356 Stream_Read_UINT16(s, drdynvc->PriorityCharge0);
1357 Stream_Read_UINT16(s, drdynvc->PriorityCharge1);
1358 Stream_Read_UINT16(s, drdynvc->PriorityCharge2);
1359 Stream_Read_UINT16(s, drdynvc->PriorityCharge3);
1360 }
1361
1362 status = drdynvc_send_capability_response(drdynvc);
1363 drdynvc->state = DRDYNVC_STATE_READY;
1364 return status;
1365}
1366
1367WINPR_ATTR_NODISCARD
1368static UINT32 drdynvc_cblen_to_bytes(int cbLen)
1369{
1370 switch (cbLen)
1371 {
1372 case 0:
1373 return 1;
1374
1375 case 1:
1376 return 2;
1377
1378 default:
1379 return 4;
1380 }
1381}
1382
1383WINPR_ATTR_NODISCARD
1384static UINT32 drdynvc_read_variable_uint(wStream* s, int cbLen)
1385{
1386 UINT32 val = 0;
1387
1388 switch (cbLen)
1389 {
1390 case 0:
1391 Stream_Read_UINT8(s, val);
1392 break;
1393
1394 case 1:
1395 Stream_Read_UINT16(s, val);
1396 break;
1397
1398 default:
1399 Stream_Read_UINT32(s, val);
1400 break;
1401 }
1402
1403 return val;
1404}
1405
1411WINPR_ATTR_NODISCARD
1412static UINT drdynvc_process_create_request(drdynvcPlugin* drdynvc, UINT8 Sp, UINT8 cbChId,
1413 wStream* s)
1414{
1415 UINT status = 0;
1416 wStream* data_out = nullptr;
1417 UINT channel_status = 0;
1418 DVCMAN* dvcman = nullptr;
1419 DVCMAN_CHANNEL* channel = nullptr;
1420 INT32 retStatus = 0;
1421
1422 WINPR_UNUSED(Sp);
1423 if (!drdynvc)
1424 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1425
1426 dvcman = (DVCMAN*)drdynvc->channel_mgr;
1427 WINPR_ASSERT(dvcman);
1428
1429 if (drdynvc->state == DRDYNVC_STATE_CAPABILITIES)
1430 {
1436 drdynvc->version = 3;
1437
1438 if ((status = drdynvc_send_capability_response(drdynvc)))
1439 {
1440 WLog_Print(drdynvc->log, WLOG_ERROR, "drdynvc_send_capability_response failed!");
1441 return status;
1442 }
1443
1444 drdynvc->state = DRDYNVC_STATE_READY;
1445 }
1446
1447 if (!Stream_CheckAndLogRequiredLength(TAG, s, drdynvc_cblen_to_bytes(cbChId)))
1448 return ERROR_INVALID_DATA;
1449
1450 const UINT32 ChannelId = drdynvc_read_variable_uint(s, cbChId);
1451 const size_t pos = Stream_GetPosition(s);
1452 const char* name = Stream_ConstPointer(s);
1453 const size_t length = Stream_GetRemainingLength(s);
1454
1455 if (strnlen(name, length) >= length)
1456 return ERROR_INVALID_DATA;
1457
1458 WLog_Print(drdynvc->log, WLOG_DEBUG,
1459 "process_create_request: ChannelId=%" PRIu32 " ChannelName=%s", ChannelId, name);
1460
1461 data_out = StreamPool_Take(dvcman->pool, pos + 4);
1462 if (!data_out)
1463 {
1464 WLog_Print(drdynvc->log, WLOG_ERROR, "StreamPool_Take failed!");
1465 return CHANNEL_RC_NO_MEMORY;
1466 }
1467
1468 Stream_Write_UINT8(data_out, (CREATE_REQUEST_PDU << 4) | cbChId);
1469 if (!Stream_SetPosition(s, 1))
1470 return ERROR_INVALID_DATA;
1471 Stream_Copy(s, data_out, pos - 1);
1472
1473 channel =
1474 dvcman_create_channel(drdynvc, drdynvc->channel_mgr, ChannelId, name, &channel_status);
1475 switch (channel_status)
1476 {
1477 case CHANNEL_RC_OK:
1478 WLog_Print(drdynvc->log, WLOG_DEBUG, "channel created");
1479 retStatus = 0;
1480 break;
1481 case CHANNEL_RC_NO_MEMORY:
1482 WLog_Print(drdynvc->log, WLOG_DEBUG, "not enough memory for channel creation");
1483 retStatus = STATUS_NO_MEMORY;
1484 break;
1485 case ERROR_NOT_FOUND:
1486 WLog_Print(drdynvc->log, WLOG_DEBUG, "no listener for '%s'", name);
1487 retStatus = STATUS_NOT_FOUND; /* same code used by mstsc, STATUS_UNSUCCESSFUL */
1488 break;
1489 default:
1490 WLog_Print(drdynvc->log, WLOG_DEBUG, "channel creation error");
1491 retStatus = STATUS_UNSUCCESSFUL; /* same code used by mstsc, STATUS_UNSUCCESSFUL */
1492 break;
1493 }
1494 Stream_Write_INT32(data_out, retStatus);
1495
1496 status = drdynvc_send(drdynvc, data_out, nullptr);
1497 if (status != CHANNEL_RC_OK)
1498 {
1499 WLog_Print(drdynvc->log, WLOG_ERROR, "VirtualChannelWriteEx failed with %s [%08" PRIX32 "]",
1500 WTSErrorToString(status), status);
1501 if (channel_status == CHANNEL_RC_OK)
1502 dvcman_channel_unref(channel);
1503 return status;
1504 }
1505
1506 if (channel_status == CHANNEL_RC_OK)
1507 {
1508 if ((status = dvcman_open_channel(drdynvc, channel)))
1509 {
1510 WLog_Print(drdynvc->log, WLOG_ERROR,
1511 "dvcman_open_channel failed with error %" PRIu32 "!", status);
1512 return status;
1513 }
1514 }
1515
1516 return status;
1517}
1518
1524WINPR_ATTR_NODISCARD
1525static UINT drdynvc_process_data_first(drdynvcPlugin* drdynvc, int Sp, int cbChId, wStream* s,
1526 BOOL compressed, UINT32 ThreadingFlags)
1527{
1528 WINPR_ASSERT(drdynvc);
1529 if (!Stream_CheckAndLogRequiredLength(
1530 TAG, s, drdynvc_cblen_to_bytes(cbChId) + drdynvc_cblen_to_bytes(Sp)))
1531 return ERROR_INVALID_DATA;
1532
1533 UINT32 ChannelId = drdynvc_read_variable_uint(s, cbChId);
1534 UINT32 Length = drdynvc_read_variable_uint(s, Sp);
1535 WLog_Print(drdynvc->log, WLOG_TRACE,
1536 "process_data_first: Sp=%d cbChId=%d, ChannelId=%" PRIu32 " Length=%" PRIu32 "", Sp,
1537 cbChId, ChannelId, Length);
1538
1539 DVCMAN_CHANNEL* channel = dvcman_get_channel_by_id(drdynvc->channel_mgr, ChannelId, TRUE);
1540 if (!channel)
1541 {
1547 WLog_Print(drdynvc->log, WLOG_ERROR, "ChannelId %" PRIu32 " not found!", ChannelId);
1548 return CHANNEL_RC_OK;
1549 }
1550
1551 UINT status = CHANNEL_RC_OK;
1552 BOOL shouldFree = FALSE;
1553 if (channel->state != DVC_CHANNEL_RUNNING)
1554 goto out;
1555
1556 if (compressed)
1557 {
1558 BYTE* data = nullptr;
1559 UINT32 dataSize = 0;
1560 if (zgfx_decompress(channel->decompressor, Stream_Pointer(s),
1561 WINPR_ASSERTING_INT_CAST(UINT32, Stream_GetRemainingLength(s)), &data,
1562 &dataSize, 0) < 0)
1563 {
1564 status = ERROR_INVALID_DATA;
1565 WLog_Print(drdynvc->log, WLOG_ERROR, "error de-compressing first packet");
1566 goto out;
1567 }
1568
1569 if (dataSize == 0)
1570 {
1571 status = ERROR_INVALID_DATA;
1572 WLog_Print(drdynvc->log, WLOG_ERROR, "error de-compressing first packet");
1573 free(data);
1574 goto out;
1575 }
1576
1577 s = Stream_New(data, dataSize);
1578 if (!s)
1579 {
1580 status = CHANNEL_RC_NO_MEMORY;
1581 WLog_Print(drdynvc->log, WLOG_ERROR, "error allocating new Stream(len=%" PRIu32 ")",
1582 dataSize);
1583 free(data);
1584 goto out;
1585 }
1586 shouldFree = TRUE;
1587 }
1588
1589 status = dvcman_receive_channel_data_first(channel, Length);
1590
1591 if (status == CHANNEL_RC_OK)
1592 status = dvcman_receive_channel_data(channel, s, ThreadingFlags);
1593
1594 if (status != CHANNEL_RC_OK)
1595 status = dvcman_channel_close(channel, FALSE, FALSE);
1596
1597out:
1598 if (shouldFree)
1599 Stream_Free(s, TRUE);
1600 dvcman_channel_unref(channel);
1601 dvcman_return_channel(drdynvc->channel_mgr, channel);
1602 return status;
1603}
1604
1610WINPR_ATTR_NODISCARD
1611static UINT drdynvc_process_data(drdynvcPlugin* drdynvc, int Sp, int cbChId, wStream* s,
1612 BOOL compressed, UINT32 ThreadingFlags)
1613{
1614 WINPR_ASSERT(drdynvc);
1615 if (!Stream_CheckAndLogRequiredLength(TAG, s, drdynvc_cblen_to_bytes(cbChId)))
1616 return ERROR_INVALID_DATA;
1617
1618 UINT32 ChannelId = drdynvc_read_variable_uint(s, cbChId);
1619 WLog_Print(drdynvc->log, WLOG_TRACE, "process_data: Sp=%d cbChId=%d, ChannelId=%" PRIu32 "", Sp,
1620 cbChId, ChannelId);
1621
1622 DVCMAN_CHANNEL* channel = dvcman_get_channel_by_id(drdynvc->channel_mgr, ChannelId, TRUE);
1623 if (!channel)
1624 {
1630 WLog_Print(drdynvc->log, WLOG_ERROR, "ChannelId %" PRIu32 " not found!", ChannelId);
1631 return CHANNEL_RC_OK;
1632 }
1633
1634 BOOL shouldFree = FALSE;
1635 UINT status = CHANNEL_RC_OK;
1636 if (channel->state != DVC_CHANNEL_RUNNING)
1637 goto out;
1638
1639 if (compressed)
1640 {
1641 BYTE* data = nullptr;
1642 UINT32 dataSize = 0;
1643
1644 if (zgfx_decompress(channel->decompressor, Stream_Pointer(s),
1645 WINPR_ASSERTING_INT_CAST(UINT32, Stream_GetRemainingLength(s)), &data,
1646 &dataSize, 0) < 0)
1647 {
1648 status = ERROR_INVALID_DATA;
1649 WLog_Print(drdynvc->log, WLOG_ERROR, "error de-compressing data packet");
1650 free(data);
1651 goto out;
1652 }
1653
1654 if (dataSize == 0)
1655 {
1656 status = ERROR_INVALID_DATA;
1657 WLog_Print(drdynvc->log, WLOG_ERROR, "error de-compressing data packet");
1658 free(data);
1659 goto out;
1660 }
1661
1662 s = Stream_New(data, dataSize);
1663 if (!s)
1664 {
1665 status = CHANNEL_RC_NO_MEMORY;
1666 WLog_Print(drdynvc->log, WLOG_ERROR, "error allocating new Stream(len=%" PRIu32 ")",
1667 dataSize);
1668 free(data);
1669 goto out;
1670 }
1671 shouldFree = TRUE;
1672 }
1673
1674 status = dvcman_receive_channel_data(channel, s, ThreadingFlags);
1675 if (status != CHANNEL_RC_OK)
1676 status = dvcman_channel_close(channel, FALSE, FALSE);
1677
1678out:
1679 if (shouldFree)
1680 Stream_Free(s, TRUE);
1681 dvcman_channel_unref(channel);
1682 dvcman_return_channel(drdynvc->channel_mgr, channel);
1683 return status;
1684}
1685
1691WINPR_ATTR_NODISCARD
1692static UINT drdynvc_process_close_request(drdynvcPlugin* drdynvc, int Sp, int cbChId, wStream* s)
1693{
1694 UINT32 ChannelId = 0;
1695 DVCMAN_CHANNEL* channel = nullptr;
1696
1697 WINPR_ASSERT(drdynvc);
1698 if (!Stream_CheckAndLogRequiredLength(TAG, s, drdynvc_cblen_to_bytes(cbChId)))
1699 return ERROR_INVALID_DATA;
1700
1701 ChannelId = drdynvc_read_variable_uint(s, cbChId);
1702 WLog_Print(drdynvc->log, WLOG_DEBUG,
1703 "process_close_request: Sp=%d cbChId=%d, ChannelId=%" PRIu32 "", Sp, cbChId,
1704 ChannelId);
1705
1706 channel = dvcman_get_channel_by_id(drdynvc->channel_mgr, ChannelId, TRUE);
1707 if (!channel)
1708 {
1709 WLog_Print(drdynvc->log, WLOG_ERROR, "dvcman_close_request channel %" PRIu32 " not present",
1710 ChannelId);
1711 return CHANNEL_RC_OK;
1712 }
1713
1714 dvcman_channel_close(channel, TRUE, FALSE);
1715 dvcman_channel_unref(channel);
1716 dvcman_return_channel(drdynvc->channel_mgr, channel);
1717 return CHANNEL_RC_OK;
1718}
1719
1725WINPR_ATTR_NODISCARD
1726static UINT drdynvc_order_recv(drdynvcPlugin* drdynvc, wStream* s, UINT32 ThreadingFlags)
1727{
1728 WINPR_ASSERT(drdynvc);
1729 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
1730 return ERROR_INVALID_DATA;
1731
1732 UINT8 value = Stream_Get_UINT8(s);
1733 const UINT8 Cmd = (value & 0xf0) >> 4;
1734 const UINT8 Sp = (value & 0x0c) >> 2;
1735 const UINT8 cbChId = (value & 0x03) >> 0;
1736 WLog_Print(drdynvc->log, WLOG_TRACE, "order_recv: Cmd=%s, Sp=%" PRIu8 " cbChId=%" PRIu8,
1737 drdynvc_get_packet_type(Cmd), Sp, cbChId);
1738
1739 switch (Cmd)
1740 {
1741 case CAPABILITY_REQUEST_PDU:
1742 return drdynvc_process_capability_request(drdynvc, Sp, cbChId, s);
1743
1744 case CREATE_REQUEST_PDU:
1745 return drdynvc_process_create_request(drdynvc, Sp, cbChId, s);
1746
1747 case DATA_FIRST_PDU:
1748 case DATA_FIRST_COMPRESSED_PDU:
1749 return drdynvc_process_data_first(drdynvc, Sp, cbChId, s,
1750 (Cmd == DATA_FIRST_COMPRESSED_PDU), ThreadingFlags);
1751
1752 case DATA_PDU:
1753 case DATA_COMPRESSED_PDU:
1754 return drdynvc_process_data(drdynvc, Sp, cbChId, s, (Cmd == DATA_COMPRESSED_PDU),
1755 ThreadingFlags);
1756
1757 case CLOSE_REQUEST_PDU:
1758 return drdynvc_process_close_request(drdynvc, Sp, cbChId, s);
1759
1760 case SOFT_SYNC_RESPONSE_PDU:
1761 WLog_Print(drdynvc->log, WLOG_ERROR,
1762 "not expecting a SOFT_SYNC_RESPONSE_PDU as a client");
1763 return ERROR_INTERNAL_ERROR;
1764
1765 default:
1766 WLog_Print(drdynvc->log, WLOG_ERROR, "unknown drdynvc cmd 0x%x", Cmd);
1767 return ERROR_INTERNAL_ERROR;
1768 }
1769}
1770
1776WINPR_ATTR_NODISCARD
1777static UINT drdynvc_virtual_channel_event_data_received(drdynvcPlugin* drdynvc, void* pData,
1778 UINT32 dataLength, UINT32 totalLength,
1779 UINT32 dataFlags)
1780{
1781 wStream* data_in = nullptr;
1782
1783 WINPR_ASSERT(drdynvc);
1784 if ((dataFlags & CHANNEL_FLAG_SUSPEND) || (dataFlags & CHANNEL_FLAG_RESUME))
1785 {
1786 return CHANNEL_RC_OK;
1787 }
1788
1789 if (dataFlags & CHANNEL_FLAG_FIRST)
1790 {
1791 if (drdynvc->firstFlagReceived)
1792 return ERROR_INVALID_DATA;
1793 drdynvc->firstFlagReceived = TRUE;
1794
1795 DVCMAN* mgr = (DVCMAN*)drdynvc->channel_mgr;
1796 if (drdynvc->data_in)
1797 Stream_Release(drdynvc->data_in);
1798
1799 drdynvc->data_in = StreamPool_Take(mgr->pool, dataLength);
1800 drdynvc->totalLength = totalLength;
1801 }
1802
1803 if (!(data_in = drdynvc->data_in))
1804 {
1805 WLog_Print(drdynvc->log, WLOG_ERROR, "StreamPool_Take failed!");
1806 return CHANNEL_RC_NO_MEMORY;
1807 }
1808
1809 if (!Stream_EnsureRemainingCapacity(data_in, dataLength))
1810 {
1811 WLog_Print(drdynvc->log, WLOG_ERROR, "Stream_EnsureRemainingCapacity failed!");
1812 Stream_Release(drdynvc->data_in);
1813 drdynvc->data_in = nullptr;
1814 return ERROR_INTERNAL_ERROR;
1815 }
1816
1817 Stream_Write(data_in, pData, dataLength);
1818
1819 if ((Stream_GetPosition(data_in) > totalLength) || (drdynvc->totalLength != totalLength))
1820 {
1821 Stream_Release(drdynvc->data_in);
1822 drdynvc->data_in = nullptr;
1823 return ERROR_INVALID_DATA;
1824 }
1825
1826 if (dataFlags & CHANNEL_FLAG_LAST)
1827 {
1828 if (!drdynvc->firstFlagReceived)
1829 return ERROR_INVALID_DATA;
1830 drdynvc->firstFlagReceived = FALSE;
1831
1832 const size_t pos = Stream_GetPosition(data_in);
1833 if (drdynvc->totalLength != pos)
1834 {
1835 WLog_Print(drdynvc->log, WLOG_ERROR, "drdynvc_plugin_process_received: read error");
1836 return ERROR_INVALID_DATA;
1837 }
1838
1839 drdynvc->totalLength = 0;
1840 drdynvc->data_in = nullptr;
1841 Stream_SealLength(data_in);
1842 Stream_ResetPosition(data_in);
1843
1844 if (drdynvc->async)
1845 {
1846 if (!MessageQueue_Post(drdynvc->queue, nullptr, 0, (void*)data_in, nullptr))
1847 {
1848 WLog_Print(drdynvc->log, WLOG_ERROR, "MessageQueue_Post failed!");
1849 return ERROR_INTERNAL_ERROR;
1850 }
1851 }
1852 else
1853 {
1854 UINT error = drdynvc_order_recv(drdynvc, data_in, TRUE);
1855 Stream_Release(data_in);
1856
1857 if (error)
1858 {
1859 WLog_Print(drdynvc->log, WLOG_WARN,
1860 "drdynvc_order_recv failed with error %" PRIu32 "!", error);
1861 return error;
1862 }
1863 }
1864 }
1865
1866 return CHANNEL_RC_OK;
1867}
1868
1869static void VCAPITYPE drdynvc_virtual_channel_open_event_ex(LPVOID lpUserParam, DWORD openHandle,
1870 UINT event, LPVOID pData,
1871 UINT32 dataLength, UINT32 totalLength,
1872 UINT32 dataFlags)
1873{
1874 UINT error = CHANNEL_RC_OK;
1875 drdynvcPlugin* drdynvc = (drdynvcPlugin*)lpUserParam;
1876
1877 WINPR_ASSERT(drdynvc);
1878 switch (event)
1879 {
1880 case CHANNEL_EVENT_DATA_RECEIVED:
1881 if (!drdynvc || (drdynvc->OpenHandle != openHandle))
1882 {
1883 WLog_ERR(TAG, "drdynvc_virtual_channel_open_event: error no match");
1884 return;
1885 }
1886 if ((error = drdynvc_virtual_channel_event_data_received(drdynvc, pData, dataLength,
1887 totalLength, dataFlags)))
1888 WLog_Print(drdynvc->log, WLOG_ERROR,
1889 "drdynvc_virtual_channel_event_data_received failed with error %" PRIu32
1890 "",
1891 error);
1892
1893 break;
1894
1895 case CHANNEL_EVENT_WRITE_CANCELLED:
1896 case CHANNEL_EVENT_WRITE_COMPLETE:
1897 {
1898 wStream* s = (wStream*)pData;
1899 Stream_Release(s);
1900 }
1901 break;
1902
1903 case CHANNEL_EVENT_USER:
1904 break;
1905 default:
1906 break;
1907 }
1908
1909 if (error && drdynvc && drdynvc->rdpcontext)
1910 setChannelError(drdynvc->rdpcontext, error,
1911 "drdynvc_virtual_channel_open_event reported an error");
1912}
1913
1914WINPR_ATTR_NODISCARD
1915static DWORD WINAPI drdynvc_virtual_channel_client_thread(LPVOID arg)
1916{
1917 /* TODO: rewrite this */
1918 wStream* data = nullptr;
1919 wMessage message = WINPR_C_ARRAY_INIT;
1920 UINT error = CHANNEL_RC_OK;
1921 drdynvcPlugin* drdynvc = (drdynvcPlugin*)arg;
1922
1923 if (!drdynvc)
1924 {
1925 ExitThread((DWORD)CHANNEL_RC_BAD_CHANNEL_HANDLE);
1926 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1927 }
1928
1929 while (1)
1930 {
1931 if (!MessageQueue_Wait(drdynvc->queue))
1932 {
1933 WLog_Print(drdynvc->log, WLOG_ERROR, "MessageQueue_Wait failed!");
1934 error = ERROR_INTERNAL_ERROR;
1935 break;
1936 }
1937
1938 if (!MessageQueue_Peek(drdynvc->queue, &message, TRUE))
1939 {
1940 WLog_Print(drdynvc->log, WLOG_ERROR, "MessageQueue_Peek failed!");
1941 error = ERROR_INTERNAL_ERROR;
1942 break;
1943 }
1944
1945 if (message.id == WMQ_QUIT)
1946 break;
1947
1948 if (message.id == 0)
1949 {
1950 UINT32 ThreadingFlags = TRUE;
1951 data = (wStream*)message.wParam;
1952
1953 if ((error = drdynvc_order_recv(drdynvc, data, ThreadingFlags)))
1954 {
1955 WLog_Print(drdynvc->log, WLOG_WARN,
1956 "drdynvc_order_recv failed with error %" PRIu32 "!", error);
1957 }
1958
1959 Stream_Release(data);
1960 }
1961 }
1962
1963 {
1964 /* Disconnect remaining dynamic channels that the server did not.
1965 * This is required to properly shut down channels by calling the appropriate
1966 * event handlers. */
1967 DVCMAN* drdynvcMgr = (DVCMAN*)drdynvc->channel_mgr;
1968
1969 HashTable_Clear(drdynvcMgr->channelsById);
1970 }
1971
1972 if (error && drdynvc->rdpcontext)
1973 setChannelError(drdynvc->rdpcontext, error,
1974 "drdynvc_virtual_channel_client_thread reported an error");
1975
1976 ExitThread((DWORD)error);
1977 return error;
1978}
1979
1980static void drdynvc_queue_object_free(void* obj)
1981{
1982 wStream* s = nullptr;
1983 wMessage* msg = (wMessage*)obj;
1984
1985 if (!msg || (msg->id != 0))
1986 return;
1987
1988 s = (wStream*)msg->wParam;
1989
1990 if (s)
1991 Stream_Release(s);
1992}
1993
1994WINPR_ATTR_NODISCARD
1995static UINT drdynvc_virtual_channel_event_initialized(drdynvcPlugin* drdynvc, LPVOID pData,
1996 UINT32 dataLength)
1997{
1998 wObject* obj = nullptr;
1999 WINPR_UNUSED(pData);
2000 WINPR_UNUSED(dataLength);
2001
2002 if (!drdynvc)
2003 goto error;
2004
2005 drdynvc->queue = MessageQueue_New(nullptr);
2006
2007 if (!drdynvc->queue)
2008 {
2009 WLog_Print(drdynvc->log, WLOG_ERROR, "MessageQueue_New failed!");
2010 goto error;
2011 }
2012
2013 obj = MessageQueue_Object(drdynvc->queue);
2014 obj->fnObjectFree = drdynvc_queue_object_free;
2015 drdynvc->channel_mgr = dvcman_new(drdynvc);
2016
2017 if (!drdynvc->channel_mgr)
2018 {
2019 WLog_Print(drdynvc->log, WLOG_ERROR, "dvcman_new failed!");
2020 goto error;
2021 }
2022
2023 return CHANNEL_RC_OK;
2024error:
2025 return ERROR_INTERNAL_ERROR;
2026}
2027
2033WINPR_ATTR_NODISCARD
2034static UINT drdynvc_virtual_channel_event_connected(drdynvcPlugin* drdynvc, LPVOID pData,
2035 UINT32 dataLength)
2036{
2037 UINT error = 0;
2038 UINT32 status = 0;
2039 rdpSettings* settings = nullptr;
2040
2041 WINPR_ASSERT(drdynvc);
2042 WINPR_UNUSED(pData);
2043 WINPR_UNUSED(dataLength);
2044
2045 if (!drdynvc)
2046 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
2047
2048 WINPR_ASSERT(drdynvc->channelEntryPoints.pVirtualChannelOpenEx);
2049 status = drdynvc->channelEntryPoints.pVirtualChannelOpenEx(
2050 drdynvc->InitHandle, &drdynvc->OpenHandle, drdynvc->channelDef.name,
2051 drdynvc_virtual_channel_open_event_ex);
2052
2053 if (status != CHANNEL_RC_OK)
2054 {
2055 WLog_Print(drdynvc->log, WLOG_ERROR, "pVirtualChannelOpen failed with %s [%08" PRIX32 "]",
2056 WTSErrorToString(status), status);
2057 return status;
2058 }
2059
2060 WINPR_ASSERT(drdynvc->rdpcontext);
2061 settings = drdynvc->rdpcontext->settings;
2062 WINPR_ASSERT(settings);
2063
2064 for (UINT32 index = 0;
2065 index < freerdp_settings_get_uint32(settings, FreeRDP_DynamicChannelCount); index++)
2066 {
2067 const ADDIN_ARGV* args =
2068 freerdp_settings_get_pointer_array(settings, FreeRDP_DynamicChannelArray, index);
2069 error = dvcman_load_addin(drdynvc, drdynvc->channel_mgr, args, drdynvc->rdpcontext);
2070
2071 if (CHANNEL_RC_OK != error)
2072 goto error;
2073 }
2074
2075 if ((error = dvcman_init(drdynvc, drdynvc->channel_mgr)))
2076 {
2077 WLog_Print(drdynvc->log, WLOG_ERROR, "dvcman_init failed with error %" PRIu32 "!", error);
2078 goto error;
2079 }
2080
2081 drdynvc->state = DRDYNVC_STATE_CAPABILITIES;
2082
2083 if (drdynvc->async)
2084 {
2085 if (!(drdynvc->thread = CreateThread(nullptr, 0, drdynvc_virtual_channel_client_thread,
2086 (void*)drdynvc, 0, nullptr)))
2087 {
2088 error = ERROR_INTERNAL_ERROR;
2089 WLog_Print(drdynvc->log, WLOG_ERROR, "CreateThread failed!");
2090 goto error;
2091 }
2092
2093 if (!SetThreadPriority(drdynvc->thread, THREAD_PRIORITY_HIGHEST))
2094 WLog_Print(drdynvc->log, WLOG_WARN, "SetThreadPriority failed, ignoring.");
2095 }
2096
2097error:
2098 return error;
2099}
2100
2106WINPR_ATTR_NODISCARD
2107static UINT drdynvc_virtual_channel_event_disconnected(drdynvcPlugin* drdynvc)
2108{
2109 UINT status = 0;
2110
2111 if (!drdynvc)
2112 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
2113
2114 if (drdynvc->OpenHandle == 0)
2115 return CHANNEL_RC_OK;
2116
2117 if (drdynvc->queue)
2118 {
2119 if (!MessageQueue_PostQuit(drdynvc->queue, 0))
2120 {
2121 status = GetLastError();
2122 WLog_Print(drdynvc->log, WLOG_ERROR,
2123 "MessageQueue_PostQuit failed with error %" PRIu32 "", status);
2124 return status;
2125 }
2126 }
2127
2128 if (drdynvc->thread)
2129 {
2130 if (WaitForSingleObject(drdynvc->thread, INFINITE) != WAIT_OBJECT_0)
2131 {
2132 status = GetLastError();
2133 WLog_Print(drdynvc->log, WLOG_ERROR,
2134 "WaitForSingleObject failed with error %" PRIu32 "", status);
2135 return status;
2136 }
2137
2138 (void)CloseHandle(drdynvc->thread);
2139 drdynvc->thread = nullptr;
2140 }
2141 else
2142 {
2143 {
2144 /* Disconnect remaining dynamic channels that the server did not.
2145 * This is required to properly shut down channels by calling the appropriate
2146 * event handlers. */
2147 DVCMAN* drdynvcMgr = (DVCMAN*)drdynvc->channel_mgr;
2148
2149 HashTable_Clear(drdynvcMgr->channelsById);
2150 }
2151 }
2152
2153 WINPR_ASSERT(drdynvc->channelEntryPoints.pVirtualChannelCloseEx);
2154 status = drdynvc->channelEntryPoints.pVirtualChannelCloseEx(drdynvc->InitHandle,
2155 drdynvc->OpenHandle);
2156
2157 if (status != CHANNEL_RC_OK)
2158 {
2159 WLog_Print(drdynvc->log, WLOG_ERROR, "pVirtualChannelClose failed with %s [%08" PRIX32 "]",
2160 WTSErrorToString(status), status);
2161 }
2162
2163 dvcman_clear(drdynvc, drdynvc->channel_mgr);
2164 if (drdynvc->queue)
2165 MessageQueue_Clear(drdynvc->queue);
2166 drdynvc->OpenHandle = 0;
2167
2168 if (drdynvc->data_in)
2169 {
2170 Stream_Release(drdynvc->data_in);
2171 drdynvc->data_in = nullptr;
2172 }
2173
2174 return status;
2175}
2176
2182WINPR_ATTR_NODISCARD
2183static UINT drdynvc_virtual_channel_event_terminated(drdynvcPlugin* drdynvc)
2184{
2185 if (!drdynvc)
2186 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
2187
2188 MessageQueue_Free(drdynvc->queue);
2189 drdynvc->queue = nullptr;
2190
2191 if (drdynvc->channel_mgr)
2192 {
2193 dvcman_free(drdynvc, drdynvc->channel_mgr);
2194 drdynvc->channel_mgr = nullptr;
2195 }
2196 drdynvc->InitHandle = nullptr;
2197 free(drdynvc->context);
2198 free(drdynvc);
2199 return CHANNEL_RC_OK;
2200}
2201
2202WINPR_ATTR_NODISCARD
2203static UINT drdynvc_virtual_channel_event_attached(drdynvcPlugin* drdynvc)
2204{
2205 UINT error = CHANNEL_RC_OK;
2206 DVCMAN* dvcman = nullptr;
2207
2208 if (!drdynvc)
2209 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
2210
2211 dvcman = (DVCMAN*)drdynvc->channel_mgr;
2212
2213 if (!dvcman)
2214 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
2215
2216 ArrayList_Lock(dvcman->plugins);
2217 for (size_t i = 0; i < ArrayList_Count(dvcman->plugins); i++)
2218 {
2219 IWTSPlugin* pPlugin = ArrayList_GetItem(dvcman->plugins, i);
2220
2221 error = IFCALLRESULT(CHANNEL_RC_OK, pPlugin->Attached, pPlugin);
2222 if (error != CHANNEL_RC_OK)
2223 {
2224 WLog_Print(drdynvc->log, WLOG_ERROR, "Attach failed with error %" PRIu32 "!", error);
2225 goto fail;
2226 }
2227 }
2228
2229fail:
2230 ArrayList_Unlock(dvcman->plugins);
2231 return error;
2232}
2233
2234WINPR_ATTR_NODISCARD
2235static UINT drdynvc_virtual_channel_event_detached(drdynvcPlugin* drdynvc)
2236{
2237 UINT error = CHANNEL_RC_OK;
2238 DVCMAN* dvcman = nullptr;
2239
2240 if (!drdynvc)
2241 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
2242
2243 dvcman = (DVCMAN*)drdynvc->channel_mgr;
2244
2245 if (!dvcman)
2246 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
2247
2248 ArrayList_Lock(dvcman->plugins);
2249 for (size_t i = 0; i < ArrayList_Count(dvcman->plugins); i++)
2250 {
2251 IWTSPlugin* pPlugin = ArrayList_GetItem(dvcman->plugins, i);
2252
2253 error = IFCALLRESULT(CHANNEL_RC_OK, pPlugin->Detached, pPlugin);
2254 if (error != CHANNEL_RC_OK)
2255 {
2256 WLog_Print(drdynvc->log, WLOG_ERROR, "Detach failed with error %" PRIu32 "!", error);
2257 goto fail;
2258 }
2259 }
2260
2261fail:
2262 ArrayList_Unlock(dvcman->plugins);
2263
2264 return error;
2265}
2266
2267static VOID VCAPITYPE drdynvc_virtual_channel_init_event_ex(LPVOID lpUserParam, LPVOID pInitHandle,
2268 UINT event, LPVOID pData,
2269 UINT dataLength)
2270{
2271 UINT error = CHANNEL_RC_OK;
2272 drdynvcPlugin* drdynvc = (drdynvcPlugin*)lpUserParam;
2273
2274 if (!drdynvc || (drdynvc->InitHandle != pInitHandle))
2275 {
2276 WLog_ERR(TAG, "drdynvc_virtual_channel_init_event: error no match");
2277 return;
2278 }
2279
2280 switch (event)
2281 {
2282 case CHANNEL_EVENT_INITIALIZED:
2283 error = drdynvc_virtual_channel_event_initialized(drdynvc, pData, dataLength);
2284 break;
2285 case CHANNEL_EVENT_CONNECTED:
2286 if ((error = drdynvc_virtual_channel_event_connected(drdynvc, pData, dataLength)))
2287 WLog_Print(drdynvc->log, WLOG_ERROR,
2288 "drdynvc_virtual_channel_event_connected failed with error %" PRIu32 "",
2289 error);
2290
2291 break;
2292
2293 case CHANNEL_EVENT_DISCONNECTED:
2294 if ((error = drdynvc_virtual_channel_event_disconnected(drdynvc)))
2295 WLog_Print(drdynvc->log, WLOG_ERROR,
2296 "drdynvc_virtual_channel_event_disconnected failed with error %" PRIu32
2297 "",
2298 error);
2299
2300 break;
2301
2302 case CHANNEL_EVENT_TERMINATED:
2303 if ((error = drdynvc_virtual_channel_event_terminated(drdynvc)))
2304 WLog_Print(drdynvc->log, WLOG_ERROR,
2305 "drdynvc_virtual_channel_event_terminated failed with error %" PRIu32 "",
2306 error);
2307
2308 break;
2309
2310 case CHANNEL_EVENT_ATTACHED:
2311 if ((error = drdynvc_virtual_channel_event_attached(drdynvc)))
2312 WLog_Print(drdynvc->log, WLOG_ERROR,
2313 "drdynvc_virtual_channel_event_attached failed with error %" PRIu32 "",
2314 error);
2315
2316 break;
2317
2318 case CHANNEL_EVENT_DETACHED:
2319 if ((error = drdynvc_virtual_channel_event_detached(drdynvc)))
2320 WLog_Print(drdynvc->log, WLOG_ERROR,
2321 "drdynvc_virtual_channel_event_detached failed with error %" PRIu32 "",
2322 error);
2323
2324 break;
2325
2326 default:
2327 break;
2328 }
2329
2330 if (error && drdynvc->rdpcontext)
2331 setChannelError(drdynvc->rdpcontext, error,
2332 "drdynvc_virtual_channel_init_event_ex reported an error");
2333}
2334
2339WINPR_ATTR_NODISCARD
2340static int drdynvc_get_version(DrdynvcClientContext* context)
2341{
2342 WINPR_ASSERT(context);
2343 drdynvcPlugin* drdynvc = (drdynvcPlugin*)context->handle;
2344 WINPR_ASSERT(drdynvc);
2345 return drdynvc->version;
2346}
2347
2348/* drdynvc is always built-in */
2349#define VirtualChannelEntryEx drdynvc_VirtualChannelEntryEx
2350
2351FREERDP_ENTRY_POINT(BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS_EX pEntryPoints,
2352 PVOID pInitHandle))
2353{
2354 UINT rc = 0;
2355 drdynvcPlugin* drdynvc = nullptr;
2356 DrdynvcClientContext* context = nullptr;
2357 CHANNEL_ENTRY_POINTS_FREERDP_EX* pEntryPointsEx = nullptr;
2358 drdynvc = (drdynvcPlugin*)calloc(1, sizeof(drdynvcPlugin));
2359
2360 WINPR_ASSERT(pEntryPoints);
2361 if (!drdynvc)
2362 {
2363 WLog_ERR(TAG, "calloc failed!");
2364 return FALSE;
2365 }
2366
2367 drdynvc->channelDef.options =
2368 CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP | CHANNEL_OPTION_COMPRESS_RDP;
2369 (void)sprintf_s(drdynvc->channelDef.name, ARRAYSIZE(drdynvc->channelDef.name),
2370 DRDYNVC_SVC_CHANNEL_NAME);
2371 drdynvc->state = DRDYNVC_STATE_INITIAL;
2372 pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP_EX*)pEntryPoints;
2373
2374 if ((pEntryPointsEx->cbSize >= sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX)) &&
2375 (pEntryPointsEx->MagicNumber == FREERDP_CHANNEL_MAGIC_NUMBER))
2376 {
2377 context = (DrdynvcClientContext*)calloc(1, sizeof(DrdynvcClientContext));
2378
2379 if (!context)
2380 {
2381 WLog_Print(drdynvc->log, WLOG_ERROR, "calloc failed!");
2382 free(drdynvc);
2383 return FALSE;
2384 }
2385
2386 context->handle = (void*)drdynvc;
2387 context->custom = nullptr;
2388 drdynvc->context = context;
2389 context->GetVersion = drdynvc_get_version;
2390 context->GetChannelStats = drdynvc_get_channel_stats;
2391 drdynvc->rdpcontext = pEntryPointsEx->context;
2392 if (!freerdp_settings_get_bool(drdynvc->rdpcontext->settings,
2393 FreeRDP_TransportDumpReplay) &&
2394 !freerdp_settings_get_bool(drdynvc->rdpcontext->settings,
2395 FreeRDP_SynchronousDynamicChannels))
2396 drdynvc->async = TRUE;
2397 }
2398
2399 drdynvc->log = WLog_Get(TAG);
2400 WLog_Print(drdynvc->log, WLOG_DEBUG, "VirtualChannelEntryEx");
2401 CopyMemory(&(drdynvc->channelEntryPoints), pEntryPoints,
2403 drdynvc->InitHandle = pInitHandle;
2404
2405 WINPR_ASSERT(drdynvc->channelEntryPoints.pVirtualChannelInitEx);
2406 rc = drdynvc->channelEntryPoints.pVirtualChannelInitEx(
2407 drdynvc, context, pInitHandle, &drdynvc->channelDef, 1, VIRTUAL_CHANNEL_VERSION_WIN2000,
2408 drdynvc_virtual_channel_init_event_ex);
2409
2410 if (CHANNEL_RC_OK != rc)
2411 {
2412 WLog_Print(drdynvc->log, WLOG_ERROR, "pVirtualChannelInit failed with %s [%08" PRIX32 "]",
2413 WTSErrorToString(rc), rc);
2414 free(drdynvc->context);
2415 free(drdynvc);
2416 return FALSE;
2417 }
2418
2419 drdynvc->channelEntryPoints.pInterface = context;
2420 return TRUE;
2421}
WINPR_ATTR_NODISCARD FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
WINPR_ATTR_NODISCARD FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
Definition svc.h:60
Dynamic channel stats struct. Contains statistic information for a single dynamic channel.
This struct contains function pointer to initialize/free objects.
Definition collections.h:52
OBJECT_FREE_FN fnObjectFree
Definition collections.h:59
WINPR_ATTR_NODISCARD OBJECT_EQUALS_FN fnObjectEquals
Definition collections.h:61
WINPR_ATTR_NODISCARD OBJECT_NEW_FN fnObjectNew
Definition collections.h:54