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/multitransport.h>
31#if defined(WITH_RDPEUDP)
32#include <freerdp/channels/channels.h>
33#endif
34#include <freerdp/channels/drdynvc.h>
35#include <freerdp/utils/drdynvc.h>
36#include <freerdp/codec/zgfx.h>
37
38#include "drdynvc_main.h"
39
40#define TAG CHANNELS_TAG("drdynvc.client")
41
42/* drdynvc message queue ids */
43typedef enum WINPR_C23_ENUM_TYPE(uint32_t)
44{
45 DRDYNVC_MSG_DATA = 0,
46 DRDYNVC_MSG_TUNNEL_DATA = 1,
47 DRDYNVC_MSG_TUNNEL_STATE = 2
48} DRDYNVC_MSG_ID;
49
50/* Tunnel data held back until the Soft-Sync Request arrives, see drdynvc_receive_tunnel_pdu */
51#define DRDYNVC_MAX_TUNNEL_BACKLOG 4096
52
53/* Distinct tunnel types a Soft-Sync Response can name */
54#define DRDYNVC_MAX_SOFT_SYNC_TUNNELS 8
55
56WINPR_ATTR_NODISCARD
57static const char* channel_state2str(DVC_CHANNEL_STATE state)
58{
59 switch (state)
60 {
61 case DVC_CHANNEL_INIT:
62 return "DVC_CHANNEL_INIT";
63 case DVC_CHANNEL_RUNNING:
64 return "DVC_CHANNEL_RUNNING";
65 case DVC_CHANNEL_CLOSED:
66 return "DVC_CHANNEL_CLOSED";
67 default:
68 return "DVC_CHANNEL_UNKNOWN";
69 }
70}
71
72static void dvcman_channel_free(DVCMAN_CHANNEL* channel);
73
74static UINT dvcman_channel_close(DVCMAN_CHANNEL* channel, BOOL perRequest, BOOL fromHashTableFn);
75static void dvcman_free(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr);
76
77WINPR_ATTR_NODISCARD
78static UINT drdynvc_write_data(drdynvcPlugin* drdynvc, UINT32 ChannelId, UINT32 tunnelType,
79 const BYTE* data, UINT32 dataSize, BOOL* close,
81
82WINPR_ATTR_NODISCARD
83static UINT drdynvc_send(drdynvcPlugin* drdynvc, wStream* s, DVCMAN_CHANNEL_STATS* stats);
84WINPR_ATTR_NODISCARD
85static UINT drdynvc_send_on(drdynvcPlugin* drdynvc, wStream* s, DVCMAN_CHANNEL_STATS* stats,
86 UINT32* tunnelType, BOOL continuation);
87
88static void dvcman_wtslistener_free(DVCMAN_LISTENER* listener)
89{
90 if (listener)
91 free(listener->channel_name);
92 free(listener);
93}
94
100WINPR_ATTR_NODISCARD
101static UINT dvcman_get_configuration(IWTSListener* pListener, void** ppPropertyBag)
102{
103 WINPR_ASSERT(ppPropertyBag);
104 WINPR_UNUSED(pListener);
105 *ppPropertyBag = nullptr;
106 return ERROR_INTERNAL_ERROR;
107}
108
114WINPR_ATTR_NODISCARD
115static UINT dvcman_create_listener(IWTSVirtualChannelManager* pChannelMgr,
116 const char* pszChannelName, ULONG ulFlags,
117 IWTSListenerCallback* pListenerCallback,
118 IWTSListener** ppListener)
119{
120 DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
121 DVCMAN_LISTENER* listener = nullptr;
122
123 WINPR_ASSERT(dvcman);
124 WLog_DBG(TAG, "create_listener: %" PRIuz ".%s.", HashTable_Count(dvcman->listeners) + 1,
125 pszChannelName);
126 listener = (DVCMAN_LISTENER*)calloc(1, sizeof(DVCMAN_LISTENER));
127
128 if (!listener)
129 {
130 WLog_ERR(TAG, "calloc failed!");
131 return CHANNEL_RC_NO_MEMORY;
132 }
133
134 listener->iface.GetConfiguration = dvcman_get_configuration;
135 listener->iface.pInterface = nullptr;
136 listener->dvcman = dvcman;
137 listener->channel_name = _strdup(pszChannelName);
138
139 if (!listener->channel_name)
140 {
141 WLog_ERR(TAG, "_strdup failed!");
142 dvcman_wtslistener_free(listener);
143 return CHANNEL_RC_NO_MEMORY;
144 }
145
146 listener->flags = ulFlags;
147 listener->listener_callback = pListenerCallback;
148
149 if (ppListener)
150 *ppListener = (IWTSListener*)listener;
151
152 if (!HashTable_Insert(dvcman->listeners, listener->channel_name, listener))
153 {
154 dvcman_wtslistener_free(listener);
155 return ERROR_INTERNAL_ERROR;
156 }
157
158 // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): HashTable_Insert takes ownership of listener
159 return CHANNEL_RC_OK;
160}
161
162WINPR_ATTR_NODISCARD
163static UINT dvcman_destroy_listener(IWTSVirtualChannelManager* pChannelMgr, IWTSListener* pListener)
164{
165 DVCMAN_LISTENER* listener = (DVCMAN_LISTENER*)pListener;
166
167 WINPR_UNUSED(pChannelMgr);
168
169 if (listener)
170 {
171 DVCMAN* dvcman = listener->dvcman;
172 if (dvcman)
173 HashTable_Remove(dvcman->listeners, listener->channel_name);
174 }
175
176 return CHANNEL_RC_OK;
177}
178
184WINPR_ATTR_NODISCARD
185static UINT dvcman_register_plugin(IDRDYNVC_ENTRY_POINTS* pEntryPoints, const char* name,
186 IWTSPlugin* pPlugin)
187{
188 WINPR_ASSERT(pEntryPoints);
189 DVCMAN* dvcman = ((DVCMAN_ENTRY_POINTS*)pEntryPoints)->dvcman;
190
191 WINPR_ASSERT(dvcman);
192 if (!ArrayList_Append(dvcman->plugin_names, name))
193 return ERROR_INTERNAL_ERROR;
194 if (!ArrayList_Append(dvcman->plugins, pPlugin))
195 return ERROR_INTERNAL_ERROR;
196
197 WLog_DBG(TAG, "register_plugin: num_plugins %" PRIuz, ArrayList_Count(dvcman->plugins));
198 return CHANNEL_RC_OK;
199}
200
201WINPR_ATTR_NODISCARD
202static IWTSPlugin* dvcman_get_plugin(IDRDYNVC_ENTRY_POINTS* pEntryPoints, const char* name)
203{
204 IWTSPlugin* plugin = nullptr;
205 size_t nc = 0;
206 size_t pc = 0;
207 WINPR_ASSERT(pEntryPoints);
208 DVCMAN* dvcman = ((DVCMAN_ENTRY_POINTS*)pEntryPoints)->dvcman;
209 if (!dvcman || !pEntryPoints || !name)
210 return nullptr;
211
212 nc = ArrayList_Count(dvcman->plugin_names);
213 pc = ArrayList_Count(dvcman->plugins);
214 if (nc != pc)
215 return nullptr;
216
217 ArrayList_Lock(dvcman->plugin_names);
218 ArrayList_Lock(dvcman->plugins);
219 for (size_t i = 0; i < pc; i++)
220 {
221 const char* cur = ArrayList_GetItem(dvcman->plugin_names, i);
222 if (strcmp(cur, name) == 0)
223 {
224 plugin = ArrayList_GetItem(dvcman->plugins, i);
225 break;
226 }
227 }
228 ArrayList_Unlock(dvcman->plugin_names);
229 ArrayList_Unlock(dvcman->plugins);
230 return plugin;
231}
232
233WINPR_ATTR_NODISCARD
234static const ADDIN_ARGV* dvcman_get_plugin_data(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
235{
236 WINPR_ASSERT(pEntryPoints);
237 return ((DVCMAN_ENTRY_POINTS*)pEntryPoints)->args;
238}
239
240WINPR_ATTR_NODISCARD
241static rdpContext* dvcman_get_rdp_context(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
242{
243 DVCMAN_ENTRY_POINTS* entry = (DVCMAN_ENTRY_POINTS*)pEntryPoints;
244 WINPR_ASSERT(entry);
245 return entry->context;
246}
247
248WINPR_ATTR_NODISCARD
249static rdpSettings* dvcman_get_rdp_settings(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
250{
251 rdpContext* context = dvcman_get_rdp_context(pEntryPoints);
252 WINPR_ASSERT(context);
253
254 return context->settings;
255}
256
257WINPR_ATTR_NODISCARD
258static UINT32 dvcman_get_channel_id(IWTSVirtualChannel* channel)
259{
260 DVCMAN_CHANNEL* dvc = (DVCMAN_CHANNEL*)channel;
261 WINPR_ASSERT(dvc);
262 return dvc->channel_id;
263}
264
265WINPR_ATTR_NODISCARD
266static const char* dvcman_get_channel_name(IWTSVirtualChannel* channel)
267{
268 DVCMAN_CHANNEL* dvc = (DVCMAN_CHANNEL*)channel;
269 WINPR_ASSERT(dvc);
270 return dvc->channel_name;
271}
272
273WINPR_ATTR_NODISCARD
274static DVCMAN_CHANNEL* dvcman_get_channel_by_id(IWTSVirtualChannelManager* pChannelMgr,
275 UINT32 ChannelId, BOOL doRef)
276{
277 DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
278 DVCMAN_CHANNEL* dvcChannel = nullptr;
279
280 WINPR_ASSERT(dvcman);
281 HashTable_Lock(dvcman->channelsById);
282 dvcChannel = HashTable_GetItemValue(dvcman->channelsById, &ChannelId);
283 if (dvcChannel)
284 {
285 if (doRef)
286 InterlockedIncrement(&dvcChannel->refCounter);
287 }
288 else
289 HashTable_Unlock(dvcman->channelsById);
290
291 return dvcChannel;
292}
293
294static void dvcman_return_channel(IWTSVirtualChannelManager* pChannelMgr, DVCMAN_CHANNEL* channel)
295{
296 DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
297
298 if (!channel)
299 return;
300
301 WINPR_ASSERT(dvcman);
302 HashTable_Unlock(dvcman->channelsById);
303}
304
305WINPR_ATTR_NODISCARD
306static IWTSVirtualChannel* dvcman_find_channel_by_id(IWTSVirtualChannelManager* pChannelMgr,
307 UINT32 ChannelId)
308{
309 DVCMAN_CHANNEL* channel = dvcman_get_channel_by_id(pChannelMgr, ChannelId, FALSE);
310 if (!channel)
311 return nullptr;
312 dvcman_return_channel(pChannelMgr, channel);
313
314 return &channel->iface;
315}
316
317static void dvcman_plugin_terminate(void* plugin)
318{
319 IWTSPlugin* pPlugin = plugin;
320
321 WINPR_ASSERT(pPlugin);
322 UINT error = IFCALLRESULT(CHANNEL_RC_OK, pPlugin->Terminated, pPlugin);
323 if (error != CHANNEL_RC_OK)
324 WLog_ERR(TAG, "Terminated failed with error %" PRIu32 "!", error);
325}
326
327static void wts_listener_free(void* arg)
328{
329 DVCMAN_LISTENER* listener = (DVCMAN_LISTENER*)arg;
330 dvcman_wtslistener_free(listener);
331}
332
333WINPR_ATTR_NODISCARD
334static BOOL channelIdMatch(const void* k1, const void* k2)
335{
336 WINPR_ASSERT(k1);
337 WINPR_ASSERT(k2);
338 return *((const UINT32*)k1) == *((const UINT32*)k2);
339}
340
341WINPR_ATTR_NODISCARD
342static UINT32 channelIdHash(const void* id)
343{
344 WINPR_ASSERT(id);
345 return *((const UINT32*)id);
346}
347
348static void channelByIdCleanerFn(void* value)
349{
350 DVCMAN_CHANNEL* channel = (DVCMAN_CHANNEL*)value;
351 if (channel)
352 {
353 dvcman_channel_close(channel, FALSE, TRUE);
354 dvcman_channel_free(channel);
355 }
356}
357
358WINPR_ATTR_MALLOC(dvcman_free, 2)
359static IWTSVirtualChannelManager* dvcman_new(drdynvcPlugin* plugin)
360{
361 wObject* obj = nullptr;
362 DVCMAN* dvcman = (DVCMAN*)calloc(1, sizeof(DVCMAN));
363
364 if (!dvcman)
365 return nullptr;
366
367 dvcman->iface.CreateListener = dvcman_create_listener;
368 dvcman->iface.DestroyListener = dvcman_destroy_listener;
369 dvcman->iface.FindChannelById = dvcman_find_channel_by_id;
370 dvcman->iface.GetChannelId = dvcman_get_channel_id;
371 dvcman->iface.GetChannelName = dvcman_get_channel_name;
372 dvcman->drdynvc = plugin;
373 dvcman->channelsById = HashTable_New(TRUE);
374
375 if (!dvcman->channelsById)
376 goto fail;
377
378 if (!HashTable_SetHashFunction(dvcman->channelsById, channelIdHash))
379 goto fail;
380
381 obj = HashTable_KeyObject(dvcman->channelsById);
382 WINPR_ASSERT(obj);
383 obj->fnObjectEquals = channelIdMatch;
384
385 obj = HashTable_ValueObject(dvcman->channelsById);
386 WINPR_ASSERT(obj);
387 obj->fnObjectFree = channelByIdCleanerFn;
388
389 dvcman->pool = StreamPool_New(TRUE, 10);
390 if (!dvcman->pool)
391 goto fail;
392
393 dvcman->listeners = HashTable_New(TRUE);
394 if (!dvcman->listeners)
395 goto fail;
396
397 if (!HashTable_SetHashFunction(dvcman->listeners, HashTable_StringHash))
398 goto fail;
399
400 obj = HashTable_KeyObject(dvcman->listeners);
401 obj->fnObjectEquals = HashTable_StringCompare;
402
403 obj = HashTable_ValueObject(dvcman->listeners);
404 obj->fnObjectFree = wts_listener_free;
405
406 dvcman->plugin_names = ArrayList_New(TRUE);
407 if (!dvcman->plugin_names)
408 goto fail;
409 obj = ArrayList_Object(dvcman->plugin_names);
410 obj->fnObjectNew = winpr_ObjectStringClone;
411 obj->fnObjectFree = winpr_ObjectStringFree;
412
413 dvcman->plugins = ArrayList_New(TRUE);
414 if (!dvcman->plugins)
415 goto fail;
416 obj = ArrayList_Object(dvcman->plugins);
417 obj->fnObjectFree = dvcman_plugin_terminate;
418 return &dvcman->iface;
419fail:
420 dvcman_free(plugin, &dvcman->iface);
421 return nullptr;
422}
423
429WINPR_ATTR_NODISCARD
430static UINT dvcman_load_addin(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr,
431 const ADDIN_ARGV* args, rdpContext* context)
432{
433 WINPR_ASSERT(drdynvc);
434 WINPR_ASSERT(pChannelMgr);
435 WINPR_ASSERT(args);
436 WINPR_ASSERT(context);
437
438 WLog_Print(drdynvc->log, WLOG_INFO, "Loading Dynamic Virtual Channel %s", args->argv[0]);
439
440 PVIRTUALCHANNELENTRY pvce = freerdp_load_channel_addin_entry(args->argv[0], nullptr, nullptr,
441 FREERDP_ADDIN_CHANNEL_DYNAMIC);
442 PDVC_PLUGIN_ENTRY pDVCPluginEntry = WINPR_FUNC_PTR_CAST(pvce, PDVC_PLUGIN_ENTRY);
443
444 if (pDVCPluginEntry)
445 {
446 DVCMAN_ENTRY_POINTS entryPoints = WINPR_C_ARRAY_INIT;
447
448 entryPoints.iface.RegisterPlugin = dvcman_register_plugin;
449 entryPoints.iface.GetPlugin = dvcman_get_plugin;
450 entryPoints.iface.GetPluginData = dvcman_get_plugin_data;
451 entryPoints.iface.GetRdpSettings = dvcman_get_rdp_settings;
452 entryPoints.iface.GetRdpContext = dvcman_get_rdp_context;
453 entryPoints.dvcman = (DVCMAN*)pChannelMgr;
454 entryPoints.args = args;
455 entryPoints.context = context;
456 return pDVCPluginEntry(&entryPoints.iface);
457 }
458
459 return ERROR_INVALID_FUNCTION;
460}
461
462static void dvcman_channel_free(DVCMAN_CHANNEL* channel)
463{
464 if (!channel)
465 return;
466
467 if (channel->dvcman)
468 {
469 drdynvcPlugin* plugin = channel->dvcman->drdynvc;
470 if (plugin)
471 {
472 rdpContext* context = plugin->rdpcontext;
473 if (context)
474 {
475 ChannelTerminatedEventArgs e = WINPR_C_ARRAY_INIT;
476 EventArgsInit(&e, "freerdp");
477 e.name = channel->channel_name;
478 e.pInterface = channel->pInterface;
479
480 const int rc = PubSub_OnChannelTerminated(context->pubSub, context, &e);
481 if (rc < 0)
482 WLog_WARN(TAG, "PubSub_OnChannelTerminated(%s) failed", channel->channel_name);
483 }
484 }
485 }
486
487 if (channel->dvc_data)
488 Stream_Release(channel->dvc_data);
489
490 zgfx_context_free(channel->decompressor);
491 DeleteCriticalSection(&(channel->lock));
492 free(channel->channel_name);
493 free(channel);
494}
495
496static void dvcman_channel_unref(DVCMAN_CHANNEL* channel)
497{
498 WINPR_ASSERT(channel);
499 if (InterlockedDecrement(&channel->refCounter))
500 return;
501
502 DVCMAN* dvcman = channel->dvcman;
503 if (dvcman)
504 HashTable_Remove(dvcman->channelsById, &channel->channel_id);
505}
506
507WINPR_ATTR_NODISCARD
508static UINT32 dvcman_channel_get_tunnel(DVCMAN_CHANNEL* channel)
509{
510 WINPR_ASSERT(channel);
511 EnterCriticalSection(&channel->lock);
512 const UINT32 tunnelType = channel->tunnelType;
513 LeaveCriticalSection(&channel->lock);
514 return tunnelType;
515}
516
517/* Waits for a message the channel is writing to be out, see DVCMAN_CHANNEL::tunnelType. */
518static void dvcman_channel_set_tunnel(DVCMAN_CHANNEL* channel, UINT32 tunnelType)
519{
520 WINPR_ASSERT(channel);
521 EnterCriticalSection(&channel->lock);
522 channel->tunnelType = tunnelType;
523 LeaveCriticalSection(&channel->lock);
524}
525
526WINPR_ATTR_NODISCARD
527static UINT dvcchannel_send_close(DVCMAN_CHANNEL* channel)
528{
529 WINPR_ASSERT(channel);
530 DVCMAN* dvcman = channel->dvcman;
531 drdynvcPlugin* drdynvc = dvcman->drdynvc;
532 wStream* s = StreamPool_Take(dvcman->pool, 5);
533
534 if (!s)
535 {
536 WLog_Print(drdynvc->log, WLOG_ERROR, "StreamPool_Take failed!");
537 return CHANNEL_RC_NO_MEMORY;
538 }
539
540 Stream_Write_UINT8(s, (CLOSE_REQUEST_PDU << 4) | 0x02);
541 Stream_Write_UINT32(s, channel->channel_id);
542 UINT32 tunnelType = dvcman_channel_get_tunnel(channel);
543 return drdynvc_send_on(drdynvc, s, &channel->stats, &tunnelType, FALSE);
544}
545
546static void check_open_close_receive(DVCMAN_CHANNEL* channel)
547{
548 WINPR_ASSERT(channel);
549
550 IWTSVirtualChannelCallback* cb = channel->channel_callback;
551 const char* name = channel->channel_name;
552 const UINT32 id = channel->channel_id;
553
554 WINPR_ASSERT(cb);
555 if (!cb->OnOpen || !cb->OnClose || !cb->OnDataReceived)
556 WLog_VRB(TAG, "{%s:%" PRIu32 "} OnOpen=%p, OnClose=%p, OnDataReceived=%p", name, id,
557 WINPR_FUNC_PTR_CAST(cb->OnOpen, const void*),
558 WINPR_FUNC_PTR_CAST(cb->OnClose, const void*),
559 WINPR_FUNC_PTR_CAST(cb->OnDataReceived, const void*));
560}
561
562WINPR_ATTR_NODISCARD
563static UINT dvcman_call_on_receive(DVCMAN_CHANNEL* channel, wStream* data)
564{
565 WINPR_ASSERT(channel);
566 WINPR_ASSERT(data);
567
568 channel->stats.packetsIn++;
569
570 IWTSVirtualChannelCallback* cb = channel->channel_callback;
571 WINPR_ASSERT(cb);
572
573 check_open_close_receive(channel);
574 WINPR_ASSERT(cb->OnDataReceived);
575 return cb->OnDataReceived(cb, data);
576}
577
578static UINT dvcman_channel_close(DVCMAN_CHANNEL* channel, BOOL perRequest, BOOL fromHashTableFn)
579{
580 UINT error = CHANNEL_RC_OK;
581 DrdynvcClientContext* context = nullptr;
582
583 WINPR_ASSERT(channel);
584 switch (channel->state)
585 {
586 case DVC_CHANNEL_INIT:
587 break;
588 case DVC_CHANNEL_RUNNING:
589 if (channel->dvcman)
590 {
591 drdynvcPlugin* drdynvc = channel->dvcman->drdynvc;
592 WINPR_ASSERT(drdynvc);
593 context = drdynvc->context;
594 if (perRequest)
595 WLog_Print(drdynvc->log, WLOG_DEBUG, "sending close confirm for '%s'",
596 channel->channel_name);
597
598 error = dvcchannel_send_close(channel);
599 if (error != CHANNEL_RC_OK)
600 {
601 if (perRequest)
602 WLog_Print(drdynvc->log, WLOG_DEBUG,
603 "error when sending closeRequest for '%s'",
604 channel->channel_name);
605 else
606 WLog_Print(drdynvc->log, WLOG_DEBUG,
607 "error when sending close confirm for '%s'",
608 channel->channel_name);
609 }
610 WLog_Print(drdynvc->log, WLOG_DEBUG, "listener %s destroyed channel %" PRIu32 "",
611 channel->channel_name, channel->channel_id);
612 }
613
614 channel->state = DVC_CHANNEL_CLOSED;
615
616 {
617 check_open_close_receive(channel);
618
619 IWTSVirtualChannelCallback* cb = channel->channel_callback;
620 channel->channel_callback = nullptr;
621 if (cb)
622 error = IFCALLRESULT(CHANNEL_RC_OK, cb->OnClose, cb);
623 }
624
625#if defined(WITH_RDPEUDP)
626 /* not announced to the application, see dvcman_create_channel */
627 if (channel->staticBridge)
628 context = nullptr;
629#endif
630 if (channel->dvcman && channel->dvcman->drdynvc)
631 {
632 if (context)
633 {
634 IFCALLRET(context->OnChannelDisconnected, error, context, channel->channel_name,
635 channel->pInterface);
636 }
637 }
638
639 if (!fromHashTableFn)
640 dvcman_channel_unref(channel);
641 break;
642 case DVC_CHANNEL_CLOSED:
643 break;
644 default:
645 break;
646 }
647
648 return error;
649}
650
651WINPR_ATTR_MALLOC(dvcman_channel_free, 1)
652static DVCMAN_CHANNEL* dvcman_channel_new(drdynvcPlugin* drdynvc,
653 IWTSVirtualChannelManager* pChannelMgr, UINT32 ChannelId,
654 const char* ChannelName)
655{
656 WINPR_ASSERT(drdynvc);
657 WINPR_ASSERT(pChannelMgr);
658 DVCMAN_CHANNEL* channel = (DVCMAN_CHANNEL*)calloc(1, sizeof(DVCMAN_CHANNEL));
659
660 if (!channel)
661 return nullptr;
662
663 channel->dvcman = (DVCMAN*)pChannelMgr;
664 channel->channel_id = ChannelId;
665 channel->refCounter = 1;
666 channel->state = DVC_CHANNEL_INIT;
667 channel->channel_name = _strdup(ChannelName);
668 if (!channel->channel_name)
669 goto fail;
670
671 channel->decompressor = zgfx_context_new(FALSE);
672 if (!channel->decompressor)
673 goto fail;
674
675 if (!InitializeCriticalSectionEx(&(channel->lock), 0, 0))
676 goto fail;
677
678 if (drdynvc)
679 {
680 rdpContext* context = drdynvc->rdpcontext;
681 if (context)
682 {
683 ChannelInitializedEventArgs e = WINPR_C_ARRAY_INIT;
684 EventArgsInit(&e, "freerdp");
685 e.name = channel->channel_name;
686 e.pInterface = channel->pInterface;
687
688 const int rc = PubSub_OnChannelInitialized(context->pubSub, context, &e);
689 if (rc < 0)
690 WLog_WARN(TAG, "PubSub_OnChannelInitialized(%s) failed", channel->channel_name);
691 }
692 }
693
694 return channel;
695fail:
696 dvcman_channel_free(channel);
697 return nullptr;
698}
699
700static void dvcman_clear(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr)
701{
702 DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
703
704 WINPR_ASSERT(dvcman);
705 WINPR_UNUSED(drdynvc);
706
707 HashTable_Clear(dvcman->channelsById);
708 ArrayList_Clear(dvcman->plugins);
709 ArrayList_Clear(dvcman->plugin_names);
710 HashTable_Clear(dvcman->listeners);
711}
712
713static void dvcman_free(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr)
714{
715 DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
716
717 WINPR_ASSERT(dvcman);
718 WINPR_UNUSED(drdynvc);
719
720 HashTable_Free(dvcman->channelsById);
721 ArrayList_Free(dvcman->plugins);
722 ArrayList_Free(dvcman->plugin_names);
723 HashTable_Free(dvcman->listeners);
724
725 StreamPool_Free(dvcman->pool);
726 free(dvcman);
727}
728
734WINPR_ATTR_NODISCARD
735static UINT dvcman_init(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr)
736{
737 DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
738 UINT error = CHANNEL_RC_OK;
739
740 WINPR_ASSERT(dvcman);
741 ArrayList_Lock(dvcman->plugins);
742 for (size_t i = 0; i < ArrayList_Count(dvcman->plugins); i++)
743 {
744 IWTSPlugin* pPlugin = ArrayList_GetItem(dvcman->plugins, i);
745
746 error = IFCALLRESULT(CHANNEL_RC_OK, pPlugin->Initialize, pPlugin, pChannelMgr);
747 if (error != CHANNEL_RC_OK)
748 {
749 WLog_Print(drdynvc->log, WLOG_ERROR, "Initialize failed with error %" PRIu32 "!",
750 error);
751 goto fail;
752 }
753 }
754
755fail:
756 ArrayList_Unlock(dvcman->plugins);
757 return error;
758}
759
765WINPR_ATTR_NODISCARD
766static UINT dvcman_write_channel(IWTSVirtualChannel* pChannel, ULONG cbSize, const BYTE* pBuffer,
767 void* pReserved)
768{
769 BOOL close = FALSE;
770 UINT status = 0;
771 DVCMAN_CHANNEL* channel = (DVCMAN_CHANNEL*)pChannel;
772
773 WINPR_UNUSED(pReserved);
774 if (!channel || !channel->dvcman)
775 return CHANNEL_RC_BAD_CHANNEL;
776
777 EnterCriticalSection(&(channel->lock));
778 status = drdynvc_write_data(channel->dvcman->drdynvc, channel->channel_id, channel->tunnelType,
779 pBuffer, cbSize, &close, &channel->stats);
780 LeaveCriticalSection(&(channel->lock));
781 /* Close delayed, it removes the channel struct */
782 if (close)
783 dvcman_channel_close(channel, FALSE, FALSE);
784
785 return status;
786}
787
793WINPR_ATTR_NODISCARD
794static UINT dvcman_close_channel_iface(IWTSVirtualChannel* pChannel)
795{
796 DVCMAN_CHANNEL* channel = (DVCMAN_CHANNEL*)pChannel;
797
798 if (!channel)
799 return CHANNEL_RC_BAD_CHANNEL;
800
801 WLog_DBG(TAG, "close_channel_iface: id=%" PRIu32 "", channel->channel_id);
802 return dvcman_channel_close(channel, FALSE, FALSE);
803}
804
805struct stats_collector_argument
806{
808 size_t count;
809 size_t used;
810};
811
812WINPR_ATTR_NODISCARD
813static BOOL stats_collector(WINPR_ATTR_UNUSED const void* key, void* value, void* arg)
814{
815 struct stats_collector_argument* args = arg;
816 WINPR_ASSERT(args);
817
818 DVCMAN_CHANNEL* channel = value;
819 WINPR_ASSERT(channel);
820 if (args->used >= args->count)
821 return FALSE;
822
823 DrdynvcClientChannelStat* stat = &args->stats[args->used++];
824
825 if (channel->channel_name)
826 {
827 const size_t len = strnlen(channel->channel_name, ARRAYSIZE(stat->channelName) - 1);
828 strncpy(stat->channelName, channel->channel_name, len);
829 }
830 else
831 memset(stat->channelName, 0, sizeof(stat->channelName));
832 stat->channelId = channel->channel_id;
833 stat->bytesIn = channel->stats.bytesIn;
834 stat->bytesOut = channel->stats.bytesOut;
835 stat->fragmentsIn = channel->stats.fragmentsIn;
836 stat->fragmentsOut = channel->stats.fragmentsOut;
837 stat->packetsIn = channel->stats.packetsIn;
838 stat->packetsOut = channel->stats.packetsOut;
839 return TRUE;
840}
841
842WINPR_ATTR_MALLOC(free, 1)
843static DrdynvcClientChannelStat* drdynvc_get_channel_stats(DrdynvcClientContext* context,
844 size_t* pCount)
845{
846 WINPR_ASSERT(context);
847 WINPR_ASSERT(pCount);
848
849 drdynvcPlugin* drdynvc = (drdynvcPlugin*)context->handle;
850 WINPR_ASSERT(drdynvc);
851
852 DVCMAN* dvcman = (DVCMAN*)drdynvc->channel_mgr;
853 *pCount = 0;
854 if (!dvcman)
855 return nullptr;
856
857 struct stats_collector_argument args = { .stats = nullptr, .count = 0 };
858
859 HashTable_Lock(dvcman->channelsById);
860 const size_t count = HashTable_Count(dvcman->channelsById);
861 if (count > 0)
862 {
863 args.stats = calloc(count, sizeof(DrdynvcClientChannelStat));
864 if (args.stats)
865 {
866 args.count = count;
867 const BOOL rc = HashTable_Foreach(dvcman->channelsById, stats_collector, &args);
868 if (!rc)
869 {
870 HashTable_Unlock(dvcman->channelsById);
871 free(args.stats);
872 return nullptr;
873 }
874 }
875 }
876 HashTable_Unlock(dvcman->channelsById);
877 *pCount = args.used;
878 return args.stats;
879}
880
881#if defined(WITH_RDPEUDP)
882/* [MS-RDPEDYC] 3.1.5.4: a server tunnelling static virtual channel traffic opens a dynamic
883 * channel with the name of a static one for it, and both ends then send and receive that static
884 * channel's data on the dynamic channel. Servers have been seen doing so also when the client did
885 * not advertise TRANSPORTTYPE_UDP_PREFERRED, so with UDP multitransport any such channel is
886 * taken: what arrives on it is handed to the static channel's plugin as if it had come on the
887 * static channel, and what the plugin writes goes out on it (freerdp_channels_set_static_bridge).
888 * Windows 11 reads the clipboard's answers only there. Without UDP multitransport such a channel
889 * is refused, as any channel without a listener. */
890typedef struct
891{
892 IWTSVirtualChannelCallback iface;
893 freerdp* instance;
894 UINT16 staticChannelId;
895 char staticChannelName[CHANNEL_NAME_LEN + 1];
896
897 /* the custom of the sends it set; the static channel owns it, here it is only compared */
898 void* target;
899 /* target until dvcman_static_bridge_publish sets it, owned here until then */
900 void* pending;
901} DVCMAN_STATIC_BRIDGE;
902
903/* What the static channel's sends are passed, see dvcman_static_bridge_send. */
904typedef struct
905{
906 drdynvcPlugin* drdynvc;
907 UINT32 channelId;
908} DVCMAN_STATIC_BRIDGE_TARGET;
909
910/* [MS-RDPEDYC] 3.1.5.4.2: the static channel's data is sent on the dynamic channel that carries
911 * it. Called for each message the static channel's plugin writes; one for a dynamic channel that
912 * closed since is discarded. */
913WINPR_ATTR_NODISCARD
914static BOOL dvcman_static_bridge_send(void* custom, const BYTE* data, size_t length)
915{
916 const DVCMAN_STATIC_BRIDGE_TARGET* target = custom;
917 WINPR_ASSERT(target);
918 WINPR_ASSERT(target->drdynvc);
919
920 if (length > UINT32_MAX)
921 return FALSE;
922
923 /* the reference keeps the channel, the table is not held while it writes */
924 IWTSVirtualChannelManager* mgr = target->drdynvc->channel_mgr;
925 DVCMAN_CHANNEL* channel = dvcman_get_channel_by_id(mgr, target->channelId, TRUE);
926 if (!channel)
927 return TRUE;
928 dvcman_return_channel(mgr, channel);
929
930 UINT rc = CHANNEL_RC_OK;
931 if ((channel->state == DVC_CHANNEL_RUNNING) && channel->staticBridge)
932 rc = dvcman_write_channel(&channel->iface, (ULONG)length, data, nullptr);
933 dvcman_channel_unref(channel);
934 return rc == CHANNEL_RC_OK;
935}
936
937/* One chunk for the static channel, with the Channel PDU Header flags and total length it would
938 * have had there. A plugin that closed its static channel takes no data, which is then
939 * discarded ([MS-RDPEDYC] 3.1.5.4.5). */
940static void dvcman_static_bridge_deliver(DVCMAN_STATIC_BRIDGE* bridge, const BYTE* data,
941 size_t length, UINT32 flags, size_t totalLength)
942{
943 WINPR_ASSERT(bridge);
944
945 if ((length > 0) && !freerdp_channels_data(bridge->instance, bridge->staticChannelId, data,
946 length, flags, totalLength))
947 WLog_DBG(TAG, "static channel %" PRIu16 " took no data, %" PRIuz " bytes discarded",
948 bridge->staticChannelId, length);
949}
950
951/* dvcman_receive_channel_data hands a bridge its chunks without this, see
952 * dvcman_static_bridge_receive; anything else is one whole message. */
953WINPR_ATTR_NODISCARD
954static UINT dvcman_static_bridge_on_data(IWTSVirtualChannelCallback* pChannelCallback,
955 wStream* data)
956{
957 WINPR_ASSERT(data);
958 const size_t length = Stream_GetRemainingLength(data);
959 dvcman_static_bridge_deliver((DVCMAN_STATIC_BRIDGE*)pChannelCallback, Stream_ConstPointer(data),
960 length, CHANNEL_FLAG_FIRST | CHANNEL_FLAG_LAST, length);
961 return CHANNEL_RC_OK;
962}
963
964/* [MS-RDPEDYC] 3.1.5.4.2: the data is not reassembled, each chunk of the data PDUs goes to the
965 * static channel as it came so that its endpoint reassembles it. 3.1.5.4.4: CHANNEL_FLAG_FIRST
966 * comes from a DATA_FIRST PDU or a DATA PDU with a whole message, CHANNEL_FLAG_LAST from the DATA
967 * PDU that completes the message. */
968WINPR_ATTR_NODISCARD
969static UINT dvcman_static_bridge_receive(DVCMAN_CHANNEL* channel, wStream* data)
970{
971 WINPR_ASSERT(channel);
972 WINPR_ASSERT(data);
973
974 const size_t length = Stream_GetRemainingLength(data);
975 UINT32 flags = CHANNEL_FLAG_FIRST | CHANNEL_FLAG_LAST;
976 size_t totalLength = length;
977
978 if (channel->bridgeFragmented)
979 {
980 if (length > channel->dvc_data_length - channel->bridgeReceived)
981 {
982 WLog_Print(channel->dvcman->drdynvc->log, WLOG_ERROR,
983 "data exceeding declared length!");
984 channel->bridgeFragmented = FALSE;
985 return ERROR_INVALID_DATA;
986 }
987
988 flags = (channel->bridgeReceived == 0) ? CHANNEL_FLAG_FIRST : 0;
989 channel->bridgeReceived += (UINT32)length;
990 totalLength = channel->dvc_data_length;
991 channel->stats.fragmentsIn++;
992 if (channel->bridgeReceived >= channel->dvc_data_length)
993 {
994 flags |= CHANNEL_FLAG_LAST;
995 channel->bridgeFragmented = FALSE;
996 }
997 }
998 if (flags & CHANNEL_FLAG_LAST)
999 channel->stats.packetsIn++;
1000
1001 dvcman_static_bridge_deliver((DVCMAN_STATIC_BRIDGE*)channel->channel_callback,
1002 Stream_ConstPointer(data), length, flags, totalLength);
1003 return CHANNEL_RC_OK;
1004}
1005
1006WINPR_ATTR_NODISCARD
1007static UINT dvcman_static_bridge_on_close(IWTSVirtualChannelCallback* pChannelCallback)
1008{
1009 DVCMAN_STATIC_BRIDGE* bridge = (DVCMAN_STATIC_BRIDGE*)pChannelCallback;
1010 WINPR_ASSERT(bridge);
1011
1012 /* the static channel sends on itself again (3.1.5.4.5), unless it is gone already or another
1013 * dynamic channel carries it since */
1014 if (bridge->target &&
1015 !freerdp_channels_set_static_bridge(bridge->instance, bridge->staticChannelName, nullptr,
1016 nullptr, bridge->target))
1017 WLog_DBG(TAG, "static channel %s is gone already", bridge->staticChannelName);
1018 free(bridge->pending);
1019 free(bridge);
1020 return CHANNEL_RC_OK;
1021}
1022
1023/* [MS-RDPEDYC] 3.1.5.4.2: the static channel's sends move to the dynamic channel once the
1024 * Create Response is out, so none reaches the server ahead of it. */
1025static void dvcman_static_bridge_publish(drdynvcPlugin* drdynvc, DVCMAN_CHANNEL* channel)
1026{
1027 WINPR_ASSERT(drdynvc);
1028 WINPR_ASSERT(channel);
1029
1030 if (!channel->staticBridge)
1031 return;
1032 DVCMAN_STATIC_BRIDGE* bridge = (DVCMAN_STATIC_BRIDGE*)channel->channel_callback;
1033 WINPR_ASSERT(bridge);
1034 void* target = bridge->pending;
1035 bridge->pending = nullptr;
1036 if (target && freerdp_channels_set_static_bridge(bridge->instance, bridge->staticChannelName,
1037 dvcman_static_bridge_send, free, target))
1038 bridge->target = target;
1039 else
1040 {
1041 WLog_Print(drdynvc->log, WLOG_WARN,
1042 "static channel %s goes on sending on itself, the server may not read it",
1043 bridge->staticChannelName);
1044 free(target);
1045 }
1046}
1047
1048/* A bridge for a dynamic channel named like a joined static channel, nullptr for any other. */
1049WINPR_ATTR_NODISCARD
1050static DVCMAN_STATIC_BRIDGE* dvcman_static_bridge_new(drdynvcPlugin* drdynvc,
1051 const char* ChannelName)
1052{
1053 WINPR_ASSERT(drdynvc);
1054 WINPR_ASSERT(ChannelName);
1055
1056 /* the channel that carries the dynamic ones stays static (3.1.5.4.2) */
1057 if (_stricmp(ChannelName, DRDYNVC_SVC_CHANNEL_NAME) == 0)
1058 return nullptr;
1059 if (!drdynvc->rdpcontext || !drdynvc->rdpcontext->instance)
1060 return nullptr;
1061
1062 /* only along with UDP multitransport: Soft-Sync, which the tunnelling goes with (3.1.5.4.1),
1063 * is negotiated when both ends do it */
1064 const rdpSettings* settings = drdynvc->rdpcontext->settings;
1065 if (!settings || !freerdp_settings_get_bool(settings, FreeRDP_SupportMultitransport) ||
1066 ((freerdp_settings_get_uint32(settings, FreeRDP_MultitransportFlags) &
1067 SOFTSYNC_TCP_TO_UDP) == 0))
1068 return nullptr;
1069
1070 freerdp* instance = drdynvc->rdpcontext->instance;
1071 const UINT16 id = freerdp_channels_get_id_by_name(instance, ChannelName);
1072 if (id == 0)
1073 return nullptr;
1074
1075 DVCMAN_STATIC_BRIDGE* bridge = calloc(1, sizeof(DVCMAN_STATIC_BRIDGE));
1076 if (!bridge)
1077 return nullptr;
1078 bridge->iface.OnDataReceived = dvcman_static_bridge_on_data;
1079 bridge->iface.OnClose = dvcman_static_bridge_on_close;
1080 bridge->instance = instance;
1081 bridge->staticChannelId = id;
1082 (void)_snprintf(bridge->staticChannelName, sizeof(bridge->staticChannelName), "%s",
1083 ChannelName);
1084 return bridge;
1085}
1086#endif
1087
1093WINPR_ATTR_NODISCARD
1094static DVCMAN_CHANNEL* dvcman_create_channel(drdynvcPlugin* drdynvc,
1095 IWTSVirtualChannelManager* pChannelMgr,
1096 UINT32 ChannelId, const char* ChannelName, UINT* res)
1097{
1098 BOOL bAccept = 0;
1099 DrdynvcClientContext* context = nullptr;
1100 DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
1101 IWTSVirtualChannelCallback* pCallback = nullptr;
1102
1103 WINPR_ASSERT(dvcman);
1104 WINPR_ASSERT(res);
1105
1106 HashTable_Lock(dvcman->listeners);
1107 /* Refuse channels without a listener before registering anything, so a refused
1108 * ChannelId leaves no state behind and the server may reuse it. */
1109 DVCMAN_LISTENER* listener =
1110 (DVCMAN_LISTENER*)HashTable_GetItemValue(dvcman->listeners, ChannelName);
1111#if defined(WITH_RDPEUDP)
1112 /* or a static channel to carry */
1113 DVCMAN_STATIC_BRIDGE* bridge =
1114 listener ? nullptr : dvcman_static_bridge_new(drdynvc, ChannelName);
1115 if (!listener && !bridge)
1116#else
1117 if (!listener)
1118#endif
1119 {
1120 HashTable_Unlock(dvcman->listeners);
1121 *res = ERROR_NOT_FOUND;
1122 return nullptr;
1123 }
1124
1125 DVCMAN_CHANNEL* channel = dvcman_get_channel_by_id(pChannelMgr, ChannelId, FALSE);
1126 if (channel)
1127 {
1128 switch (channel->state)
1129 {
1130 case DVC_CHANNEL_RUNNING:
1131 WLog_Print(drdynvc->log, WLOG_ERROR,
1132 "Protocol error: Duplicated ChannelId %" PRIu32 " (%s)!", ChannelId,
1133 ChannelName);
1134 *res = CHANNEL_RC_ALREADY_OPEN;
1135 goto out;
1136
1137 case DVC_CHANNEL_CLOSED:
1138 case DVC_CHANNEL_INIT:
1139 default:
1140 {
1141 WLog_Print(drdynvc->log, WLOG_ERROR, "not expecting a createChannel from state %s",
1142 channel_state2str(channel->state));
1143 *res = CHANNEL_RC_INITIALIZATION_ERROR;
1144 goto out;
1145 }
1146 }
1147 }
1148 else
1149 {
1150 HashTable_Lock(dvcman->channelsById);
1151 channel = dvcman_channel_new(drdynvc, pChannelMgr, ChannelId, ChannelName);
1152 if (!channel)
1153 {
1154 WLog_Print(drdynvc->log, WLOG_ERROR, "dvcman_channel_new failed!");
1155 *res = CHANNEL_RC_NO_MEMORY;
1156 goto out;
1157 }
1158 }
1159
1160 if (!HashTable_Insert(dvcman->channelsById, &channel->channel_id, channel))
1161 {
1162 WLog_Print(drdynvc->log, WLOG_ERROR, "unable to register channel in our channel list");
1163 *res = ERROR_INTERNAL_ERROR;
1164 dvcman_channel_free(channel);
1165 channel = nullptr;
1166 goto out;
1167 }
1168
1169 channel->iface.Write = dvcman_write_channel;
1170 channel->iface.Close = dvcman_close_channel_iface;
1171 bAccept = TRUE;
1172
1173#if defined(WITH_RDPEUDP)
1174 if (bridge)
1175 {
1176 /* No OnChannelConnected: clients take the pInterface of a channel named like a static
1177 * one for that channel's interface, and a bridge has none. */
1178 WLog_Print(drdynvc->log, WLOG_INFO,
1179 "dynamic channel %" PRIu32 " carries the data of static channel %s",
1180 channel->channel_id, ChannelName);
1181 channel->staticBridge = TRUE;
1182 channel->state = DVC_CHANNEL_RUNNING;
1183 channel->channel_callback = &bridge->iface;
1184
1185 /* published once the Create Response is out, see dvcman_static_bridge_publish */
1186 DVCMAN_STATIC_BRIDGE_TARGET* target = calloc(1, sizeof(DVCMAN_STATIC_BRIDGE_TARGET));
1187 if (target)
1188 {
1189 target->drdynvc = drdynvc;
1190 target->channelId = channel->channel_id;
1191 }
1192 bridge->pending = target;
1193 bridge = nullptr;
1194 *res = CHANNEL_RC_OK;
1195 goto out;
1196 }
1197#endif
1198
1199 *res = listener->listener_callback->OnNewChannelConnection(
1200 listener->listener_callback, &channel->iface, nullptr, &bAccept, &pCallback);
1201
1202 if (*res != CHANNEL_RC_OK)
1203 {
1204 WLog_Print(drdynvc->log, WLOG_ERROR,
1205 "OnNewChannelConnection failed with error %" PRIu32 "!", *res);
1206 *res = ERROR_INTERNAL_ERROR;
1207 dvcman_channel_unref(channel);
1208 channel = nullptr;
1209 goto out;
1210 }
1211
1212 if (!bAccept)
1213 {
1214 WLog_Print(drdynvc->log, WLOG_ERROR, "OnNewChannelConnection returned with bAccept FALSE!");
1215 *res = ERROR_INTERNAL_ERROR;
1216 dvcman_channel_unref(channel);
1217 channel = nullptr;
1218 goto out;
1219 }
1220
1221 WLog_Print(drdynvc->log, WLOG_DEBUG, "listener %s created new channel %" PRIu32 "",
1222 listener->channel_name, channel->channel_id);
1223 channel->state = DVC_CHANNEL_RUNNING;
1224 channel->channel_callback = pCallback;
1225 channel->pInterface = listener->iface.pInterface;
1226 context = dvcman->drdynvc->context;
1227
1228 IFCALLRET(context->OnChannelConnected, *res, context, ChannelName, listener->iface.pInterface);
1229 if (*res != CHANNEL_RC_OK)
1230 {
1231 WLog_Print(drdynvc->log, WLOG_ERROR,
1232 "context.OnChannelConnected failed with error %" PRIu32 "", *res);
1233 }
1234
1235out:
1236 HashTable_Unlock(dvcman->listeners);
1237 HashTable_Unlock(dvcman->channelsById);
1238#if defined(WITH_RDPEUDP)
1239 free(bridge);
1240#endif
1241
1242 return channel;
1243}
1244
1250WINPR_ATTR_NODISCARD
1251static UINT dvcman_open_channel(drdynvcPlugin* drdynvc, DVCMAN_CHANNEL* channel)
1252{
1253 UINT error = CHANNEL_RC_OK;
1254
1255 WINPR_ASSERT(drdynvc);
1256 WINPR_ASSERT(channel);
1257 if (channel->state == DVC_CHANNEL_RUNNING)
1258 {
1259 IWTSVirtualChannelCallback* pCallback = channel->channel_callback;
1260
1261 if (pCallback->OnOpen)
1262 {
1263 check_open_close_receive(channel);
1264 error = pCallback->OnOpen(pCallback);
1265 if (error)
1266 {
1267 WLog_Print(drdynvc->log, WLOG_ERROR, "OnOpen failed with error %" PRIu32 "!",
1268 error);
1269 goto out;
1270 }
1271 }
1272
1273 WLog_Print(drdynvc->log, WLOG_DEBUG, "open_channel: ChannelId %" PRIu32 "",
1274 channel->channel_id);
1275 }
1276
1277out:
1278 return error;
1279}
1280
1286WINPR_ATTR_NODISCARD
1287static UINT dvcman_receive_channel_data_first(DVCMAN_CHANNEL* channel, UINT32 length)
1288{
1289 WINPR_ASSERT(channel);
1290 WINPR_ASSERT(channel->dvcman);
1291#if defined(WITH_RDPEUDP)
1292 if (channel->staticBridge)
1293 {
1294 /* not reassembled here, see dvcman_static_bridge_receive */
1295 channel->dvc_data_length = length;
1296 channel->bridgeReceived = 0;
1297 channel->bridgeFragmented = TRUE;
1298 return CHANNEL_RC_OK;
1299 }
1300#endif
1301 if (channel->dvc_data)
1302 Stream_Release(channel->dvc_data);
1303
1304 channel->dvc_data = StreamPool_Take(channel->dvcman->pool, 1600);
1305
1306 if (!channel->dvc_data)
1307 {
1308 drdynvcPlugin* drdynvc = channel->dvcman->drdynvc;
1309 WLog_Print(drdynvc->log, WLOG_ERROR, "StreamPool_Take failed!");
1310 return CHANNEL_RC_NO_MEMORY;
1311 }
1312
1313 channel->dvc_data_length = length;
1314 return CHANNEL_RC_OK;
1315}
1316
1322WINPR_ATTR_NODISCARD
1323static UINT dvcman_receive_channel_data(DVCMAN_CHANNEL* channel, wStream* data,
1324 WINPR_ATTR_UNUSED UINT32 ThreadingFlags)
1325{
1326 UINT status = CHANNEL_RC_OK;
1327 size_t dataSize = Stream_GetRemainingLength(data);
1328
1329 WINPR_ASSERT(channel);
1330 WINPR_ASSERT(channel->dvcman);
1331
1332 channel->stats.bytesIn += Stream_Length(data);
1333#if defined(WITH_RDPEUDP)
1334 if (channel->staticBridge)
1335 return dvcman_static_bridge_receive(channel, data);
1336#endif
1337 if (channel->dvc_data)
1338 {
1339 drdynvcPlugin* drdynvc = channel->dvcman->drdynvc;
1340
1341 /* Fragmented data */
1342 if (Stream_GetPosition(channel->dvc_data) + dataSize > channel->dvc_data_length)
1343 {
1344 WLog_Print(drdynvc->log, WLOG_ERROR, "data exceeding declared length!");
1345 Stream_Release(channel->dvc_data);
1346 channel->dvc_data = nullptr;
1347 status = ERROR_INVALID_DATA;
1348 goto out;
1349 }
1350 if (!Stream_EnsureRemainingCapacity(channel->dvc_data, dataSize))
1351 {
1352 Stream_Release(channel->dvc_data);
1353 channel->dvc_data = nullptr;
1354 status = ERROR_INVALID_DATA;
1355 goto out;
1356 }
1357
1358 Stream_Copy(data, channel->dvc_data, dataSize);
1359 channel->stats.fragmentsIn++;
1360
1361 if (Stream_GetPosition(channel->dvc_data) >= channel->dvc_data_length)
1362 {
1363 Stream_SealLength(channel->dvc_data);
1364 Stream_ResetPosition(channel->dvc_data);
1365
1366 status = dvcman_call_on_receive(channel, channel->dvc_data);
1367 Stream_Release(channel->dvc_data);
1368 channel->dvc_data = nullptr;
1369 }
1370 }
1371 else
1372 status = dvcman_call_on_receive(channel, data);
1373
1374out:
1375 return status;
1376}
1377
1378WINPR_ATTR_NODISCARD
1379static UINT8 drdynvc_write_variable_uint(wStream* s, UINT32 val)
1380{
1381 UINT8 cb = 0;
1382
1383 if (val <= 0xFF)
1384 {
1385 cb = 0;
1386 Stream_Write_UINT8(s, (UINT8)val);
1387 }
1388 else if (val <= 0xFFFF)
1389 {
1390 cb = 1;
1391 Stream_Write_UINT16(s, (UINT16)val);
1392 }
1393 else
1394 {
1395 cb = 2;
1396 Stream_Write_UINT32(s, val);
1397 }
1398
1399 return cb;
1400}
1401
1407WINPR_ATTR_NODISCARD
1408static UINT drdynvc_send(drdynvcPlugin* drdynvc, wStream* s, DVCMAN_CHANNEL_STATS* stats)
1409{
1410 UINT status = 0;
1411
1412 if (!drdynvc)
1413 status = CHANNEL_RC_BAD_CHANNEL_HANDLE;
1414 else
1415 {
1416 const size_t len = Stream_GetPosition(s);
1417
1418 if (stats)
1419 stats->bytesOut += len;
1420
1421 WINPR_ASSERT(drdynvc->channelEntryPoints.pVirtualChannelWriteEx);
1422 status = drdynvc->channelEntryPoints.pVirtualChannelWriteEx(
1423 drdynvc->InitHandle, drdynvc->OpenHandle, Stream_Buffer(s), (UINT32)len, s);
1424 }
1425
1426 switch (status)
1427 {
1428 case CHANNEL_RC_OK:
1429 return CHANNEL_RC_OK;
1430
1431 case CHANNEL_RC_NOT_CONNECTED:
1432 Stream_Release(s);
1433 return CHANNEL_RC_OK;
1434
1435 case CHANNEL_RC_BAD_CHANNEL_HANDLE:
1436 Stream_Release(s);
1437 WLog_ERR(TAG, "VirtualChannelWriteEx failed with CHANNEL_RC_BAD_CHANNEL_HANDLE");
1438 return status;
1439
1440 default:
1441 Stream_Release(s);
1442 WLog_Print(drdynvc->log, WLOG_ERROR,
1443 "VirtualChannelWriteEx failed with %s [%08" PRIX32 "]",
1444 WTSErrorToString(status), status);
1445 return status;
1446 }
1447}
1448
1453static void drdynvc_tunnel_abort(drdynvcPlugin* drdynvc, const char* why)
1454{
1455 WINPR_ASSERT(drdynvc);
1456 WINPR_ASSERT(why);
1457
1458 WLog_Print(drdynvc->log, WLOG_ERROR, "%s, ending the session", why);
1459 if (!drdynvc->rdpcontext)
1460 return;
1461 freerdp_set_last_error_if_not(drdynvc->rdpcontext, FREERDP_ERROR_CONNECT_TRANSPORT_FAILED);
1462 (void)freerdp_abort_connect_context(drdynvc->rdpcontext);
1463}
1464
1475WINPR_ATTR_NODISCARD
1476static UINT drdynvc_send_on(drdynvcPlugin* drdynvc, wStream* s, DVCMAN_CHANNEL_STATS* stats,
1477 UINT32* tunnelType, BOOL continuation)
1478{
1479 WINPR_ASSERT(s);
1480 WINPR_ASSERT(tunnelType);
1481 if (drdynvc && (*tunnelType != 0))
1482 {
1483 const size_t len = Stream_GetPosition(s);
1484 if (freerdp_multitransport_send_dvc(drdynvc->rdpcontext, *tunnelType, Stream_Buffer(s),
1485 len))
1486 {
1487 if (stats)
1488 stats->bytesOut += len;
1489 Stream_Release(s);
1490 return CHANNEL_RC_OK;
1491 }
1492 if (continuation)
1493 {
1494 Stream_Release(s);
1495 drdynvc_tunnel_abort(drdynvc, "the tunnel failed in the middle of a message");
1496 return ERROR_BROKEN_PIPE;
1497 }
1498 *tunnelType = 0;
1499 }
1500 return drdynvc_send(drdynvc, s, stats);
1501}
1502
1508WINPR_ATTR_NODISCARD
1509static UINT drdynvc_write_data(drdynvcPlugin* drdynvc, UINT32 ChannelId, UINT32 tunnelType,
1510 const BYTE* data, UINT32 dataSize, BOOL* close,
1511 DVCMAN_CHANNEL_STATS* stats)
1512{
1513 size_t pos = 0;
1514 UINT8 cbChId = 0;
1515 UINT8 cbLen = 0;
1516 UINT status = CHANNEL_RC_BAD_INIT_HANDLE;
1517 DVCMAN* dvcman = nullptr;
1518
1519 if (!drdynvc)
1520 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1521
1522 dvcman = (DVCMAN*)drdynvc->channel_mgr;
1523 WINPR_ASSERT(dvcman);
1524
1525 /* All fragments of one message take the same way, so a message never starts on one
1526 * transport and continues on the other. */
1527 if ((tunnelType != 0) &&
1528 (freerdp_multitransport_get_tunnel_state(drdynvc->rdpcontext, tunnelType) !=
1529 FREERDP_MULTITRANSPORT_TUNNEL_READY))
1530 tunnelType = 0;
1531
1532 WLog_Print(drdynvc->log, WLOG_TRACE, "write_data: ChannelId=%" PRIu32 " size=%" PRIu32 "",
1533 ChannelId, dataSize);
1534 wStream* data_out = StreamPool_Take(dvcman->pool, CHANNEL_CHUNK_LENGTH);
1535
1536 if (!data_out)
1537 {
1538 WLog_Print(drdynvc->log, WLOG_ERROR, "StreamPool_Take failed!");
1539 return CHANNEL_RC_NO_MEMORY;
1540 }
1541
1542 if (!Stream_SetPosition(data_out, 1))
1543 {
1544 Stream_Release(data_out);
1545 return ERROR_INVALID_DATA;
1546 }
1547 cbChId = drdynvc_write_variable_uint(data_out, ChannelId);
1548 pos = Stream_GetPosition(data_out);
1549
1550 if (dataSize == 0)
1551 {
1552 /* TODO: shall treat that case with write(0) that do a close */
1553 *close = TRUE;
1554 Stream_Release(data_out);
1555 }
1556 else if (dataSize <= CHANNEL_CHUNK_LENGTH - pos)
1557 {
1558 Stream_ResetPosition(data_out);
1559 Stream_Write_UINT8(data_out, (DATA_PDU << 4) | cbChId);
1560 if (!Stream_SetPosition(data_out, pos))
1561 {
1562 Stream_Release(data_out);
1563 return ERROR_INVALID_DATA;
1564 }
1565 Stream_Write(data_out, data, dataSize);
1566 stats->packetsOut++;
1567 status = drdynvc_send_on(drdynvc, data_out, stats, &tunnelType, FALSE);
1568 }
1569 else
1570 {
1571 /* Fragment the data */
1572 cbLen = drdynvc_write_variable_uint(data_out, dataSize);
1573 pos = Stream_GetPosition(data_out);
1574 Stream_ResetPosition(data_out);
1575
1576 const INT32 pdu = (DATA_FIRST_PDU << 4) | cbChId | (cbLen << 2);
1577 Stream_Write_UINT8(data_out, WINPR_ASSERTING_INT_CAST(UINT8, pdu));
1578 if (!Stream_SetPosition(data_out, pos))
1579 {
1580 Stream_Release(data_out);
1581 return ERROR_INVALID_DATA;
1582 }
1583
1584 {
1585 WINPR_ASSERT(pos <= CHANNEL_CHUNK_LENGTH);
1586 const uint32_t chunkLength =
1587 CHANNEL_CHUNK_LENGTH - WINPR_ASSERTING_INT_CAST(uint32_t, pos);
1588 Stream_Write(data_out, data, chunkLength);
1589
1590 data += chunkLength;
1591 dataSize -= chunkLength;
1592 }
1593 if (dataSize > 0)
1594 stats->fragmentsOut++;
1595
1596 status = drdynvc_send_on(drdynvc, data_out, stats, &tunnelType, FALSE);
1597
1598 while (status == CHANNEL_RC_OK && dataSize > 0)
1599 {
1600 data_out = StreamPool_Take(dvcman->pool, CHANNEL_CHUNK_LENGTH);
1601
1602 if (!data_out)
1603 {
1604 WLog_Print(drdynvc->log, WLOG_ERROR, "StreamPool_Take failed!");
1605 return CHANNEL_RC_NO_MEMORY;
1606 }
1607
1608 if (!Stream_SetPosition(data_out, 1))
1609 {
1610 Stream_Release(data_out);
1611 return ERROR_INVALID_DATA;
1612 }
1613
1614 cbChId = drdynvc_write_variable_uint(data_out, ChannelId);
1615 pos = Stream_GetPosition(data_out);
1616 Stream_ResetPosition(data_out);
1617 Stream_Write_UINT8(data_out, (DATA_PDU << 4) | cbChId);
1618 if (!Stream_SetPosition(data_out, pos))
1619 {
1620 Stream_Release(data_out);
1621 return ERROR_INVALID_DATA;
1622 }
1623
1624 uint32_t chunkLength = dataSize;
1625
1626 WINPR_ASSERT(pos <= CHANNEL_CHUNK_LENGTH);
1627 const uint32_t clen = CHANNEL_CHUNK_LENGTH - WINPR_ASSERTING_INT_CAST(uint32_t, pos);
1628 if (chunkLength > clen)
1629 {
1630 stats->fragmentsOut++;
1631 chunkLength = clen;
1632 }
1633 else
1634 stats->packetsOut++;
1635
1636 Stream_Write(data_out, data, chunkLength);
1637 data += chunkLength;
1638 dataSize -= chunkLength;
1639
1640 status = drdynvc_send_on(drdynvc, data_out, stats, &tunnelType, TRUE);
1641 }
1642 }
1643
1644 if (status != CHANNEL_RC_OK)
1645 {
1646 WLog_Print(drdynvc->log, WLOG_ERROR, "VirtualChannelWriteEx failed with %s [%08" PRIX32 "]",
1647 WTSErrorToString(status), status);
1648 return status;
1649 }
1650
1651 return CHANNEL_RC_OK;
1652}
1653
1659WINPR_ATTR_NODISCARD
1660static UINT drdynvc_send_capability_response(drdynvcPlugin* drdynvc)
1661{
1662 UINT status = 0;
1663 wStream* s = nullptr;
1664 DVCMAN* dvcman = nullptr;
1665
1666 if (!drdynvc)
1667 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1668
1669 dvcman = (DVCMAN*)drdynvc->channel_mgr;
1670 WINPR_ASSERT(dvcman);
1671
1672 WLog_Print(drdynvc->log, WLOG_TRACE, "capability_response");
1673 s = StreamPool_Take(dvcman->pool, 4);
1674
1675 if (!s)
1676 {
1677 WLog_Print(drdynvc->log, WLOG_ERROR, "Stream_New failed!");
1678 return CHANNEL_RC_NO_MEMORY;
1679 }
1680
1681 Stream_Write_UINT16(s, 0x0050); /* Cmd+Sp+cbChId+Pad. Note: MSTSC sends 0x005c */
1682 Stream_Write_UINT16(s, drdynvc->version);
1683 status = drdynvc_send(drdynvc, s, nullptr);
1684
1685 if (status != CHANNEL_RC_OK)
1686 {
1687 WLog_Print(drdynvc->log, WLOG_ERROR, "VirtualChannelWriteEx failed with %s [%08" PRIX32 "]",
1688 WTSErrorToString(status), status);
1689 }
1690
1691 return status;
1692}
1693
1699WINPR_ATTR_NODISCARD
1700static UINT drdynvc_process_capability_request(drdynvcPlugin* drdynvc, int Sp, int cbChId,
1701 wStream* s)
1702{
1703 UINT status = 0;
1704
1705 if (!drdynvc)
1706 return CHANNEL_RC_BAD_INIT_HANDLE;
1707
1708 if (!Stream_CheckAndLogRequiredLength(TAG, s, 3))
1709 return ERROR_INVALID_DATA;
1710
1711 WLog_Print(drdynvc->log, WLOG_TRACE, "capability_request Sp=%d cbChId=%d", Sp, cbChId);
1712 Stream_Seek(s, 1); /* pad */
1713 Stream_Read_UINT16(s, drdynvc->version);
1714
1715 /* RDP8 servers offer version 3, though Microsoft forgot to document it
1716 * in their early documents. It behaves the same as version 2.
1717 */
1718 if ((drdynvc->version == 2) || (drdynvc->version == 3))
1719 {
1720 if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
1721 return ERROR_INVALID_DATA;
1722
1723 Stream_Read_UINT16(s, drdynvc->PriorityCharge0);
1724 Stream_Read_UINT16(s, drdynvc->PriorityCharge1);
1725 Stream_Read_UINT16(s, drdynvc->PriorityCharge2);
1726 Stream_Read_UINT16(s, drdynvc->PriorityCharge3);
1727 }
1728
1729 status = drdynvc_send_capability_response(drdynvc);
1730 drdynvc->state = DRDYNVC_STATE_READY;
1731 return status;
1732}
1733
1734WINPR_ATTR_NODISCARD
1735static UINT32 drdynvc_cblen_to_bytes(int cbLen)
1736{
1737 switch (cbLen)
1738 {
1739 case 0:
1740 return 1;
1741
1742 case 1:
1743 return 2;
1744
1745 default:
1746 return 4;
1747 }
1748}
1749
1750WINPR_ATTR_NODISCARD
1751static UINT32 drdynvc_read_variable_uint(wStream* s, int cbLen)
1752{
1753 UINT32 val = 0;
1754
1755 switch (cbLen)
1756 {
1757 case 0:
1758 Stream_Read_UINT8(s, val);
1759 break;
1760
1761 case 1:
1762 Stream_Read_UINT16(s, val);
1763 break;
1764
1765 default:
1766 Stream_Read_UINT32(s, val);
1767 break;
1768 }
1769
1770 return val;
1771}
1772
1778WINPR_ATTR_NODISCARD
1779static UINT drdynvc_process_create_request(drdynvcPlugin* drdynvc, UINT8 Sp, UINT8 cbChId,
1780 wStream* s, UINT32 tunnelType)
1781{
1782 UINT status = 0;
1783 wStream* data_out = nullptr;
1784 UINT channel_status = 0;
1785 DVCMAN* dvcman = nullptr;
1786 DVCMAN_CHANNEL* channel = nullptr;
1787 INT32 retStatus = 0;
1788
1789 WINPR_UNUSED(Sp);
1790 if (!drdynvc)
1791 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1792
1793 dvcman = (DVCMAN*)drdynvc->channel_mgr;
1794 WINPR_ASSERT(dvcman);
1795
1796 if (drdynvc->state == DRDYNVC_STATE_CAPABILITIES)
1797 {
1803 drdynvc->version = 3;
1804
1805 if ((status = drdynvc_send_capability_response(drdynvc)))
1806 {
1807 WLog_Print(drdynvc->log, WLOG_ERROR, "drdynvc_send_capability_response failed!");
1808 return status;
1809 }
1810
1811 drdynvc->state = DRDYNVC_STATE_READY;
1812 }
1813
1814 if (!Stream_CheckAndLogRequiredLength(TAG, s, drdynvc_cblen_to_bytes(cbChId)))
1815 return ERROR_INVALID_DATA;
1816
1817 const UINT32 ChannelId = drdynvc_read_variable_uint(s, cbChId);
1818 const size_t pos = Stream_GetPosition(s);
1819 const char* name = Stream_ConstPointer(s);
1820 const size_t length = Stream_GetRemainingLength(s);
1821
1822 if (strnlen(name, length) >= length)
1823 return ERROR_INVALID_DATA;
1824
1825 WLog_Print(drdynvc->log, WLOG_DEBUG,
1826 "process_create_request: ChannelId=%" PRIu32 " ChannelName=%s", ChannelId, name);
1827
1828 data_out = StreamPool_Take(dvcman->pool, pos + 4);
1829 if (!data_out)
1830 {
1831 WLog_Print(drdynvc->log, WLOG_ERROR, "StreamPool_Take failed!");
1832 return CHANNEL_RC_NO_MEMORY;
1833 }
1834
1835 Stream_Write_UINT8(data_out, (CREATE_REQUEST_PDU << 4) | cbChId);
1836 if (!Stream_SetPosition(s, 1))
1837 return ERROR_INVALID_DATA;
1838 Stream_Copy(s, data_out, pos - 1);
1839
1840 channel =
1841 dvcman_create_channel(drdynvc, drdynvc->channel_mgr, ChannelId, name, &channel_status);
1842 switch (channel_status)
1843 {
1844 case CHANNEL_RC_OK:
1845 WLog_Print(drdynvc->log, WLOG_DEBUG, "channel created");
1846 retStatus = 0;
1847 break;
1848 case CHANNEL_RC_NO_MEMORY:
1849 WLog_Print(drdynvc->log, WLOG_DEBUG, "not enough memory for channel creation");
1850 retStatus = STATUS_NO_MEMORY;
1851 break;
1852 case ERROR_NOT_FOUND:
1853 WLog_Print(drdynvc->log, WLOG_DEBUG, "no listener for '%s'", name);
1854 retStatus = STATUS_NOT_FOUND; /* same code used by mstsc, STATUS_UNSUCCESSFUL */
1855 break;
1856 default:
1857 WLog_Print(drdynvc->log, WLOG_DEBUG, "channel creation error");
1858 retStatus = STATUS_UNSUCCESSFUL; /* same code used by mstsc, STATUS_UNSUCCESSFUL */
1859 break;
1860 }
1861 Stream_Write_INT32(data_out, retStatus);
1862
1863 /* A channel created on a tunnel is read there by the server, so it is answered there. */
1864 if (channel && (channel_status == CHANNEL_RC_OK))
1865 dvcman_channel_set_tunnel(channel, tunnelType);
1866 UINT32 responseTunnel = tunnelType;
1867 status = drdynvc_send_on(drdynvc, data_out, nullptr, &responseTunnel, FALSE);
1868 if (status != CHANNEL_RC_OK)
1869 {
1870 WLog_Print(drdynvc->log, WLOG_ERROR, "VirtualChannelWriteEx failed with %s [%08" PRIX32 "]",
1871 WTSErrorToString(status), status);
1872 if (channel_status == CHANNEL_RC_OK)
1873 dvcman_channel_unref(channel);
1874 return status;
1875 }
1876
1877 if (channel_status == CHANNEL_RC_OK)
1878 {
1879#if defined(WITH_RDPEUDP)
1880 dvcman_static_bridge_publish(drdynvc, channel);
1881#endif
1882 if ((status = dvcman_open_channel(drdynvc, channel)))
1883 {
1884 WLog_Print(drdynvc->log, WLOG_ERROR,
1885 "dvcman_open_channel failed with error %" PRIu32 "!", status);
1886 return status;
1887 }
1888 }
1889
1890 return status;
1891}
1892
1898WINPR_ATTR_NODISCARD
1899static UINT drdynvc_process_data_first(drdynvcPlugin* drdynvc, int Sp, int cbChId, wStream* s,
1900 BOOL compressed, UINT32 ThreadingFlags, UINT32 tunnelType)
1901{
1902 WINPR_ASSERT(drdynvc);
1903 if (!Stream_CheckAndLogRequiredLength(
1904 TAG, s, drdynvc_cblen_to_bytes(cbChId) + drdynvc_cblen_to_bytes(Sp)))
1905 return ERROR_INVALID_DATA;
1906
1907 UINT32 ChannelId = drdynvc_read_variable_uint(s, cbChId);
1908 UINT32 Length = drdynvc_read_variable_uint(s, Sp);
1909 WLog_Print(drdynvc->log, WLOG_TRACE,
1910 "process_data_first: Sp=%d cbChId=%d, ChannelId=%" PRIu32 " Length=%" PRIu32 "", Sp,
1911 cbChId, ChannelId, Length);
1912
1913 DVCMAN_CHANNEL* channel = dvcman_get_channel_by_id(drdynvc->channel_mgr, ChannelId, TRUE);
1914 if (!channel)
1915 {
1921 WLog_Print(drdynvc->log, WLOG_ERROR, "ChannelId %" PRIu32 " not found!", ChannelId);
1922 return CHANNEL_RC_OK;
1923 }
1924
1925 UINT status = CHANNEL_RC_OK;
1926 BOOL shouldFree = FALSE;
1927 if (tunnelType != 0)
1928 dvcman_channel_set_tunnel(channel, tunnelType);
1929 if (channel->state != DVC_CHANNEL_RUNNING)
1930 goto out;
1931
1932 if (compressed)
1933 {
1934 BYTE* data = nullptr;
1935 UINT32 dataSize = 0;
1936 if (zgfx_decompress(channel->decompressor, Stream_Pointer(s),
1937 WINPR_ASSERTING_INT_CAST(UINT32, Stream_GetRemainingLength(s)), &data,
1938 &dataSize, 0) < 0)
1939 {
1940 status = ERROR_INVALID_DATA;
1941 WLog_Print(drdynvc->log, WLOG_ERROR, "error de-compressing first packet");
1942 goto out;
1943 }
1944
1945 if (dataSize == 0)
1946 {
1947 status = ERROR_INVALID_DATA;
1948 WLog_Print(drdynvc->log, WLOG_ERROR, "error de-compressing first packet");
1949 free(data);
1950 goto out;
1951 }
1952
1953 s = Stream_New(data, dataSize);
1954 if (!s)
1955 {
1956 status = CHANNEL_RC_NO_MEMORY;
1957 WLog_Print(drdynvc->log, WLOG_ERROR, "error allocating new Stream(len=%" PRIu32 ")",
1958 dataSize);
1959 free(data);
1960 goto out;
1961 }
1962 shouldFree = TRUE;
1963 }
1964
1965 status = dvcman_receive_channel_data_first(channel, Length);
1966
1967 if (status == CHANNEL_RC_OK)
1968 status = dvcman_receive_channel_data(channel, s, ThreadingFlags);
1969
1970 if (status != CHANNEL_RC_OK)
1971 status = dvcman_channel_close(channel, FALSE, FALSE);
1972
1973out:
1974 if (shouldFree)
1975 Stream_Free(s, TRUE);
1976 dvcman_channel_unref(channel);
1977 dvcman_return_channel(drdynvc->channel_mgr, channel);
1978 return status;
1979}
1980
1986WINPR_ATTR_NODISCARD
1987static UINT drdynvc_process_data(drdynvcPlugin* drdynvc, int Sp, int cbChId, wStream* s,
1988 BOOL compressed, UINT32 ThreadingFlags, UINT32 tunnelType)
1989{
1990 WINPR_ASSERT(drdynvc);
1991 if (!Stream_CheckAndLogRequiredLength(TAG, s, drdynvc_cblen_to_bytes(cbChId)))
1992 return ERROR_INVALID_DATA;
1993
1994 UINT32 ChannelId = drdynvc_read_variable_uint(s, cbChId);
1995 WLog_Print(drdynvc->log, WLOG_TRACE, "process_data: Sp=%d cbChId=%d, ChannelId=%" PRIu32 "", Sp,
1996 cbChId, ChannelId);
1997
1998 DVCMAN_CHANNEL* channel = dvcman_get_channel_by_id(drdynvc->channel_mgr, ChannelId, TRUE);
1999 if (!channel)
2000 {
2006 WLog_Print(drdynvc->log, WLOG_ERROR, "ChannelId %" PRIu32 " not found!", ChannelId);
2007 return CHANNEL_RC_OK;
2008 }
2009
2010 BOOL shouldFree = FALSE;
2011 UINT status = CHANNEL_RC_OK;
2012 if (tunnelType != 0)
2013 dvcman_channel_set_tunnel(channel, tunnelType);
2014 if (channel->state != DVC_CHANNEL_RUNNING)
2015 goto out;
2016
2017 if (compressed)
2018 {
2019 BYTE* data = nullptr;
2020 UINT32 dataSize = 0;
2021
2022 if (zgfx_decompress(channel->decompressor, Stream_Pointer(s),
2023 WINPR_ASSERTING_INT_CAST(UINT32, Stream_GetRemainingLength(s)), &data,
2024 &dataSize, 0) < 0)
2025 {
2026 status = ERROR_INVALID_DATA;
2027 WLog_Print(drdynvc->log, WLOG_ERROR, "error de-compressing data packet");
2028 free(data);
2029 goto out;
2030 }
2031
2032 if (dataSize == 0)
2033 {
2034 status = ERROR_INVALID_DATA;
2035 WLog_Print(drdynvc->log, WLOG_ERROR, "error de-compressing data packet");
2036 free(data);
2037 goto out;
2038 }
2039
2040 s = Stream_New(data, dataSize);
2041 if (!s)
2042 {
2043 status = CHANNEL_RC_NO_MEMORY;
2044 WLog_Print(drdynvc->log, WLOG_ERROR, "error allocating new Stream(len=%" PRIu32 ")",
2045 dataSize);
2046 free(data);
2047 goto out;
2048 }
2049 shouldFree = TRUE;
2050 }
2051
2052 status = dvcman_receive_channel_data(channel, s, ThreadingFlags);
2053 if (status != CHANNEL_RC_OK)
2054 status = dvcman_channel_close(channel, FALSE, FALSE);
2055
2056out:
2057 if (shouldFree)
2058 Stream_Free(s, TRUE);
2059 dvcman_channel_unref(channel);
2060 dvcman_return_channel(drdynvc->channel_mgr, channel);
2061 return status;
2062}
2063
2069WINPR_ATTR_NODISCARD
2070static UINT drdynvc_process_close_request(drdynvcPlugin* drdynvc, int Sp, int cbChId, wStream* s,
2071 UINT32 tunnelType)
2072{
2073 UINT32 ChannelId = 0;
2074 DVCMAN_CHANNEL* channel = nullptr;
2075
2076 WINPR_ASSERT(drdynvc);
2077 if (!Stream_CheckAndLogRequiredLength(TAG, s, drdynvc_cblen_to_bytes(cbChId)))
2078 return ERROR_INVALID_DATA;
2079
2080 ChannelId = drdynvc_read_variable_uint(s, cbChId);
2081 WLog_Print(drdynvc->log, WLOG_DEBUG,
2082 "process_close_request: Sp=%d cbChId=%d, ChannelId=%" PRIu32 "", Sp, cbChId,
2083 ChannelId);
2084
2085 channel = dvcman_get_channel_by_id(drdynvc->channel_mgr, ChannelId, TRUE);
2086 if (!channel)
2087 {
2088 WLog_Print(drdynvc->log, WLOG_ERROR, "dvcman_close_request channel %" PRIu32 " not present",
2089 ChannelId);
2090 return CHANNEL_RC_OK;
2091 }
2092
2093 if (tunnelType != 0)
2094 dvcman_channel_set_tunnel(channel, tunnelType);
2095 dvcman_channel_close(channel, TRUE, FALSE);
2096 dvcman_channel_unref(channel);
2097 dvcman_return_channel(drdynvc->channel_mgr, channel);
2098 return CHANNEL_RC_OK;
2099}
2100
2101WINPR_ATTR_NODISCARD
2102static BOOL drdynvc_soft_sync_negotiated(drdynvcPlugin* drdynvc)
2103{
2104 WINPR_ASSERT(drdynvc);
2105 if (!drdynvc->rdpcontext)
2106 return FALSE;
2107 const UINT32 flags =
2108 freerdp_settings_get_uint32(drdynvc->rdpcontext->settings, FreeRDP_MultitransportFlags);
2109 return (flags & SOFTSYNC_TCP_TO_UDP) != 0;
2110}
2111
2112WINPR_ATTR_NODISCARD
2113static UINT drdynvc_order_recv(drdynvcPlugin* drdynvc, wStream* s, UINT32 ThreadingFlags,
2114 UINT32 tunnelType);
2115
2116static void drdynvc_tunnel_backlog_clear(drdynvcPlugin* drdynvc)
2117{
2118 WINPR_ASSERT(drdynvc);
2119 for (size_t x = 0; x < drdynvc->tunnelBacklogCount; x++)
2120 Stream_Release(drdynvc->tunnelBacklog[x]);
2121 drdynvc->tunnelBacklogCount = 0;
2122}
2123
2124/* Hands up tunnel data that arrived before the Soft-Sync Request, in arrival order. The backlog
2125 * is taken out first, so a PDU handed up that leads back here finds it empty instead of entries
2126 * this loop has handed up already. */
2127WINPR_ATTR_NODISCARD
2128static UINT drdynvc_tunnel_backlog_flush(drdynvcPlugin* drdynvc)
2129{
2130 WINPR_ASSERT(drdynvc);
2131
2132 wStream** backlog = drdynvc->tunnelBacklog;
2133 UINT32* types = drdynvc->tunnelBacklogTypes;
2134 const size_t count = drdynvc->tunnelBacklogCount;
2135 drdynvc->tunnelBacklog = nullptr;
2136 drdynvc->tunnelBacklogTypes = nullptr;
2137 drdynvc->tunnelBacklogCount = 0;
2138 drdynvc->tunnelBacklogCapacity = 0;
2139
2140 UINT error = CHANNEL_RC_OK;
2141 for (size_t x = 0; x < count; x++)
2142 {
2143 const UINT rc = drdynvc_order_recv(drdynvc, backlog[x], TRUE, types[x]);
2144 Stream_Release(backlog[x]);
2145 if (rc != CHANNEL_RC_OK)
2146 error = rc;
2147 }
2148 free((void*)backlog);
2149 free(types);
2150 return error;
2151}
2152
2153WINPR_ATTR_NODISCARD
2154static BOOL drdynvc_tunnel_backlog_add(drdynvcPlugin* drdynvc, wStream* s, UINT32 tunnelType)
2155{
2156 WINPR_ASSERT(drdynvc);
2157 WINPR_ASSERT(s);
2158
2159 if (drdynvc->tunnelBacklogCount == drdynvc->tunnelBacklogCapacity)
2160 {
2161 /* bounded by DRDYNVC_MAX_TUNNEL_BACKLOG, checked anyway */
2162 if (drdynvc->tunnelBacklogCapacity > SIZE_MAX / 2 / sizeof(wStream*))
2163 return FALSE;
2164 const size_t capacity =
2165 (drdynvc->tunnelBacklogCapacity == 0) ? 32 : drdynvc->tunnelBacklogCapacity * 2;
2166 wStream** streams =
2167 (wStream**)realloc((void*)drdynvc->tunnelBacklog, capacity * sizeof(wStream*));
2168 if (!streams)
2169 return FALSE;
2170 drdynvc->tunnelBacklog = streams;
2171 UINT32* types = realloc(drdynvc->tunnelBacklogTypes, capacity * sizeof(UINT32));
2172 if (!types)
2173 return FALSE;
2174 drdynvc->tunnelBacklogTypes = types;
2175 drdynvc->tunnelBacklogCapacity = capacity;
2176 }
2177 drdynvc->tunnelBacklog[drdynvc->tunnelBacklogCount] = s;
2178 drdynvc->tunnelBacklogTypes[drdynvc->tunnelBacklogCount] = tunnelType;
2179 drdynvc->tunnelBacklogCount++;
2180 return TRUE;
2181}
2182
2190WINPR_ATTR_NODISCARD
2191static UINT drdynvc_receive_tunnel_pdu(drdynvcPlugin* drdynvc, wStream* s, UINT32 tunnelType)
2192{
2193 WINPR_ASSERT(drdynvc);
2194 WINPR_ASSERT(s);
2195
2196 if (drdynvc_soft_sync_negotiated(drdynvc) && !drdynvc->softSyncDone)
2197 {
2198 /* Reading on without the request would interleave tunnel and TCP data in no defined
2199 * order, so a server that never sends it ends the session. */
2200 if (drdynvc->tunnelBacklogCount >= DRDYNVC_MAX_TUNNEL_BACKLOG)
2201 {
2202 Stream_Release(s);
2203 drdynvc_tunnel_backlog_clear(drdynvc);
2204 drdynvc_tunnel_abort(drdynvc,
2205 "no Soft-Sync Request after the tunnel backlog filled up");
2206 return ERROR_INVALID_DATA;
2207 }
2208 if (!drdynvc_tunnel_backlog_add(drdynvc, s, tunnelType))
2209 {
2210 /* acknowledged to the server already, dropping it leaves a hole in the stream */
2211 Stream_Release(s);
2212 drdynvc_tunnel_abort(drdynvc, "no memory to hold tunnel data until the Soft-Sync");
2213 return CHANNEL_RC_NO_MEMORY;
2214 }
2215 return CHANNEL_RC_OK;
2216 }
2217
2218 const UINT error = drdynvc_order_recv(drdynvc, s, TRUE, tunnelType);
2219 Stream_Release(s);
2220 return error;
2221}
2222
2223/* Moves a channel's data to a tunnel, if the channel exists. The lookup and the move happen under
2224 * the channel table's lock, so the channel cannot go away in between. */
2225static void dvcman_move_channel_to_tunnel(drdynvcPlugin* drdynvc, UINT32 channelId,
2226 UINT32 tunnelType)
2227{
2228 WINPR_ASSERT(drdynvc);
2229 DVCMAN* dvcman = (DVCMAN*)drdynvc->channel_mgr;
2230 WINPR_ASSERT(dvcman);
2231
2232 HashTable_Lock(dvcman->channelsById);
2233 DVCMAN_CHANNEL* channel = HashTable_GetItemValue(dvcman->channelsById, &channelId);
2234 if (channel)
2235 {
2236 dvcman_channel_set_tunnel(channel, tunnelType);
2237 WLog_Print(drdynvc->log, WLOG_DEBUG,
2238 "channel %" PRIu32 " (%s) moves to tunnel 0x%08" PRIx32, channelId,
2239 channel->channel_name, tunnelType);
2240 }
2241 HashTable_Unlock(dvcman->channelsById);
2242}
2243
2244WINPR_ATTR_NODISCARD
2245static UINT drdynvc_send_soft_sync_response(drdynvcPlugin* drdynvc, const UINT32* tunnels,
2246 UINT32 count)
2247{
2248 WINPR_ASSERT(drdynvc);
2249 WINPR_ASSERT(tunnels || (count == 0));
2250 DVCMAN* dvcman = (DVCMAN*)drdynvc->channel_mgr;
2251 WINPR_ASSERT(dvcman);
2252
2253 /* count is the number of tunnels we accept, never a value from the wire */
2254 if (count > DRDYNVC_MAX_SOFT_SYNC_TUNNELS)
2255 return ERROR_INVALID_PARAMETER;
2256 wStream* s = StreamPool_Take(dvcman->pool, 6 + sizeof(UINT32) * count);
2257 if (!s)
2258 return CHANNEL_RC_NO_MEMORY;
2259
2260 /* [MS-RDPEDYC] 2.2.5.2 DYNVC_SOFT_SYNC_RESPONSE */
2261 Stream_Write_UINT8(s, SOFT_SYNC_RESPONSE_PDU << 4);
2262 Stream_Write_UINT8(s, 0); /* Pad */
2263 Stream_Write_UINT32(s, count);
2264 for (UINT32 x = 0; x < count; x++)
2265 Stream_Write_UINT32(s, tunnels[x]);
2266
2267 /* always on the drdynvc static channel */
2268 return drdynvc_send(drdynvc, s, nullptr);
2269}
2270
2274WINPR_ATTR_NODISCARD
2275static UINT drdynvc_process_soft_sync_request(drdynvcPlugin* drdynvc, wStream* s)
2276{
2277 WINPR_ASSERT(drdynvc);
2278 WINPR_ASSERT(s);
2279
2280 /* the header byte was read already */
2281 const size_t position = Stream_GetPosition(s);
2282 if (position < 1)
2283 return ERROR_INVALID_DATA;
2284 const size_t start = position - 1;
2285 if (!Stream_CheckAndLogRequiredLength(TAG, s, 9))
2286 return ERROR_INVALID_DATA;
2287
2288 Stream_Seek_UINT8(s); /* Pad */
2289 const UINT32 length = Stream_Get_UINT32(s);
2290 const UINT16 flags = Stream_Get_UINT16(s);
2291 const UINT16 numberOfTunnels = Stream_Get_UINT16(s);
2292
2293 /* Windows counts Length from the Length field on, the header byte and Pad are not in it. The
2294 * channel lists must fit in it, nothing after it belongs to them. */
2295 const size_t total = Stream_Length(s);
2296 if ((total < start) || (total - start < 2))
2297 return ERROR_INVALID_DATA;
2298 const size_t available = total - start - 2;
2299 if ((length < 8) || (length > available))
2300 {
2301 WLog_Print(drdynvc->log, WLOG_ERROR,
2302 "Soft-Sync Request Length %" PRIu32 " with %" PRIuz " bytes present", length,
2303 available);
2304 return ERROR_INVALID_DATA;
2305 }
2306 /* length <= available, so the request ends inside the stream */
2307 if (!Stream_SetLength(s, total - (available - length)))
2308 return ERROR_INVALID_DATA;
2309
2310 WLog_Print(drdynvc->log, WLOG_DEBUG,
2311 "Soft-Sync Request: flags 0x%04" PRIx16 ", %" PRIu16 " tunnel(s)", flags,
2312 numberOfTunnels);
2313
2314 /* [MS-RDPEDYC] 2.2.5.1: SOFT_SYNC_TCP_FLUSHED must be set. Without it the server has not
2315 * said its TCP data for these channels is out, so none of them moves: an empty response
2316 * keeps them all on TCP. */
2317 if (!(flags & SOFT_SYNC_TCP_FLUSHED))
2318 {
2319 WLog_Print(drdynvc->log, WLOG_WARN,
2320 "Soft-Sync Request without SOFT_SYNC_TCP_FLUSHED, channels stay on TCP");
2321 const UINT error = drdynvc_send_soft_sync_response(drdynvc, nullptr, 0);
2322 if (error != CHANNEL_RC_OK)
2323 return error;
2324 drdynvc->softSyncDone = TRUE;
2325 return drdynvc_tunnel_backlog_flush(drdynvc);
2326 }
2327
2328 const size_t lists = Stream_GetPosition(s);
2329
2330 /* A tunnel that is still being set up gets its answer once it is up, or has failed. The
2331 * response must not name a tunnel before the Initiate Multitransport Response for it went
2332 * out ([MS-RDPEDYC] 3.2.5.3.2). */
2333 if (flags & SOFT_SYNC_CHANNEL_LIST_PRESENT)
2334 {
2335 for (UINT16 x = 0; x < numberOfTunnels; x++)
2336 {
2337 if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
2338 return ERROR_INVALID_DATA;
2339 const UINT32 tunnelType = Stream_Get_UINT32(s);
2340 const UINT16 count = Stream_Get_UINT16(s);
2341 if (!Stream_CheckAndLogRequiredLengthOfSize(TAG, s, count, sizeof(UINT32)))
2342 return ERROR_INVALID_DATA;
2343 Stream_Seek(s, 4ull * count);
2344
2345 if (freerdp_multitransport_get_tunnel_state(drdynvc->rdpcontext, tunnelType) ==
2346 FREERDP_MULTITRANSPORT_TUNNEL_PENDING)
2347 {
2348 WLog_Print(drdynvc->log, WLOG_DEBUG,
2349 "tunnel 0x%08" PRIx32 " is not up yet, answering Soft-Sync later",
2350 tunnelType);
2351 const size_t size = Stream_Length(s) - start;
2352 Stream_Free(drdynvc->pendingSoftSync, TRUE);
2353 drdynvc->pendingSoftSync = Stream_New(nullptr, size);
2354 if (!drdynvc->pendingSoftSync)
2355 return CHANNEL_RC_NO_MEMORY;
2356 Stream_Write(drdynvc->pendingSoftSync, Stream_Buffer(s) + start, size);
2357 Stream_SealLength(drdynvc->pendingSoftSync);
2358 return CHANNEL_RC_OK;
2359 }
2360 }
2361 }
2362
2363 UINT32 accepted[DRDYNVC_MAX_SOFT_SYNC_TUNNELS] = WINPR_C_ARRAY_INIT;
2364 UINT32 acceptedCount = 0;
2365 if (!Stream_SetPosition(s, lists))
2366 return ERROR_INVALID_DATA;
2367
2368 if (flags & SOFT_SYNC_CHANNEL_LIST_PRESENT)
2369 {
2370 for (UINT16 x = 0; x < numberOfTunnels; x++)
2371 {
2372 const UINT32 tunnelType = Stream_Get_UINT32(s);
2373 const UINT16 count = Stream_Get_UINT16(s);
2374 const BOOL ready =
2375 freerdp_multitransport_get_tunnel_state(drdynvc->rdpcontext, tunnelType) ==
2376 FREERDP_MULTITRANSPORT_TUNNEL_READY;
2377
2378 for (UINT16 y = 0; y < count; y++)
2379 {
2380 const UINT32 channelId = Stream_Get_UINT32(s);
2381 if (ready)
2382 dvcman_move_channel_to_tunnel(drdynvc, channelId, tunnelType);
2383 }
2384
2385 if (ready && (acceptedCount < ARRAYSIZE(accepted)))
2386 {
2387 BOOL known = FALSE;
2388 for (UINT32 z = 0; z < acceptedCount; z++)
2389 known |= (accepted[z] == tunnelType);
2390 if (!known)
2391 accepted[acceptedCount++] = tunnelType;
2392 }
2393 }
2394 }
2395 else if (freerdp_multitransport_get_tunnel_state(drdynvc->rdpcontext, TUNNELTYPE_UDPFECR) ==
2396 FREERDP_MULTITRANSPORT_TUNNEL_READY)
2397 {
2398 accepted[acceptedCount++] = TUNNELTYPE_UDPFECR;
2399 }
2400
2401 WLog_Print(drdynvc->log, WLOG_INFO,
2402 "Soft-Sync: %" PRIu32 " tunnel(s) will carry dynamic channel data", acceptedCount);
2403
2404 const UINT error = drdynvc_send_soft_sync_response(drdynvc, accepted, acceptedCount);
2405 if (error != CHANNEL_RC_OK)
2406 return error;
2407
2408 drdynvc->softSyncDone = TRUE;
2409 return drdynvc_tunnel_backlog_flush(drdynvc);
2410}
2411
2412/* A tunnel came up or went away: a deferred Soft-Sync Request can be answered now. */
2413WINPR_ATTR_NODISCARD
2414static UINT drdynvc_tunnel_state_changed(drdynvcPlugin* drdynvc)
2415{
2416 WINPR_ASSERT(drdynvc);
2417 if (drdynvc->pendingSoftSync)
2418 {
2419 wStream* s = drdynvc->pendingSoftSync;
2420 drdynvc->pendingSoftSync = nullptr;
2421 Stream_ResetPosition(s);
2422 const UINT error = drdynvc_order_recv(drdynvc, s, TRUE, 0);
2423 Stream_Free(s, TRUE);
2424 if (error != CHANNEL_RC_OK)
2425 return error;
2426 }
2427
2428 /* without a tunnel there is nothing to wait for */
2429 if (!drdynvc->softSyncDone &&
2430 (freerdp_multitransport_get_tunnel_state(drdynvc->rdpcontext, TUNNELTYPE_UDPFECR) ==
2431 FREERDP_MULTITRANSPORT_TUNNEL_NONE))
2432 return drdynvc_tunnel_backlog_flush(drdynvc);
2433 return CHANNEL_RC_OK;
2434}
2435
2441WINPR_ATTR_NODISCARD
2442static UINT drdynvc_order_recv(drdynvcPlugin* drdynvc, wStream* s, UINT32 ThreadingFlags,
2443 UINT32 tunnelType)
2444{
2445 WINPR_ASSERT(drdynvc);
2446 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
2447 return ERROR_INVALID_DATA;
2448
2449 UINT8 value = Stream_Get_UINT8(s);
2450 const UINT8 Cmd = (value & 0xf0) >> 4;
2451 const UINT8 Sp = (value & 0x0c) >> 2;
2452 const UINT8 cbChId = (value & 0x03) >> 0;
2453 WLog_Print(drdynvc->log, WLOG_TRACE, "order_recv: Cmd=%s, Sp=%" PRIu8 " cbChId=%" PRIu8,
2454 drdynvc_get_packet_type(Cmd), Sp, cbChId);
2455
2456 /* Capabilities and Soft-Sync travel on the drdynvc static channel only, a tunnel carries the
2457 * channels themselves. Accepting a Soft-Sync Request from the tunnel would also let it run
2458 * while the tunnel backlog is being handed up. */
2459 if ((tunnelType != 0) && ((Cmd == CAPABILITY_REQUEST_PDU) || (Cmd == SOFT_SYNC_REQUEST_PDU)))
2460 {
2461 WLog_Print(drdynvc->log, WLOG_ERROR, "%s on tunnel 0x%08" PRIx32 ", rejected",
2462 drdynvc_get_packet_type(Cmd), tunnelType);
2463 return ERROR_INVALID_DATA;
2464 }
2465
2466 switch (Cmd)
2467 {
2468 case CAPABILITY_REQUEST_PDU:
2469 return drdynvc_process_capability_request(drdynvc, Sp, cbChId, s);
2470
2471 case CREATE_REQUEST_PDU:
2472 return drdynvc_process_create_request(drdynvc, Sp, cbChId, s, tunnelType);
2473
2474 case DATA_FIRST_PDU:
2475 case DATA_FIRST_COMPRESSED_PDU:
2476 return drdynvc_process_data_first(drdynvc, Sp, cbChId, s,
2477 (Cmd == DATA_FIRST_COMPRESSED_PDU), ThreadingFlags,
2478 tunnelType);
2479
2480 case DATA_PDU:
2481 case DATA_COMPRESSED_PDU:
2482 return drdynvc_process_data(drdynvc, Sp, cbChId, s, (Cmd == DATA_COMPRESSED_PDU),
2483 ThreadingFlags, tunnelType);
2484
2485 case CLOSE_REQUEST_PDU:
2486 return drdynvc_process_close_request(drdynvc, Sp, cbChId, s, tunnelType);
2487
2488 case SOFT_SYNC_REQUEST_PDU:
2489 return drdynvc_process_soft_sync_request(drdynvc, s);
2490
2491 case SOFT_SYNC_RESPONSE_PDU:
2492 WLog_Print(drdynvc->log, WLOG_ERROR,
2493 "not expecting a SOFT_SYNC_RESPONSE_PDU as a client");
2494 return ERROR_INTERNAL_ERROR;
2495
2496 default:
2497 WLog_Print(drdynvc->log, WLOG_ERROR, "unknown drdynvc cmd 0x%x", Cmd);
2498 return ERROR_INTERNAL_ERROR;
2499 }
2500}
2501
2507WINPR_ATTR_NODISCARD
2508static UINT drdynvc_virtual_channel_event_data_received(drdynvcPlugin* drdynvc, void* pData,
2509 UINT32 dataLength, UINT32 totalLength,
2510 UINT32 dataFlags)
2511{
2512 wStream* data_in = nullptr;
2513
2514 WINPR_ASSERT(drdynvc);
2515 if ((dataFlags & CHANNEL_FLAG_SUSPEND) || (dataFlags & CHANNEL_FLAG_RESUME))
2516 {
2517 return CHANNEL_RC_OK;
2518 }
2519
2520 if (dataFlags & CHANNEL_FLAG_FIRST)
2521 {
2522 if (drdynvc->firstFlagReceived)
2523 return ERROR_INVALID_DATA;
2524 drdynvc->firstFlagReceived = TRUE;
2525
2526 DVCMAN* mgr = (DVCMAN*)drdynvc->channel_mgr;
2527 if (drdynvc->data_in)
2528 Stream_Release(drdynvc->data_in);
2529
2530 drdynvc->data_in = StreamPool_Take(mgr->pool, dataLength);
2531 drdynvc->totalLength = totalLength;
2532 }
2533
2534 if (!(data_in = drdynvc->data_in))
2535 {
2536 WLog_Print(drdynvc->log, WLOG_ERROR, "StreamPool_Take failed!");
2537 return CHANNEL_RC_NO_MEMORY;
2538 }
2539
2540 if (!Stream_EnsureRemainingCapacity(data_in, dataLength))
2541 {
2542 WLog_Print(drdynvc->log, WLOG_ERROR, "Stream_EnsureRemainingCapacity failed!");
2543 Stream_Release(drdynvc->data_in);
2544 drdynvc->data_in = nullptr;
2545 return ERROR_INTERNAL_ERROR;
2546 }
2547
2548 Stream_Write(data_in, pData, dataLength);
2549
2550 if ((Stream_GetPosition(data_in) > totalLength) || (drdynvc->totalLength != totalLength))
2551 {
2552 Stream_Release(drdynvc->data_in);
2553 drdynvc->data_in = nullptr;
2554 return ERROR_INVALID_DATA;
2555 }
2556
2557 if (dataFlags & CHANNEL_FLAG_LAST)
2558 {
2559 if (!drdynvc->firstFlagReceived)
2560 return ERROR_INVALID_DATA;
2561 drdynvc->firstFlagReceived = FALSE;
2562
2563 const size_t pos = Stream_GetPosition(data_in);
2564 if (drdynvc->totalLength != pos)
2565 {
2566 WLog_Print(drdynvc->log, WLOG_ERROR, "drdynvc_plugin_process_received: read error");
2567 return ERROR_INVALID_DATA;
2568 }
2569
2570 drdynvc->totalLength = 0;
2571 drdynvc->data_in = nullptr;
2572 Stream_SealLength(data_in);
2573 Stream_ResetPosition(data_in);
2574
2575 if (drdynvc->async)
2576 {
2577 if (!MessageQueue_Post(drdynvc->queue, nullptr, DRDYNVC_MSG_DATA, (void*)data_in,
2578 nullptr))
2579 {
2580 WLog_Print(drdynvc->log, WLOG_ERROR, "MessageQueue_Post failed!");
2581 return ERROR_INTERNAL_ERROR;
2582 }
2583 }
2584 else
2585 {
2586 UINT error = drdynvc_order_recv(drdynvc, data_in, TRUE, 0);
2587 Stream_Release(data_in);
2588
2589 if (error)
2590 {
2591 WLog_Print(drdynvc->log, WLOG_WARN,
2592 "drdynvc_order_recv failed with error %" PRIu32 "!", error);
2593 return error;
2594 }
2595 }
2596 }
2597
2598 return CHANNEL_RC_OK;
2599}
2600
2601static void VCAPITYPE drdynvc_virtual_channel_open_event_ex(LPVOID lpUserParam, DWORD openHandle,
2602 UINT event, LPVOID pData,
2603 UINT32 dataLength, UINT32 totalLength,
2604 UINT32 dataFlags)
2605{
2606 UINT error = CHANNEL_RC_OK;
2607 drdynvcPlugin* drdynvc = (drdynvcPlugin*)lpUserParam;
2608
2609 WINPR_ASSERT(drdynvc);
2610 switch (event)
2611 {
2612 case CHANNEL_EVENT_DATA_RECEIVED:
2613 if (!drdynvc || (drdynvc->OpenHandle != openHandle))
2614 {
2615 WLog_ERR(TAG, "drdynvc_virtual_channel_open_event: error no match");
2616 return;
2617 }
2618 if ((error = drdynvc_virtual_channel_event_data_received(drdynvc, pData, dataLength,
2619 totalLength, dataFlags)))
2620 WLog_Print(drdynvc->log, WLOG_ERROR,
2621 "drdynvc_virtual_channel_event_data_received failed with error %" PRIu32
2622 "",
2623 error);
2624
2625 break;
2626
2627 case CHANNEL_EVENT_WRITE_CANCELLED:
2628 case CHANNEL_EVENT_WRITE_COMPLETE:
2629 {
2630 wStream* s = (wStream*)pData;
2631 Stream_Release(s);
2632 }
2633 break;
2634
2635 case CHANNEL_EVENT_USER:
2636 break;
2637 default:
2638 break;
2639 }
2640
2641 if (error && drdynvc && drdynvc->rdpcontext)
2642 setChannelError(drdynvc->rdpcontext, error,
2643 "drdynvc_virtual_channel_open_event reported an error");
2644}
2645
2646WINPR_ATTR_NODISCARD
2647static DWORD WINAPI drdynvc_virtual_channel_client_thread(LPVOID arg)
2648{
2649 /* TODO: rewrite this */
2650 wStream* data = nullptr;
2651 wMessage message = WINPR_C_ARRAY_INIT;
2652 UINT error = CHANNEL_RC_OK;
2653 drdynvcPlugin* drdynvc = (drdynvcPlugin*)arg;
2654
2655 if (!drdynvc)
2656 {
2657 ExitThread((DWORD)CHANNEL_RC_BAD_CHANNEL_HANDLE);
2658 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
2659 }
2660
2661 while (1)
2662 {
2663 if (!MessageQueue_Wait(drdynvc->queue))
2664 {
2665 WLog_Print(drdynvc->log, WLOG_ERROR, "MessageQueue_Wait failed!");
2666 error = ERROR_INTERNAL_ERROR;
2667 break;
2668 }
2669
2670 if (!MessageQueue_Peek(drdynvc->queue, &message, TRUE))
2671 {
2672 WLog_Print(drdynvc->log, WLOG_ERROR, "MessageQueue_Peek failed!");
2673 error = ERROR_INTERNAL_ERROR;
2674 break;
2675 }
2676
2677 if (message.id == WMQ_QUIT)
2678 break;
2679
2680 if (message.id == DRDYNVC_MSG_DATA)
2681 {
2682 UINT32 ThreadingFlags = TRUE;
2683 data = (wStream*)message.wParam;
2684
2685 if ((error = drdynvc_order_recv(drdynvc, data, ThreadingFlags, 0)))
2686 {
2687 WLog_Print(drdynvc->log, WLOG_WARN,
2688 "drdynvc_order_recv failed with error %" PRIu32 "!", error);
2689 }
2690
2691 Stream_Release(data);
2692 }
2693 else if (message.id == DRDYNVC_MSG_TUNNEL_DATA)
2694 {
2695 data = (wStream*)message.wParam;
2696 const UINT32 tunnelType = WINPR_ASSERTING_INT_CAST(UINT32, (UINT_PTR)message.lParam);
2697
2698 if ((error = drdynvc_receive_tunnel_pdu(drdynvc, data, tunnelType)))
2699 {
2700 WLog_Print(drdynvc->log, WLOG_WARN,
2701 "drdynvc_receive_tunnel_pdu failed with error %" PRIu32 "!", error);
2702 }
2703 }
2704 else if (message.id == DRDYNVC_MSG_TUNNEL_STATE)
2705 {
2706 if ((error = drdynvc_tunnel_state_changed(drdynvc)))
2707 {
2708 WLog_Print(drdynvc->log, WLOG_WARN,
2709 "drdynvc_tunnel_state_changed failed with error %" PRIu32 "!", error);
2710 }
2711 }
2712 }
2713
2714 {
2715 /* Disconnect remaining dynamic channels that the server did not.
2716 * This is required to properly shut down channels by calling the appropriate
2717 * event handlers. */
2718 DVCMAN* drdynvcMgr = (DVCMAN*)drdynvc->channel_mgr;
2719
2720 HashTable_Clear(drdynvcMgr->channelsById);
2721 }
2722
2723 if (error && drdynvc->rdpcontext)
2724 setChannelError(drdynvc->rdpcontext, error,
2725 "drdynvc_virtual_channel_client_thread reported an error");
2726
2727 ExitThread((DWORD)error);
2728 return error;
2729}
2730
2731static void drdynvc_queue_object_free(void* obj)
2732{
2733 wStream* s = nullptr;
2734 wMessage* msg = (wMessage*)obj;
2735
2736 if (!msg || ((msg->id != DRDYNVC_MSG_DATA) && (msg->id != DRDYNVC_MSG_TUNNEL_DATA)))
2737 return;
2738
2739 s = (wStream*)msg->wParam;
2740
2741 if (s)
2742 Stream_Release(s);
2743}
2744
2745/* Multitransport hooks, called on the thread running the main loop, and tunnel data on the
2746 * tunnel thread when the plugin has its own thread. Like data from the static channel, a failure
2747 * here ends the connection. */
2748WINPR_ATTR_NODISCARD
2749static BOOL drdynvc_on_tunnel_data(void* custom, UINT32 tunnelType, wStream* s)
2750{
2751 drdynvcPlugin* drdynvc = custom;
2752 WINPR_ASSERT(drdynvc);
2753 WINPR_ASSERT(s);
2754
2755 if (!drdynvc->channel_mgr || (Stream_Length(s) == 0))
2756 {
2757 Stream_Release(s);
2758 return TRUE;
2759 }
2760
2761 if (drdynvc->async)
2762 {
2763 if (!MessageQueue_Post(drdynvc->queue, nullptr, DRDYNVC_MSG_TUNNEL_DATA, s,
2764 (void*)(UINT_PTR)tunnelType))
2765 {
2766 WLog_Print(drdynvc->log, WLOG_ERROR, "MessageQueue_Post failed!");
2767 Stream_Release(s);
2768 return FALSE;
2769 }
2770 return TRUE;
2771 }
2772
2773 const UINT error = drdynvc_receive_tunnel_pdu(drdynvc, s, tunnelType);
2774 if (error)
2775 {
2776 WLog_Print(drdynvc->log, WLOG_WARN,
2777 "drdynvc_receive_tunnel_pdu failed with error %" PRIu32 "!", error);
2778 return FALSE;
2779 }
2780 return TRUE;
2781}
2782
2783WINPR_ATTR_NODISCARD
2784static BOOL drdynvc_on_tunnel_state(void* custom, UINT32 tunnelType,
2785 FreeRDP_MultitransportTunnelState state)
2786{
2787 drdynvcPlugin* drdynvc = custom;
2788 WINPR_ASSERT(drdynvc);
2789
2790 WLog_Print(drdynvc->log, WLOG_DEBUG, "tunnel 0x%08" PRIx32 " is %s", tunnelType,
2791 (state == FREERDP_MULTITRANSPORT_TUNNEL_READY) ? "ready" : "gone");
2792
2793 if (drdynvc->async)
2794 {
2795 if (!MessageQueue_Post(drdynvc->queue, nullptr, DRDYNVC_MSG_TUNNEL_STATE, nullptr, nullptr))
2796 {
2797 WLog_Print(drdynvc->log, WLOG_ERROR, "MessageQueue_Post failed!");
2798 return FALSE;
2799 }
2800 return TRUE;
2801 }
2802
2803 const UINT error = drdynvc_tunnel_state_changed(drdynvc);
2804 if (error)
2805 {
2806 WLog_Print(drdynvc->log, WLOG_WARN,
2807 "drdynvc_tunnel_state_changed failed with error %" PRIu32 "!", error);
2808 return FALSE;
2809 }
2810 return TRUE;
2811}
2812
2813static void drdynvc_soft_sync_reset(drdynvcPlugin* drdynvc)
2814{
2815 WINPR_ASSERT(drdynvc);
2816 drdynvc->softSyncDone = FALSE;
2817 Stream_Free(drdynvc->pendingSoftSync, TRUE);
2818 drdynvc->pendingSoftSync = nullptr;
2819 drdynvc_tunnel_backlog_clear(drdynvc);
2820}
2821
2822/* After this no tunnel event reaches the plugin. */
2823static void drdynvc_tunnel_hooks_remove(drdynvcPlugin* drdynvc)
2824{
2825 WINPR_ASSERT(drdynvc);
2826
2827 /* fails only without a multitransport, then nothing was registered */
2828 if (!freerdp_multitransport_set_dvc_callbacks(drdynvc->rdpcontext, nullptr, nullptr))
2829 WLog_Print(drdynvc->log, WLOG_DEBUG, "no multitransport tunnel hooks to remove");
2830}
2831
2832WINPR_ATTR_NODISCARD
2833static UINT drdynvc_virtual_channel_event_initialized(drdynvcPlugin* drdynvc, LPVOID pData,
2834 UINT32 dataLength)
2835{
2836 wObject* obj = nullptr;
2837 WINPR_UNUSED(pData);
2838 WINPR_UNUSED(dataLength);
2839
2840 if (!drdynvc)
2841 goto error;
2842
2843 drdynvc->queue = MessageQueue_New(nullptr);
2844
2845 if (!drdynvc->queue)
2846 {
2847 WLog_Print(drdynvc->log, WLOG_ERROR, "MessageQueue_New failed!");
2848 goto error;
2849 }
2850
2851 obj = MessageQueue_Object(drdynvc->queue);
2852 obj->fnObjectFree = drdynvc_queue_object_free;
2853 drdynvc->channel_mgr = dvcman_new(drdynvc);
2854
2855 if (!drdynvc->channel_mgr)
2856 {
2857 WLog_Print(drdynvc->log, WLOG_ERROR, "dvcman_new failed!");
2858 goto error;
2859 }
2860
2861 return CHANNEL_RC_OK;
2862error:
2863 return ERROR_INTERNAL_ERROR;
2864}
2865
2871WINPR_ATTR_NODISCARD
2872static UINT drdynvc_virtual_channel_event_connected(drdynvcPlugin* drdynvc, LPVOID pData,
2873 UINT32 dataLength)
2874{
2875 UINT error = 0;
2876 UINT32 status = 0;
2877 rdpSettings* settings = nullptr;
2878
2879 WINPR_ASSERT(drdynvc);
2880 WINPR_UNUSED(pData);
2881 WINPR_UNUSED(dataLength);
2882
2883 if (!drdynvc)
2884 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
2885
2886 WINPR_ASSERT(drdynvc->channelEntryPoints.pVirtualChannelOpenEx);
2887 status = drdynvc->channelEntryPoints.pVirtualChannelOpenEx(
2888 drdynvc->InitHandle, &drdynvc->OpenHandle, drdynvc->channelDef.name,
2889 drdynvc_virtual_channel_open_event_ex);
2890
2891 if (status != CHANNEL_RC_OK)
2892 {
2893 WLog_Print(drdynvc->log, WLOG_ERROR, "pVirtualChannelOpen failed with %s [%08" PRIX32 "]",
2894 WTSErrorToString(status), status);
2895 return status;
2896 }
2897
2898 WINPR_ASSERT(drdynvc->rdpcontext);
2899 settings = drdynvc->rdpcontext->settings;
2900 WINPR_ASSERT(settings);
2901
2902 for (UINT32 index = 0;
2903 index < freerdp_settings_get_uint32(settings, FreeRDP_DynamicChannelCount); index++)
2904 {
2905 const ADDIN_ARGV* args =
2906 freerdp_settings_get_pointer_array(settings, FreeRDP_DynamicChannelArray, index);
2907 error = dvcman_load_addin(drdynvc, drdynvc->channel_mgr, args, drdynvc->rdpcontext);
2908
2909 if (CHANNEL_RC_OK != error)
2910 goto error;
2911 }
2912
2913 if ((error = dvcman_init(drdynvc, drdynvc->channel_mgr)))
2914 {
2915 WLog_Print(drdynvc->log, WLOG_ERROR, "dvcman_init failed with error %" PRIu32 "!", error);
2916 goto error;
2917 }
2918
2919 drdynvc->state = DRDYNVC_STATE_CAPABILITIES;
2920
2921 if (drdynvc->async)
2922 {
2923 if (!(drdynvc->thread = CreateThread(nullptr, 0, drdynvc_virtual_channel_client_thread,
2924 (void*)drdynvc, 0, nullptr)))
2925 {
2926 error = ERROR_INTERNAL_ERROR;
2927 WLog_Print(drdynvc->log, WLOG_ERROR, "CreateThread failed!");
2928 goto error;
2929 }
2930
2931 if (!SetThreadPriority(drdynvc->thread, THREAD_PRIORITY_HIGHEST))
2932 WLog_Print(drdynvc->log, WLOG_WARN, "SetThreadPriority failed, ignoring.");
2933 }
2934
2935 {
2936 drdynvc_soft_sync_reset(drdynvc);
2937 const FreeRDP_MultitransportDvcCallbacks callbacks = {
2938 .TunnelDataReceived = drdynvc_on_tunnel_data,
2939 .TunnelStateChanged = drdynvc_on_tunnel_state,
2940 .DataOnTunnelThread = drdynvc->async
2941 };
2942 if (!freerdp_multitransport_set_dvc_callbacks(drdynvc->rdpcontext, &callbacks, drdynvc))
2943 WLog_Print(drdynvc->log, WLOG_WARN,
2944 "multitransport tunnels unavailable, using the static channel only");
2945 }
2946
2947error:
2948 return error;
2949}
2950
2956WINPR_ATTR_NODISCARD
2957static UINT drdynvc_virtual_channel_event_disconnected(drdynvcPlugin* drdynvc)
2958{
2959 UINT status = 0;
2960
2961 if (!drdynvc)
2962 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
2963
2964 /* registered on initialization, whether the channel opened or not */
2965 drdynvc_tunnel_hooks_remove(drdynvc);
2966
2967 if (drdynvc->OpenHandle == 0)
2968 return CHANNEL_RC_OK;
2969
2970 if (drdynvc->queue)
2971 {
2972 if (!MessageQueue_PostQuit(drdynvc->queue, 0))
2973 {
2974 status = GetLastError();
2975 WLog_Print(drdynvc->log, WLOG_ERROR,
2976 "MessageQueue_PostQuit failed with error %" PRIu32 "", status);
2977 return status;
2978 }
2979 }
2980
2981 if (drdynvc->thread)
2982 {
2983 if (WaitForSingleObject(drdynvc->thread, INFINITE) != WAIT_OBJECT_0)
2984 {
2985 status = GetLastError();
2986 WLog_Print(drdynvc->log, WLOG_ERROR,
2987 "WaitForSingleObject failed with error %" PRIu32 "", status);
2988 return status;
2989 }
2990
2991 (void)CloseHandle(drdynvc->thread);
2992 drdynvc->thread = nullptr;
2993 }
2994 else
2995 {
2996 {
2997 /* Disconnect remaining dynamic channels that the server did not.
2998 * This is required to properly shut down channels by calling the appropriate
2999 * event handlers. */
3000 DVCMAN* drdynvcMgr = (DVCMAN*)drdynvc->channel_mgr;
3001
3002 HashTable_Clear(drdynvcMgr->channelsById);
3003 }
3004 }
3005
3006 WINPR_ASSERT(drdynvc->channelEntryPoints.pVirtualChannelCloseEx);
3007 status = drdynvc->channelEntryPoints.pVirtualChannelCloseEx(drdynvc->InitHandle,
3008 drdynvc->OpenHandle);
3009
3010 if (status != CHANNEL_RC_OK)
3011 {
3012 WLog_Print(drdynvc->log, WLOG_ERROR, "pVirtualChannelClose failed with %s [%08" PRIX32 "]",
3013 WTSErrorToString(status), status);
3014 }
3015
3016 drdynvc_soft_sync_reset(drdynvc);
3017 dvcman_clear(drdynvc, drdynvc->channel_mgr);
3018 if (drdynvc->queue)
3019 MessageQueue_Clear(drdynvc->queue);
3020 drdynvc->OpenHandle = 0;
3021
3022 if (drdynvc->data_in)
3023 {
3024 Stream_Release(drdynvc->data_in);
3025 drdynvc->data_in = nullptr;
3026 }
3027
3028 return status;
3029}
3030
3036WINPR_ATTR_NODISCARD
3037static UINT drdynvc_virtual_channel_event_terminated(drdynvcPlugin* drdynvc)
3038{
3039 if (!drdynvc)
3040 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
3041
3042 /* the hooks point at this plugin, which is freed below */
3043 drdynvc_tunnel_hooks_remove(drdynvc);
3044
3045 MessageQueue_Free(drdynvc->queue);
3046 drdynvc->queue = nullptr;
3047
3048 drdynvc_soft_sync_reset(drdynvc);
3049 free((void*)drdynvc->tunnelBacklog);
3050 free(drdynvc->tunnelBacklogTypes);
3051
3052 if (drdynvc->channel_mgr)
3053 {
3054 dvcman_free(drdynvc, drdynvc->channel_mgr);
3055 drdynvc->channel_mgr = nullptr;
3056 }
3057 drdynvc->InitHandle = nullptr;
3058 free(drdynvc->context);
3059 free(drdynvc);
3060 return CHANNEL_RC_OK;
3061}
3062
3063WINPR_ATTR_NODISCARD
3064static UINT drdynvc_virtual_channel_event_attached(drdynvcPlugin* drdynvc)
3065{
3066 UINT error = CHANNEL_RC_OK;
3067 DVCMAN* dvcman = nullptr;
3068
3069 if (!drdynvc)
3070 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
3071
3072 dvcman = (DVCMAN*)drdynvc->channel_mgr;
3073
3074 if (!dvcman)
3075 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
3076
3077 ArrayList_Lock(dvcman->plugins);
3078 for (size_t i = 0; i < ArrayList_Count(dvcman->plugins); i++)
3079 {
3080 IWTSPlugin* pPlugin = ArrayList_GetItem(dvcman->plugins, i);
3081
3082 error = IFCALLRESULT(CHANNEL_RC_OK, pPlugin->Attached, pPlugin);
3083 if (error != CHANNEL_RC_OK)
3084 {
3085 WLog_Print(drdynvc->log, WLOG_ERROR, "Attach failed with error %" PRIu32 "!", error);
3086 goto fail;
3087 }
3088 }
3089
3090fail:
3091 ArrayList_Unlock(dvcman->plugins);
3092 return error;
3093}
3094
3095WINPR_ATTR_NODISCARD
3096static UINT drdynvc_virtual_channel_event_detached(drdynvcPlugin* drdynvc)
3097{
3098 UINT error = CHANNEL_RC_OK;
3099 DVCMAN* dvcman = nullptr;
3100
3101 if (!drdynvc)
3102 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
3103
3104 dvcman = (DVCMAN*)drdynvc->channel_mgr;
3105
3106 if (!dvcman)
3107 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
3108
3109 ArrayList_Lock(dvcman->plugins);
3110 for (size_t i = 0; i < ArrayList_Count(dvcman->plugins); i++)
3111 {
3112 IWTSPlugin* pPlugin = ArrayList_GetItem(dvcman->plugins, i);
3113
3114 error = IFCALLRESULT(CHANNEL_RC_OK, pPlugin->Detached, pPlugin);
3115 if (error != CHANNEL_RC_OK)
3116 {
3117 WLog_Print(drdynvc->log, WLOG_ERROR, "Detach failed with error %" PRIu32 "!", error);
3118 goto fail;
3119 }
3120 }
3121
3122fail:
3123 ArrayList_Unlock(dvcman->plugins);
3124
3125 return error;
3126}
3127
3128static VOID VCAPITYPE drdynvc_virtual_channel_init_event_ex(LPVOID lpUserParam, LPVOID pInitHandle,
3129 UINT event, LPVOID pData,
3130 UINT dataLength)
3131{
3132 UINT error = CHANNEL_RC_OK;
3133 drdynvcPlugin* drdynvc = (drdynvcPlugin*)lpUserParam;
3134
3135 if (!drdynvc || (drdynvc->InitHandle != pInitHandle))
3136 {
3137 WLog_ERR(TAG, "drdynvc_virtual_channel_init_event: error no match");
3138 return;
3139 }
3140
3141 switch (event)
3142 {
3143 case CHANNEL_EVENT_INITIALIZED:
3144 error = drdynvc_virtual_channel_event_initialized(drdynvc, pData, dataLength);
3145 break;
3146 case CHANNEL_EVENT_CONNECTED:
3147 if ((error = drdynvc_virtual_channel_event_connected(drdynvc, pData, dataLength)))
3148 WLog_Print(drdynvc->log, WLOG_ERROR,
3149 "drdynvc_virtual_channel_event_connected failed with error %" PRIu32 "",
3150 error);
3151
3152 break;
3153
3154 case CHANNEL_EVENT_DISCONNECTED:
3155 if ((error = drdynvc_virtual_channel_event_disconnected(drdynvc)))
3156 WLog_Print(drdynvc->log, WLOG_ERROR,
3157 "drdynvc_virtual_channel_event_disconnected failed with error %" PRIu32
3158 "",
3159 error);
3160
3161 break;
3162
3163 case CHANNEL_EVENT_TERMINATED:
3164 if ((error = drdynvc_virtual_channel_event_terminated(drdynvc)))
3165 WLog_Print(drdynvc->log, WLOG_ERROR,
3166 "drdynvc_virtual_channel_event_terminated failed with error %" PRIu32 "",
3167 error);
3168
3169 break;
3170
3171 case CHANNEL_EVENT_ATTACHED:
3172 if ((error = drdynvc_virtual_channel_event_attached(drdynvc)))
3173 WLog_Print(drdynvc->log, WLOG_ERROR,
3174 "drdynvc_virtual_channel_event_attached failed with error %" PRIu32 "",
3175 error);
3176
3177 break;
3178
3179 case CHANNEL_EVENT_DETACHED:
3180 if ((error = drdynvc_virtual_channel_event_detached(drdynvc)))
3181 WLog_Print(drdynvc->log, WLOG_ERROR,
3182 "drdynvc_virtual_channel_event_detached failed with error %" PRIu32 "",
3183 error);
3184
3185 break;
3186
3187 default:
3188 break;
3189 }
3190
3191 if (error && drdynvc->rdpcontext)
3192 setChannelError(drdynvc->rdpcontext, error,
3193 "drdynvc_virtual_channel_init_event_ex reported an error");
3194}
3195
3200WINPR_ATTR_NODISCARD
3201static int drdynvc_get_version(DrdynvcClientContext* context)
3202{
3203 WINPR_ASSERT(context);
3204 drdynvcPlugin* drdynvc = (drdynvcPlugin*)context->handle;
3205 WINPR_ASSERT(drdynvc);
3206 return drdynvc->version;
3207}
3208
3209/* drdynvc is always built-in */
3210#define VirtualChannelEntryEx drdynvc_VirtualChannelEntryEx
3211
3212FREERDP_ENTRY_POINT(BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS_EX pEntryPoints,
3213 PVOID pInitHandle))
3214{
3215 UINT rc = 0;
3216 drdynvcPlugin* drdynvc = nullptr;
3217 DrdynvcClientContext* context = nullptr;
3218 CHANNEL_ENTRY_POINTS_FREERDP_EX* pEntryPointsEx = nullptr;
3219 drdynvc = (drdynvcPlugin*)calloc(1, sizeof(drdynvcPlugin));
3220
3221 WINPR_ASSERT(pEntryPoints);
3222 if (!drdynvc)
3223 {
3224 WLog_ERR(TAG, "calloc failed!");
3225 return FALSE;
3226 }
3227
3228 drdynvc->channelDef.options =
3229 CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP | CHANNEL_OPTION_COMPRESS_RDP;
3230 (void)sprintf_s(drdynvc->channelDef.name, ARRAYSIZE(drdynvc->channelDef.name),
3231 DRDYNVC_SVC_CHANNEL_NAME);
3232 drdynvc->state = DRDYNVC_STATE_INITIAL;
3233 pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP_EX*)pEntryPoints;
3234
3235 if ((pEntryPointsEx->cbSize >= sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX)) &&
3236 (pEntryPointsEx->MagicNumber == FREERDP_CHANNEL_MAGIC_NUMBER))
3237 {
3238 context = (DrdynvcClientContext*)calloc(1, sizeof(DrdynvcClientContext));
3239
3240 if (!context)
3241 {
3242 WLog_Print(drdynvc->log, WLOG_ERROR, "calloc failed!");
3243 free(drdynvc);
3244 return FALSE;
3245 }
3246
3247 context->handle = (void*)drdynvc;
3248 context->custom = nullptr;
3249 drdynvc->context = context;
3250 context->GetVersion = drdynvc_get_version;
3251 context->GetChannelStats = drdynvc_get_channel_stats;
3252 drdynvc->rdpcontext = pEntryPointsEx->context;
3253 if (!freerdp_settings_get_bool(drdynvc->rdpcontext->settings,
3254 FreeRDP_TransportDumpReplay) &&
3255 !freerdp_settings_get_bool(drdynvc->rdpcontext->settings,
3256 FreeRDP_SynchronousDynamicChannels))
3257 drdynvc->async = TRUE;
3258 }
3259
3260 drdynvc->log = WLog_Get(TAG);
3261 WLog_Print(drdynvc->log, WLOG_DEBUG, "VirtualChannelEntryEx");
3262 CopyMemory(&(drdynvc->channelEntryPoints), pEntryPoints,
3264 drdynvc->InitHandle = pInitHandle;
3265
3266 WINPR_ASSERT(drdynvc->channelEntryPoints.pVirtualChannelInitEx);
3267 rc = drdynvc->channelEntryPoints.pVirtualChannelInitEx(
3268 drdynvc, context, pInitHandle, &drdynvc->channelDef, 1, VIRTUAL_CHANNEL_VERSION_WIN2000,
3269 drdynvc_virtual_channel_init_event_ex);
3270
3271 if (CHANNEL_RC_OK != rc)
3272 {
3273 WLog_Print(drdynvc->log, WLOG_ERROR, "pVirtualChannelInit failed with %s [%08" PRIX32 "]",
3274 WTSErrorToString(rc), rc);
3275 free(drdynvc->context);
3276 free(drdynvc);
3277 return FALSE;
3278 }
3279
3280 drdynvc->channelEntryPoints.pInterface = context;
3281 return TRUE;
3282}
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.
Dynamic virtual channel hooks of the client side tunnels.
WINPR_ATTR_NODISCARD BOOL(* TunnelDataReceived)(void *custom, UINT32 tunnelType, wStream *s)
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