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