22#include <freerdp/config.h>
25#include <winpr/cast.h>
26#include <winpr/stream.h>
27#include <winpr/interlocked.h>
29#include <freerdp/freerdp.h>
30#include <freerdp/channels/drdynvc.h>
31#include <freerdp/utils/drdynvc.h>
32#include <freerdp/codec/zgfx.h>
34#include "drdynvc_main.h"
36#define TAG CHANNELS_TAG("drdynvc.client")
39static const char* channel_state2str(DVC_CHANNEL_STATE state)
43 case DVC_CHANNEL_INIT:
44 return "DVC_CHANNEL_INIT";
45 case DVC_CHANNEL_RUNNING:
46 return "DVC_CHANNEL_RUNNING";
47 case DVC_CHANNEL_CLOSED:
48 return "DVC_CHANNEL_CLOSED";
50 return "DVC_CHANNEL_UNKNOWN";
56static UINT dvcman_channel_close(
DVCMAN_CHANNEL* channel, BOOL perRequest, BOOL fromHashTableFn);
57static void dvcman_free(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr);
60static UINT drdynvc_write_data(drdynvcPlugin* drdynvc, UINT32 ChannelId,
const BYTE* data,
69 free(listener->channel_name);
79static UINT dvcman_get_configuration(IWTSListener* pListener,
void** ppPropertyBag)
81 WINPR_ASSERT(ppPropertyBag);
82 WINPR_UNUSED(pListener);
83 *ppPropertyBag =
nullptr;
84 return ERROR_INTERNAL_ERROR;
93static UINT dvcman_create_listener(IWTSVirtualChannelManager* pChannelMgr,
94 const char* pszChannelName, ULONG ulFlags,
95 IWTSListenerCallback* pListenerCallback,
96 IWTSListener** ppListener)
101 WINPR_ASSERT(dvcman);
102 WLog_DBG(TAG,
"create_listener: %" PRIuz
".%s.", HashTable_Count(dvcman->listeners) + 1,
108 WLog_ERR(TAG,
"calloc failed!");
109 return CHANNEL_RC_NO_MEMORY;
112 listener->iface.GetConfiguration = dvcman_get_configuration;
113 listener->iface.pInterface =
nullptr;
114 listener->dvcman = dvcman;
115 listener->channel_name = _strdup(pszChannelName);
117 if (!listener->channel_name)
119 WLog_ERR(TAG,
"_strdup failed!");
120 dvcman_wtslistener_free(listener);
121 return CHANNEL_RC_NO_MEMORY;
124 listener->flags = ulFlags;
125 listener->listener_callback = pListenerCallback;
128 *ppListener = (IWTSListener*)listener;
130 if (!HashTable_Insert(dvcman->listeners, listener->channel_name, listener))
132 dvcman_wtslistener_free(listener);
133 return ERROR_INTERNAL_ERROR;
137 return CHANNEL_RC_OK;
141static UINT dvcman_destroy_listener(IWTSVirtualChannelManager* pChannelMgr, IWTSListener* pListener)
145 WINPR_UNUSED(pChannelMgr);
149 DVCMAN* dvcman = listener->dvcman;
151 HashTable_Remove(dvcman->listeners, listener->channel_name);
154 return CHANNEL_RC_OK;
163static UINT dvcman_register_plugin(IDRDYNVC_ENTRY_POINTS* pEntryPoints,
const char* name,
166 WINPR_ASSERT(pEntryPoints);
169 WINPR_ASSERT(dvcman);
170 if (!ArrayList_Append(dvcman->plugin_names, name))
171 return ERROR_INTERNAL_ERROR;
172 if (!ArrayList_Append(dvcman->plugins, pPlugin))
173 return ERROR_INTERNAL_ERROR;
175 WLog_DBG(TAG,
"register_plugin: num_plugins %" PRIuz, ArrayList_Count(dvcman->plugins));
176 return CHANNEL_RC_OK;
180static IWTSPlugin* dvcman_get_plugin(IDRDYNVC_ENTRY_POINTS* pEntryPoints,
const char* name)
182 IWTSPlugin* plugin =
nullptr;
185 WINPR_ASSERT(pEntryPoints);
187 if (!dvcman || !pEntryPoints || !name)
190 nc = ArrayList_Count(dvcman->plugin_names);
191 pc = ArrayList_Count(dvcman->plugins);
195 ArrayList_Lock(dvcman->plugin_names);
196 ArrayList_Lock(dvcman->plugins);
197 for (
size_t i = 0; i < pc; i++)
199 const char* cur = ArrayList_GetItem(dvcman->plugin_names, i);
200 if (strcmp(cur, name) == 0)
202 plugin = ArrayList_GetItem(dvcman->plugins, i);
206 ArrayList_Unlock(dvcman->plugin_names);
207 ArrayList_Unlock(dvcman->plugins);
212static const ADDIN_ARGV* dvcman_get_plugin_data(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
214 WINPR_ASSERT(pEntryPoints);
219static rdpContext* dvcman_get_rdp_context(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
223 return entry->context;
227static rdpSettings* dvcman_get_rdp_settings(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
229 rdpContext* context = dvcman_get_rdp_context(pEntryPoints);
230 WINPR_ASSERT(context);
232 return context->settings;
236static UINT32 dvcman_get_channel_id(IWTSVirtualChannel* channel)
240 return dvc->channel_id;
244static const char* dvcman_get_channel_name(IWTSVirtualChannel* channel)
248 return dvc->channel_name;
252static DVCMAN_CHANNEL* dvcman_get_channel_by_id(IWTSVirtualChannelManager* pChannelMgr,
253 UINT32 ChannelId, BOOL doRef)
258 WINPR_ASSERT(dvcman);
259 HashTable_Lock(dvcman->channelsById);
260 dvcChannel = HashTable_GetItemValue(dvcman->channelsById, &ChannelId);
264 InterlockedIncrement(&dvcChannel->refCounter);
267 HashTable_Unlock(dvcman->channelsById);
272static void dvcman_return_channel(IWTSVirtualChannelManager* pChannelMgr,
DVCMAN_CHANNEL* channel)
279 WINPR_ASSERT(dvcman);
280 HashTable_Unlock(dvcman->channelsById);
284static IWTSVirtualChannel* dvcman_find_channel_by_id(IWTSVirtualChannelManager* pChannelMgr,
287 DVCMAN_CHANNEL* channel = dvcman_get_channel_by_id(pChannelMgr, ChannelId, FALSE);
290 dvcman_return_channel(pChannelMgr, channel);
292 return &channel->iface;
295static void dvcman_plugin_terminate(
void* plugin)
297 IWTSPlugin* pPlugin = plugin;
299 WINPR_ASSERT(pPlugin);
300 UINT error = IFCALLRESULT(CHANNEL_RC_OK, pPlugin->Terminated, pPlugin);
301 if (error != CHANNEL_RC_OK)
302 WLog_ERR(TAG,
"Terminated failed with error %" PRIu32
"!", error);
305static void wts_listener_free(
void* arg)
308 dvcman_wtslistener_free(listener);
312static BOOL channelIdMatch(
const void* k1,
const void* k2)
316 return *((
const UINT32*)k1) == *((
const UINT32*)k2);
320static UINT32 channelIdHash(
const void*
id)
323 return *((
const UINT32*)
id);
326static void channelByIdCleanerFn(
void* value)
331 dvcman_channel_close(channel, FALSE, TRUE);
332 dvcman_channel_free(channel);
336WINPR_ATTR_MALLOC(dvcman_free, 2)
337static IWTSVirtualChannelManager* dvcman_new(drdynvcPlugin* plugin)
345 dvcman->iface.CreateListener = dvcman_create_listener;
346 dvcman->iface.DestroyListener = dvcman_destroy_listener;
347 dvcman->iface.FindChannelById = dvcman_find_channel_by_id;
348 dvcman->iface.GetChannelId = dvcman_get_channel_id;
349 dvcman->iface.GetChannelName = dvcman_get_channel_name;
350 dvcman->drdynvc = plugin;
351 dvcman->channelsById = HashTable_New(TRUE);
353 if (!dvcman->channelsById)
356 if (!HashTable_SetHashFunction(dvcman->channelsById, channelIdHash))
359 obj = HashTable_KeyObject(dvcman->channelsById);
363 obj = HashTable_ValueObject(dvcman->channelsById);
367 dvcman->pool = StreamPool_New(TRUE, 10);
371 dvcman->listeners = HashTable_New(TRUE);
372 if (!dvcman->listeners)
375 if (!HashTable_SetHashFunction(dvcman->listeners, HashTable_StringHash))
378 obj = HashTable_KeyObject(dvcman->listeners);
381 obj = HashTable_ValueObject(dvcman->listeners);
384 dvcman->plugin_names = ArrayList_New(TRUE);
385 if (!dvcman->plugin_names)
387 obj = ArrayList_Object(dvcman->plugin_names);
391 dvcman->plugins = ArrayList_New(TRUE);
392 if (!dvcman->plugins)
394 obj = ArrayList_Object(dvcman->plugins);
396 return &dvcman->iface;
398 dvcman_free(plugin, &dvcman->iface);
408static UINT dvcman_load_addin(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr,
411 WINPR_ASSERT(drdynvc);
412 WINPR_ASSERT(pChannelMgr);
414 WINPR_ASSERT(context);
416 WLog_Print(drdynvc->log, WLOG_INFO,
"Loading Dynamic Virtual Channel %s", args->argv[0]);
418 PVIRTUALCHANNELENTRY pvce = freerdp_load_channel_addin_entry(args->argv[0],
nullptr,
nullptr,
419 FREERDP_ADDIN_CHANNEL_DYNAMIC);
420 PDVC_PLUGIN_ENTRY pDVCPluginEntry = WINPR_FUNC_PTR_CAST(pvce, PDVC_PLUGIN_ENTRY);
426 entryPoints.iface.RegisterPlugin = dvcman_register_plugin;
427 entryPoints.iface.GetPlugin = dvcman_get_plugin;
428 entryPoints.iface.GetPluginData = dvcman_get_plugin_data;
429 entryPoints.iface.GetRdpSettings = dvcman_get_rdp_settings;
430 entryPoints.iface.GetRdpContext = dvcman_get_rdp_context;
431 entryPoints.dvcman = (
DVCMAN*)pChannelMgr;
432 entryPoints.args = args;
433 entryPoints.context = context;
434 return pDVCPluginEntry(&entryPoints.iface);
437 return ERROR_INVALID_FUNCTION;
447 drdynvcPlugin* plugin = channel->dvcman->drdynvc;
450 rdpContext* context = plugin->rdpcontext;
453 ChannelTerminatedEventArgs e = WINPR_C_ARRAY_INIT;
454 EventArgsInit(&e,
"freerdp");
455 e.name = channel->channel_name;
456 e.pInterface = channel->pInterface;
458 const int rc = PubSub_OnChannelTerminated(context->pubSub, context, &e);
460 WLog_WARN(TAG,
"PubSub_OnChannelTerminated(%s) failed", channel->channel_name);
465 if (channel->dvc_data)
466 Stream_Release(channel->dvc_data);
468 zgfx_context_free(channel->decompressor);
469 DeleteCriticalSection(&(channel->lock));
470 free(channel->channel_name);
476 WINPR_ASSERT(channel);
477 if (InterlockedDecrement(&channel->refCounter))
480 DVCMAN* dvcman = channel->dvcman;
482 HashTable_Remove(dvcman->channelsById, &channel->channel_id);
488 WINPR_ASSERT(channel);
489 DVCMAN* dvcman = channel->dvcman;
490 drdynvcPlugin* drdynvc = dvcman->drdynvc;
491 wStream* s = StreamPool_Take(dvcman->pool, 5);
495 WLog_Print(drdynvc->log, WLOG_ERROR,
"StreamPool_Take failed!");
496 return CHANNEL_RC_NO_MEMORY;
499 Stream_Write_UINT8(s, (CLOSE_REQUEST_PDU << 4) | 0x02);
500 Stream_Write_UINT32(s, channel->channel_id);
501 return drdynvc_send(drdynvc, s, &channel->stats);
506 WINPR_ASSERT(channel);
508 IWTSVirtualChannelCallback* cb = channel->channel_callback;
509 const char* name = channel->channel_name;
510 const UINT32
id = channel->channel_id;
513 if (!cb->OnOpen || !cb->OnClose || !cb->OnDataReceived)
514 WLog_VRB(TAG,
"{%s:%" PRIu32
"} OnOpen=%p, OnClose=%p, OnDataReceived=%p", name,
id,
515 WINPR_FUNC_PTR_CAST(cb->OnOpen,
const void*),
516 WINPR_FUNC_PTR_CAST(cb->OnClose,
const void*),
517 WINPR_FUNC_PTR_CAST(cb->OnDataReceived,
const void*));
523 WINPR_ASSERT(channel);
526 channel->stats.packetsIn++;
528 IWTSVirtualChannelCallback* cb = channel->channel_callback;
531 check_open_close_receive(channel);
532 WINPR_ASSERT(cb->OnDataReceived);
533 return cb->OnDataReceived(cb, data);
536static UINT dvcman_channel_close(
DVCMAN_CHANNEL* channel, BOOL perRequest, BOOL fromHashTableFn)
538 UINT error = CHANNEL_RC_OK;
539 DrdynvcClientContext* context =
nullptr;
541 WINPR_ASSERT(channel);
542 switch (channel->state)
544 case DVC_CHANNEL_INIT:
546 case DVC_CHANNEL_RUNNING:
549 drdynvcPlugin* drdynvc = channel->dvcman->drdynvc;
550 WINPR_ASSERT(drdynvc);
551 context = drdynvc->context;
553 WLog_Print(drdynvc->log, WLOG_DEBUG,
"sending close confirm for '%s'",
554 channel->channel_name);
556 error = dvcchannel_send_close(channel);
557 if (error != CHANNEL_RC_OK)
560 WLog_Print(drdynvc->log, WLOG_DEBUG,
561 "error when sending closeRequest for '%s'",
562 channel->channel_name);
564 WLog_Print(drdynvc->log, WLOG_DEBUG,
565 "error when sending close confirm for '%s'",
566 channel->channel_name);
568 WLog_Print(drdynvc->log, WLOG_DEBUG,
"listener %s destroyed channel %" PRIu32
"",
569 channel->channel_name, channel->channel_id);
572 channel->state = DVC_CHANNEL_CLOSED;
575 check_open_close_receive(channel);
577 IWTSVirtualChannelCallback* cb = channel->channel_callback;
578 channel->channel_callback =
nullptr;
580 error = IFCALLRESULT(CHANNEL_RC_OK, cb->OnClose, cb);
583 if (channel->dvcman && channel->dvcman->drdynvc)
587 IFCALLRET(context->OnChannelDisconnected, error, context, channel->channel_name,
588 channel->pInterface);
592 if (!fromHashTableFn)
593 dvcman_channel_unref(channel);
595 case DVC_CHANNEL_CLOSED:
604WINPR_ATTR_MALLOC(dvcman_channel_free, 1)
606 IWTSVirtualChannelManager* pChannelMgr, UINT32 ChannelId,
607 const
char* ChannelName)
609 WINPR_ASSERT(drdynvc);
610 WINPR_ASSERT(pChannelMgr);
616 channel->dvcman = (
DVCMAN*)pChannelMgr;
617 channel->channel_id = ChannelId;
618 channel->refCounter = 1;
619 channel->state = DVC_CHANNEL_INIT;
620 channel->channel_name = _strdup(ChannelName);
621 if (!channel->channel_name)
624 channel->decompressor = zgfx_context_new(FALSE);
625 if (!channel->decompressor)
628 if (!InitializeCriticalSectionEx(&(channel->lock), 0, 0))
633 rdpContext* context = drdynvc->rdpcontext;
636 ChannelInitializedEventArgs e = WINPR_C_ARRAY_INIT;
637 EventArgsInit(&e,
"freerdp");
638 e.name = channel->channel_name;
639 e.pInterface = channel->pInterface;
641 const int rc = PubSub_OnChannelInitialized(context->pubSub, context, &e);
643 WLog_WARN(TAG,
"PubSub_OnChannelInitialized(%s) failed", channel->channel_name);
649 dvcman_channel_free(channel);
653static void dvcman_clear(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr)
657 WINPR_ASSERT(dvcman);
658 WINPR_UNUSED(drdynvc);
660 HashTable_Clear(dvcman->channelsById);
661 ArrayList_Clear(dvcman->plugins);
662 ArrayList_Clear(dvcman->plugin_names);
663 HashTable_Clear(dvcman->listeners);
666static void dvcman_free(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr)
670 WINPR_ASSERT(dvcman);
671 WINPR_UNUSED(drdynvc);
673 HashTable_Free(dvcman->channelsById);
674 ArrayList_Free(dvcman->plugins);
675 ArrayList_Free(dvcman->plugin_names);
676 HashTable_Free(dvcman->listeners);
678 StreamPool_Free(dvcman->pool);
688static UINT dvcman_init(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr)
691 UINT error = CHANNEL_RC_OK;
693 WINPR_ASSERT(dvcman);
694 ArrayList_Lock(dvcman->plugins);
695 for (
size_t i = 0; i < ArrayList_Count(dvcman->plugins); i++)
697 IWTSPlugin* pPlugin = ArrayList_GetItem(dvcman->plugins, i);
699 error = IFCALLRESULT(CHANNEL_RC_OK, pPlugin->Initialize, pPlugin, pChannelMgr);
700 if (error != CHANNEL_RC_OK)
702 WLog_Print(drdynvc->log, WLOG_ERROR,
"Initialize failed with error %" PRIu32
"!",
709 ArrayList_Unlock(dvcman->plugins);
719static UINT dvcman_write_channel(IWTSVirtualChannel* pChannel, ULONG cbSize,
const BYTE* pBuffer,
726 WINPR_UNUSED(pReserved);
727 if (!channel || !channel->dvcman)
728 return CHANNEL_RC_BAD_CHANNEL;
730 EnterCriticalSection(&(channel->lock));
731 status = drdynvc_write_data(channel->dvcman->drdynvc, channel->channel_id, pBuffer, cbSize,
732 &close, &channel->stats);
733 LeaveCriticalSection(&(channel->lock));
736 dvcman_channel_close(channel, FALSE, FALSE);
747static UINT dvcman_close_channel_iface(IWTSVirtualChannel* pChannel)
752 return CHANNEL_RC_BAD_CHANNEL;
754 WLog_DBG(TAG,
"close_channel_iface: id=%" PRIu32
"", channel->channel_id);
755 return dvcman_channel_close(channel, FALSE, FALSE);
758struct stats_collector_argument
766static BOOL stats_collector(WINPR_ATTR_UNUSED
const void* key,
void* value,
void* arg)
768 struct stats_collector_argument* args = arg;
772 WINPR_ASSERT(channel);
773 if (args->used >= args->count)
778 if (channel->channel_name)
780 const size_t len = strnlen(channel->channel_name, ARRAYSIZE(stat->channelName) - 1);
781 strncpy(stat->channelName, channel->channel_name, len);
784 memset(stat->channelName, 0,
sizeof(stat->channelName));
785 stat->channelId = channel->channel_id;
786 stat->bytesIn = channel->stats.bytesIn;
787 stat->bytesOut = channel->stats.bytesOut;
788 stat->fragmentsIn = channel->stats.fragmentsIn;
789 stat->fragmentsOut = channel->stats.fragmentsOut;
790 stat->packetsIn = channel->stats.packetsIn;
791 stat->packetsOut = channel->stats.packetsOut;
795WINPR_ATTR_MALLOC(free, 1)
799 WINPR_ASSERT(context);
800 WINPR_ASSERT(pCount);
802 drdynvcPlugin* drdynvc = (drdynvcPlugin*)context->handle;
803 WINPR_ASSERT(drdynvc);
810 struct stats_collector_argument args = { .stats =
nullptr, .count = 0 };
812 HashTable_Lock(dvcman->channelsById);
813 const size_t count = HashTable_Count(dvcman->channelsById);
820 const BOOL rc = HashTable_Foreach(dvcman->channelsById, stats_collector, &args);
823 HashTable_Unlock(dvcman->channelsById);
829 HashTable_Unlock(dvcman->channelsById);
840static DVCMAN_CHANNEL* dvcman_create_channel(drdynvcPlugin* drdynvc,
841 IWTSVirtualChannelManager* pChannelMgr,
842 UINT32 ChannelId,
const char* ChannelName, UINT* res)
846 DrdynvcClientContext* context =
nullptr;
848 IWTSVirtualChannelCallback* pCallback =
nullptr;
850 WINPR_ASSERT(dvcman);
853 HashTable_Lock(dvcman->listeners);
855 (
DVCMAN_LISTENER*)HashTable_GetItemValue(dvcman->listeners, ChannelName);
858 *res = ERROR_NOT_FOUND;
862 channel = dvcman_get_channel_by_id(pChannelMgr, ChannelId, FALSE);
865 switch (channel->state)
867 case DVC_CHANNEL_RUNNING:
868 WLog_Print(drdynvc->log, WLOG_ERROR,
869 "Protocol error: Duplicated ChannelId %" PRIu32
" (%s)!", ChannelId,
871 *res = CHANNEL_RC_ALREADY_OPEN;
874 case DVC_CHANNEL_CLOSED:
875 case DVC_CHANNEL_INIT:
878 WLog_Print(drdynvc->log, WLOG_ERROR,
"not expecting a createChannel from state %s",
879 channel_state2str(channel->state));
880 *res = CHANNEL_RC_INITIALIZATION_ERROR;
887 channel = dvcman_channel_new(drdynvc, pChannelMgr, ChannelId, ChannelName);
890 WLog_Print(drdynvc->log, WLOG_ERROR,
"dvcman_channel_new failed!");
891 *res = CHANNEL_RC_NO_MEMORY;
894 HashTable_Lock(dvcman->channelsById);
897 if (!HashTable_Insert(dvcman->channelsById, &channel->channel_id, channel))
899 WLog_Print(drdynvc->log, WLOG_ERROR,
"unable to register channel in our channel list");
900 *res = ERROR_INTERNAL_ERROR;
901 dvcman_channel_free(channel);
906 channel->iface.Write = dvcman_write_channel;
907 channel->iface.Close = dvcman_close_channel_iface;
910 *res = listener->listener_callback->OnNewChannelConnection(
911 listener->listener_callback, &channel->iface,
nullptr, &bAccept, &pCallback);
913 if (*res != CHANNEL_RC_OK)
915 WLog_Print(drdynvc->log, WLOG_ERROR,
916 "OnNewChannelConnection failed with error %" PRIu32
"!", *res);
917 *res = ERROR_INTERNAL_ERROR;
918 dvcman_channel_unref(channel);
925 WLog_Print(drdynvc->log, WLOG_ERROR,
"OnNewChannelConnection returned with bAccept FALSE!");
926 *res = ERROR_INTERNAL_ERROR;
927 dvcman_channel_unref(channel);
932 WLog_Print(drdynvc->log, WLOG_DEBUG,
"listener %s created new channel %" PRIu32
"",
933 listener->channel_name, channel->channel_id);
934 channel->state = DVC_CHANNEL_RUNNING;
935 channel->channel_callback = pCallback;
936 channel->pInterface = listener->iface.pInterface;
937 context = dvcman->drdynvc->context;
939 IFCALLRET(context->OnChannelConnected, *res, context, ChannelName, listener->iface.pInterface);
940 if (*res != CHANNEL_RC_OK)
942 WLog_Print(drdynvc->log, WLOG_ERROR,
943 "context.OnChannelConnected failed with error %" PRIu32
"", *res);
947 HashTable_Unlock(dvcman->listeners);
949 dvcman_return_channel(drdynvc->channel_mgr, channel);
960static UINT dvcman_open_channel(drdynvcPlugin* drdynvc,
DVCMAN_CHANNEL* channel)
962 UINT error = CHANNEL_RC_OK;
964 WINPR_ASSERT(drdynvc);
965 WINPR_ASSERT(channel);
966 if (channel->state == DVC_CHANNEL_RUNNING)
968 IWTSVirtualChannelCallback* pCallback = channel->channel_callback;
970 if (pCallback->OnOpen)
972 check_open_close_receive(channel);
973 error = pCallback->OnOpen(pCallback);
976 WLog_Print(drdynvc->log, WLOG_ERROR,
"OnOpen failed with error %" PRIu32
"!",
982 WLog_Print(drdynvc->log, WLOG_DEBUG,
"open_channel: ChannelId %" PRIu32
"",
983 channel->channel_id);
996static UINT dvcman_receive_channel_data_first(
DVCMAN_CHANNEL* channel, UINT32 length)
998 WINPR_ASSERT(channel);
999 WINPR_ASSERT(channel->dvcman);
1000 if (channel->dvc_data)
1001 Stream_Release(channel->dvc_data);
1003 channel->dvc_data = StreamPool_Take(channel->dvcman->pool, 1600);
1005 if (!channel->dvc_data)
1007 drdynvcPlugin* drdynvc = channel->dvcman->drdynvc;
1008 WLog_Print(drdynvc->log, WLOG_ERROR,
"StreamPool_Take failed!");
1009 return CHANNEL_RC_NO_MEMORY;
1012 channel->dvc_data_length = length;
1013 return CHANNEL_RC_OK;
1023 WINPR_ATTR_UNUSED UINT32 ThreadingFlags)
1025 UINT status = CHANNEL_RC_OK;
1026 size_t dataSize = Stream_GetRemainingLength(data);
1028 WINPR_ASSERT(channel);
1029 WINPR_ASSERT(channel->dvcman);
1031 channel->stats.bytesIn += Stream_Length(data);
1032 if (channel->dvc_data)
1034 drdynvcPlugin* drdynvc = channel->dvcman->drdynvc;
1037 if (Stream_GetPosition(channel->dvc_data) + dataSize > channel->dvc_data_length)
1039 WLog_Print(drdynvc->log, WLOG_ERROR,
"data exceeding declared length!");
1040 Stream_Release(channel->dvc_data);
1041 channel->dvc_data =
nullptr;
1042 status = ERROR_INVALID_DATA;
1045 if (!Stream_EnsureRemainingCapacity(channel->dvc_data, dataSize))
1047 Stream_Release(channel->dvc_data);
1048 channel->dvc_data =
nullptr;
1049 status = ERROR_INVALID_DATA;
1053 Stream_Copy(data, channel->dvc_data, dataSize);
1054 channel->stats.fragmentsIn++;
1056 if (Stream_GetPosition(channel->dvc_data) >= channel->dvc_data_length)
1058 Stream_SealLength(channel->dvc_data);
1059 Stream_ResetPosition(channel->dvc_data);
1061 status = dvcman_call_on_receive(channel, channel->dvc_data);
1062 Stream_Release(channel->dvc_data);
1063 channel->dvc_data =
nullptr;
1067 status = dvcman_call_on_receive(channel, data);
1074static UINT8 drdynvc_write_variable_uint(
wStream* s, UINT32 val)
1081 Stream_Write_UINT8(s, (UINT8)val);
1083 else if (val <= 0xFFFF)
1086 Stream_Write_UINT16(s, (UINT16)val);
1091 Stream_Write_UINT32(s, val);
1108 status = CHANNEL_RC_BAD_CHANNEL_HANDLE;
1111 const size_t len = Stream_GetPosition(s);
1114 stats->bytesOut += len;
1116 WINPR_ASSERT(drdynvc->channelEntryPoints.pVirtualChannelWriteEx);
1117 status = drdynvc->channelEntryPoints.pVirtualChannelWriteEx(
1118 drdynvc->InitHandle, drdynvc->OpenHandle, Stream_Buffer(s), (UINT32)len, s);
1124 return CHANNEL_RC_OK;
1126 case CHANNEL_RC_NOT_CONNECTED:
1128 return CHANNEL_RC_OK;
1130 case CHANNEL_RC_BAD_CHANNEL_HANDLE:
1132 WLog_ERR(TAG,
"VirtualChannelWriteEx failed with CHANNEL_RC_BAD_CHANNEL_HANDLE");
1137 WLog_Print(drdynvc->log, WLOG_ERROR,
1138 "VirtualChannelWriteEx failed with %s [%08" PRIX32
"]",
1139 WTSErrorToString(status), status);
1150static UINT drdynvc_write_data(drdynvcPlugin* drdynvc, UINT32 ChannelId,
const BYTE* data,
1156 UINT status = CHANNEL_RC_BAD_INIT_HANDLE;
1157 DVCMAN* dvcman =
nullptr;
1160 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1162 dvcman = (
DVCMAN*)drdynvc->channel_mgr;
1163 WINPR_ASSERT(dvcman);
1165 WLog_Print(drdynvc->log, WLOG_TRACE,
"write_data: ChannelId=%" PRIu32
" size=%" PRIu32
"",
1166 ChannelId, dataSize);
1167 wStream* data_out = StreamPool_Take(dvcman->pool, CHANNEL_CHUNK_LENGTH);
1171 WLog_Print(drdynvc->log, WLOG_ERROR,
"StreamPool_Take failed!");
1172 return CHANNEL_RC_NO_MEMORY;
1175 if (!Stream_SetPosition(data_out, 1))
1177 Stream_Release(data_out);
1178 return ERROR_INVALID_DATA;
1180 cbChId = drdynvc_write_variable_uint(data_out, ChannelId);
1181 pos = Stream_GetPosition(data_out);
1187 Stream_Release(data_out);
1189 else if (dataSize <= CHANNEL_CHUNK_LENGTH - pos)
1191 Stream_ResetPosition(data_out);
1192 Stream_Write_UINT8(data_out, (DATA_PDU << 4) | cbChId);
1193 if (!Stream_SetPosition(data_out, pos))
1195 Stream_Release(data_out);
1196 return ERROR_INVALID_DATA;
1198 Stream_Write(data_out, data, dataSize);
1199 stats->packetsOut++;
1200 status = drdynvc_send(drdynvc, data_out, stats);
1205 cbLen = drdynvc_write_variable_uint(data_out, dataSize);
1206 pos = Stream_GetPosition(data_out);
1207 Stream_ResetPosition(data_out);
1209 const INT32 pdu = (DATA_FIRST_PDU << 4) | cbChId | (cbLen << 2);
1210 Stream_Write_UINT8(data_out, WINPR_ASSERTING_INT_CAST(UINT8, pdu));
1211 if (!Stream_SetPosition(data_out, pos))
1213 Stream_Release(data_out);
1214 return ERROR_INVALID_DATA;
1218 WINPR_ASSERT(pos <= CHANNEL_CHUNK_LENGTH);
1219 const uint32_t chunkLength =
1220 CHANNEL_CHUNK_LENGTH - WINPR_ASSERTING_INT_CAST(uint32_t, pos);
1221 Stream_Write(data_out, data, chunkLength);
1223 data += chunkLength;
1224 dataSize -= chunkLength;
1227 stats->fragmentsOut++;
1229 status = drdynvc_send(drdynvc, data_out, stats);
1231 while (status == CHANNEL_RC_OK && dataSize > 0)
1233 data_out = StreamPool_Take(dvcman->pool, CHANNEL_CHUNK_LENGTH);
1237 WLog_Print(drdynvc->log, WLOG_ERROR,
"StreamPool_Take failed!");
1238 return CHANNEL_RC_NO_MEMORY;
1241 if (!Stream_SetPosition(data_out, 1))
1243 Stream_Release(data_out);
1244 return ERROR_INVALID_DATA;
1247 cbChId = drdynvc_write_variable_uint(data_out, ChannelId);
1248 pos = Stream_GetPosition(data_out);
1249 Stream_ResetPosition(data_out);
1250 Stream_Write_UINT8(data_out, (DATA_PDU << 4) | cbChId);
1251 if (!Stream_SetPosition(data_out, pos))
1253 Stream_Release(data_out);
1254 return ERROR_INVALID_DATA;
1257 uint32_t chunkLength = dataSize;
1259 WINPR_ASSERT(pos <= CHANNEL_CHUNK_LENGTH);
1260 const uint32_t clen = CHANNEL_CHUNK_LENGTH - WINPR_ASSERTING_INT_CAST(uint32_t, pos);
1261 if (chunkLength > clen)
1263 stats->fragmentsOut++;
1267 stats->packetsOut++;
1269 Stream_Write(data_out, data, chunkLength);
1270 data += chunkLength;
1271 dataSize -= chunkLength;
1273 status = drdynvc_send(drdynvc, data_out, stats);
1277 if (status != CHANNEL_RC_OK)
1279 WLog_Print(drdynvc->log, WLOG_ERROR,
"VirtualChannelWriteEx failed with %s [%08" PRIX32
"]",
1280 WTSErrorToString(status), status);
1284 return CHANNEL_RC_OK;
1293static UINT drdynvc_send_capability_response(drdynvcPlugin* drdynvc)
1297 DVCMAN* dvcman =
nullptr;
1300 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1302 dvcman = (
DVCMAN*)drdynvc->channel_mgr;
1303 WINPR_ASSERT(dvcman);
1305 WLog_Print(drdynvc->log, WLOG_TRACE,
"capability_response");
1306 s = StreamPool_Take(dvcman->pool, 4);
1310 WLog_Print(drdynvc->log, WLOG_ERROR,
"Stream_New failed!");
1311 return CHANNEL_RC_NO_MEMORY;
1314 Stream_Write_UINT16(s, 0x0050);
1315 Stream_Write_UINT16(s, drdynvc->version);
1316 status = drdynvc_send(drdynvc, s,
nullptr);
1318 if (status != CHANNEL_RC_OK)
1320 WLog_Print(drdynvc->log, WLOG_ERROR,
"VirtualChannelWriteEx failed with %s [%08" PRIX32
"]",
1321 WTSErrorToString(status), status);
1333static UINT drdynvc_process_capability_request(drdynvcPlugin* drdynvc,
int Sp,
int cbChId,
1339 return CHANNEL_RC_BAD_INIT_HANDLE;
1341 if (!Stream_CheckAndLogRequiredLength(TAG, s, 3))
1342 return ERROR_INVALID_DATA;
1344 WLog_Print(drdynvc->log, WLOG_TRACE,
"capability_request Sp=%d cbChId=%d", Sp, cbChId);
1346 Stream_Read_UINT16(s, drdynvc->version);
1351 if ((drdynvc->version == 2) || (drdynvc->version == 3))
1353 if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
1354 return ERROR_INVALID_DATA;
1356 Stream_Read_UINT16(s, drdynvc->PriorityCharge0);
1357 Stream_Read_UINT16(s, drdynvc->PriorityCharge1);
1358 Stream_Read_UINT16(s, drdynvc->PriorityCharge2);
1359 Stream_Read_UINT16(s, drdynvc->PriorityCharge3);
1362 status = drdynvc_send_capability_response(drdynvc);
1363 drdynvc->state = DRDYNVC_STATE_READY;
1368static UINT32 drdynvc_cblen_to_bytes(
int cbLen)
1384static UINT32 drdynvc_read_variable_uint(
wStream* s,
int cbLen)
1391 Stream_Read_UINT8(s, val);
1395 Stream_Read_UINT16(s, val);
1399 Stream_Read_UINT32(s, val);
1412static UINT drdynvc_process_create_request(drdynvcPlugin* drdynvc, UINT8 Sp, UINT8 cbChId,
1417 UINT channel_status = 0;
1418 DVCMAN* dvcman =
nullptr;
1420 INT32 retStatus = 0;
1424 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1426 dvcman = (
DVCMAN*)drdynvc->channel_mgr;
1427 WINPR_ASSERT(dvcman);
1429 if (drdynvc->state == DRDYNVC_STATE_CAPABILITIES)
1436 drdynvc->version = 3;
1438 if ((status = drdynvc_send_capability_response(drdynvc)))
1440 WLog_Print(drdynvc->log, WLOG_ERROR,
"drdynvc_send_capability_response failed!");
1444 drdynvc->state = DRDYNVC_STATE_READY;
1447 if (!Stream_CheckAndLogRequiredLength(TAG, s, drdynvc_cblen_to_bytes(cbChId)))
1448 return ERROR_INVALID_DATA;
1450 const UINT32 ChannelId = drdynvc_read_variable_uint(s, cbChId);
1451 const size_t pos = Stream_GetPosition(s);
1452 const char* name = Stream_ConstPointer(s);
1453 const size_t length = Stream_GetRemainingLength(s);
1455 if (strnlen(name, length) >= length)
1456 return ERROR_INVALID_DATA;
1458 WLog_Print(drdynvc->log, WLOG_DEBUG,
1459 "process_create_request: ChannelId=%" PRIu32
" ChannelName=%s", ChannelId, name);
1461 data_out = StreamPool_Take(dvcman->pool, pos + 4);
1464 WLog_Print(drdynvc->log, WLOG_ERROR,
"StreamPool_Take failed!");
1465 return CHANNEL_RC_NO_MEMORY;
1468 Stream_Write_UINT8(data_out, (CREATE_REQUEST_PDU << 4) | cbChId);
1469 if (!Stream_SetPosition(s, 1))
1470 return ERROR_INVALID_DATA;
1471 Stream_Copy(s, data_out, pos - 1);
1474 dvcman_create_channel(drdynvc, drdynvc->channel_mgr, ChannelId, name, &channel_status);
1475 switch (channel_status)
1478 WLog_Print(drdynvc->log, WLOG_DEBUG,
"channel created");
1481 case CHANNEL_RC_NO_MEMORY:
1482 WLog_Print(drdynvc->log, WLOG_DEBUG,
"not enough memory for channel creation");
1483 retStatus = STATUS_NO_MEMORY;
1485 case ERROR_NOT_FOUND:
1486 WLog_Print(drdynvc->log, WLOG_DEBUG,
"no listener for '%s'", name);
1487 retStatus = STATUS_NOT_FOUND;
1490 WLog_Print(drdynvc->log, WLOG_DEBUG,
"channel creation error");
1491 retStatus = STATUS_UNSUCCESSFUL;
1494 Stream_Write_INT32(data_out, retStatus);
1496 status = drdynvc_send(drdynvc, data_out,
nullptr);
1497 if (status != CHANNEL_RC_OK)
1499 WLog_Print(drdynvc->log, WLOG_ERROR,
"VirtualChannelWriteEx failed with %s [%08" PRIX32
"]",
1500 WTSErrorToString(status), status);
1501 if (channel_status == CHANNEL_RC_OK)
1502 dvcman_channel_unref(channel);
1506 if (channel_status == CHANNEL_RC_OK)
1508 if ((status = dvcman_open_channel(drdynvc, channel)))
1510 WLog_Print(drdynvc->log, WLOG_ERROR,
1511 "dvcman_open_channel failed with error %" PRIu32
"!", status);
1525static UINT drdynvc_process_data_first(drdynvcPlugin* drdynvc,
int Sp,
int cbChId,
wStream* s,
1526 BOOL compressed, UINT32 ThreadingFlags)
1528 WINPR_ASSERT(drdynvc);
1529 if (!Stream_CheckAndLogRequiredLength(
1530 TAG, s, drdynvc_cblen_to_bytes(cbChId) + drdynvc_cblen_to_bytes(Sp)))
1531 return ERROR_INVALID_DATA;
1533 UINT32 ChannelId = drdynvc_read_variable_uint(s, cbChId);
1534 UINT32 Length = drdynvc_read_variable_uint(s, Sp);
1535 WLog_Print(drdynvc->log, WLOG_TRACE,
1536 "process_data_first: Sp=%d cbChId=%d, ChannelId=%" PRIu32
" Length=%" PRIu32
"", Sp,
1537 cbChId, ChannelId, Length);
1539 DVCMAN_CHANNEL* channel = dvcman_get_channel_by_id(drdynvc->channel_mgr, ChannelId, TRUE);
1547 WLog_Print(drdynvc->log, WLOG_ERROR,
"ChannelId %" PRIu32
" not found!", ChannelId);
1548 return CHANNEL_RC_OK;
1551 UINT status = CHANNEL_RC_OK;
1552 BOOL shouldFree = FALSE;
1553 if (channel->state != DVC_CHANNEL_RUNNING)
1558 BYTE* data =
nullptr;
1559 UINT32 dataSize = 0;
1560 if (zgfx_decompress(channel->decompressor, Stream_Pointer(s),
1561 WINPR_ASSERTING_INT_CAST(UINT32, Stream_GetRemainingLength(s)), &data,
1564 status = ERROR_INVALID_DATA;
1565 WLog_Print(drdynvc->log, WLOG_ERROR,
"error de-compressing first packet");
1571 status = ERROR_INVALID_DATA;
1572 WLog_Print(drdynvc->log, WLOG_ERROR,
"error de-compressing first packet");
1577 s = Stream_New(data, dataSize);
1580 status = CHANNEL_RC_NO_MEMORY;
1581 WLog_Print(drdynvc->log, WLOG_ERROR,
"error allocating new Stream(len=%" PRIu32
")",
1589 status = dvcman_receive_channel_data_first(channel, Length);
1591 if (status == CHANNEL_RC_OK)
1592 status = dvcman_receive_channel_data(channel, s, ThreadingFlags);
1594 if (status != CHANNEL_RC_OK)
1595 status = dvcman_channel_close(channel, FALSE, FALSE);
1599 Stream_Free(s, TRUE);
1600 dvcman_channel_unref(channel);
1601 dvcman_return_channel(drdynvc->channel_mgr, channel);
1611static UINT drdynvc_process_data(drdynvcPlugin* drdynvc,
int Sp,
int cbChId,
wStream* s,
1612 BOOL compressed, UINT32 ThreadingFlags)
1614 WINPR_ASSERT(drdynvc);
1615 if (!Stream_CheckAndLogRequiredLength(TAG, s, drdynvc_cblen_to_bytes(cbChId)))
1616 return ERROR_INVALID_DATA;
1618 UINT32 ChannelId = drdynvc_read_variable_uint(s, cbChId);
1619 WLog_Print(drdynvc->log, WLOG_TRACE,
"process_data: Sp=%d cbChId=%d, ChannelId=%" PRIu32
"", Sp,
1622 DVCMAN_CHANNEL* channel = dvcman_get_channel_by_id(drdynvc->channel_mgr, ChannelId, TRUE);
1630 WLog_Print(drdynvc->log, WLOG_ERROR,
"ChannelId %" PRIu32
" not found!", ChannelId);
1631 return CHANNEL_RC_OK;
1634 BOOL shouldFree = FALSE;
1635 UINT status = CHANNEL_RC_OK;
1636 if (channel->state != DVC_CHANNEL_RUNNING)
1641 BYTE* data =
nullptr;
1642 UINT32 dataSize = 0;
1644 if (zgfx_decompress(channel->decompressor, Stream_Pointer(s),
1645 WINPR_ASSERTING_INT_CAST(UINT32, Stream_GetRemainingLength(s)), &data,
1648 status = ERROR_INVALID_DATA;
1649 WLog_Print(drdynvc->log, WLOG_ERROR,
"error de-compressing data packet");
1656 status = ERROR_INVALID_DATA;
1657 WLog_Print(drdynvc->log, WLOG_ERROR,
"error de-compressing data packet");
1662 s = Stream_New(data, dataSize);
1665 status = CHANNEL_RC_NO_MEMORY;
1666 WLog_Print(drdynvc->log, WLOG_ERROR,
"error allocating new Stream(len=%" PRIu32
")",
1674 status = dvcman_receive_channel_data(channel, s, ThreadingFlags);
1675 if (status != CHANNEL_RC_OK)
1676 status = dvcman_channel_close(channel, FALSE, FALSE);
1680 Stream_Free(s, TRUE);
1681 dvcman_channel_unref(channel);
1682 dvcman_return_channel(drdynvc->channel_mgr, channel);
1692static UINT drdynvc_process_close_request(drdynvcPlugin* drdynvc,
int Sp,
int cbChId,
wStream* s)
1694 UINT32 ChannelId = 0;
1697 WINPR_ASSERT(drdynvc);
1698 if (!Stream_CheckAndLogRequiredLength(TAG, s, drdynvc_cblen_to_bytes(cbChId)))
1699 return ERROR_INVALID_DATA;
1701 ChannelId = drdynvc_read_variable_uint(s, cbChId);
1702 WLog_Print(drdynvc->log, WLOG_DEBUG,
1703 "process_close_request: Sp=%d cbChId=%d, ChannelId=%" PRIu32
"", Sp, cbChId,
1706 channel = dvcman_get_channel_by_id(drdynvc->channel_mgr, ChannelId, TRUE);
1709 WLog_Print(drdynvc->log, WLOG_ERROR,
"dvcman_close_request channel %" PRIu32
" not present",
1711 return CHANNEL_RC_OK;
1714 dvcman_channel_close(channel, TRUE, FALSE);
1715 dvcman_channel_unref(channel);
1716 dvcman_return_channel(drdynvc->channel_mgr, channel);
1717 return CHANNEL_RC_OK;
1726static UINT drdynvc_order_recv(drdynvcPlugin* drdynvc,
wStream* s, UINT32 ThreadingFlags)
1728 WINPR_ASSERT(drdynvc);
1729 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
1730 return ERROR_INVALID_DATA;
1732 UINT8 value = Stream_Get_UINT8(s);
1733 const UINT8 Cmd = (value & 0xf0) >> 4;
1734 const UINT8 Sp = (value & 0x0c) >> 2;
1735 const UINT8 cbChId = (value & 0x03) >> 0;
1736 WLog_Print(drdynvc->log, WLOG_TRACE,
"order_recv: Cmd=%s, Sp=%" PRIu8
" cbChId=%" PRIu8,
1737 drdynvc_get_packet_type(Cmd), Sp, cbChId);
1741 case CAPABILITY_REQUEST_PDU:
1742 return drdynvc_process_capability_request(drdynvc, Sp, cbChId, s);
1744 case CREATE_REQUEST_PDU:
1745 return drdynvc_process_create_request(drdynvc, Sp, cbChId, s);
1747 case DATA_FIRST_PDU:
1748 case DATA_FIRST_COMPRESSED_PDU:
1749 return drdynvc_process_data_first(drdynvc, Sp, cbChId, s,
1750 (Cmd == DATA_FIRST_COMPRESSED_PDU), ThreadingFlags);
1753 case DATA_COMPRESSED_PDU:
1754 return drdynvc_process_data(drdynvc, Sp, cbChId, s, (Cmd == DATA_COMPRESSED_PDU),
1757 case CLOSE_REQUEST_PDU:
1758 return drdynvc_process_close_request(drdynvc, Sp, cbChId, s);
1760 case SOFT_SYNC_RESPONSE_PDU:
1761 WLog_Print(drdynvc->log, WLOG_ERROR,
1762 "not expecting a SOFT_SYNC_RESPONSE_PDU as a client");
1763 return ERROR_INTERNAL_ERROR;
1766 WLog_Print(drdynvc->log, WLOG_ERROR,
"unknown drdynvc cmd 0x%x", Cmd);
1767 return ERROR_INTERNAL_ERROR;
1777static UINT drdynvc_virtual_channel_event_data_received(drdynvcPlugin* drdynvc,
void* pData,
1778 UINT32 dataLength, UINT32 totalLength,
1783 WINPR_ASSERT(drdynvc);
1784 if ((dataFlags & CHANNEL_FLAG_SUSPEND) || (dataFlags & CHANNEL_FLAG_RESUME))
1786 return CHANNEL_RC_OK;
1789 if (dataFlags & CHANNEL_FLAG_FIRST)
1791 if (drdynvc->firstFlagReceived)
1792 return ERROR_INVALID_DATA;
1793 drdynvc->firstFlagReceived = TRUE;
1796 if (drdynvc->data_in)
1797 Stream_Release(drdynvc->data_in);
1799 drdynvc->data_in = StreamPool_Take(mgr->pool, dataLength);
1800 drdynvc->totalLength = totalLength;
1803 if (!(data_in = drdynvc->data_in))
1805 WLog_Print(drdynvc->log, WLOG_ERROR,
"StreamPool_Take failed!");
1806 return CHANNEL_RC_NO_MEMORY;
1809 if (!Stream_EnsureRemainingCapacity(data_in, dataLength))
1811 WLog_Print(drdynvc->log, WLOG_ERROR,
"Stream_EnsureRemainingCapacity failed!");
1812 Stream_Release(drdynvc->data_in);
1813 drdynvc->data_in =
nullptr;
1814 return ERROR_INTERNAL_ERROR;
1817 Stream_Write(data_in, pData, dataLength);
1819 if ((Stream_GetPosition(data_in) > totalLength) || (drdynvc->totalLength != totalLength))
1821 Stream_Release(drdynvc->data_in);
1822 drdynvc->data_in =
nullptr;
1823 return ERROR_INVALID_DATA;
1826 if (dataFlags & CHANNEL_FLAG_LAST)
1828 if (!drdynvc->firstFlagReceived)
1829 return ERROR_INVALID_DATA;
1830 drdynvc->firstFlagReceived = FALSE;
1832 const size_t pos = Stream_GetPosition(data_in);
1833 if (drdynvc->totalLength != pos)
1835 WLog_Print(drdynvc->log, WLOG_ERROR,
"drdynvc_plugin_process_received: read error");
1836 return ERROR_INVALID_DATA;
1839 drdynvc->totalLength = 0;
1840 drdynvc->data_in =
nullptr;
1841 Stream_SealLength(data_in);
1842 Stream_ResetPosition(data_in);
1846 if (!MessageQueue_Post(drdynvc->queue,
nullptr, 0, (
void*)data_in,
nullptr))
1848 WLog_Print(drdynvc->log, WLOG_ERROR,
"MessageQueue_Post failed!");
1849 return ERROR_INTERNAL_ERROR;
1854 UINT error = drdynvc_order_recv(drdynvc, data_in, TRUE);
1855 Stream_Release(data_in);
1859 WLog_Print(drdynvc->log, WLOG_WARN,
1860 "drdynvc_order_recv failed with error %" PRIu32
"!", error);
1866 return CHANNEL_RC_OK;
1869static void VCAPITYPE drdynvc_virtual_channel_open_event_ex(LPVOID lpUserParam, DWORD openHandle,
1870 UINT event, LPVOID pData,
1871 UINT32 dataLength, UINT32 totalLength,
1874 UINT error = CHANNEL_RC_OK;
1875 drdynvcPlugin* drdynvc = (drdynvcPlugin*)lpUserParam;
1877 WINPR_ASSERT(drdynvc);
1880 case CHANNEL_EVENT_DATA_RECEIVED:
1881 if (!drdynvc || (drdynvc->OpenHandle != openHandle))
1883 WLog_ERR(TAG,
"drdynvc_virtual_channel_open_event: error no match");
1886 if ((error = drdynvc_virtual_channel_event_data_received(drdynvc, pData, dataLength,
1887 totalLength, dataFlags)))
1888 WLog_Print(drdynvc->log, WLOG_ERROR,
1889 "drdynvc_virtual_channel_event_data_received failed with error %" PRIu32
1895 case CHANNEL_EVENT_WRITE_CANCELLED:
1896 case CHANNEL_EVENT_WRITE_COMPLETE:
1903 case CHANNEL_EVENT_USER:
1909 if (error && drdynvc && drdynvc->rdpcontext)
1910 setChannelError(drdynvc->rdpcontext, error,
1911 "drdynvc_virtual_channel_open_event reported an error");
1915static DWORD WINAPI drdynvc_virtual_channel_client_thread(LPVOID arg)
1919 wMessage message = WINPR_C_ARRAY_INIT;
1920 UINT error = CHANNEL_RC_OK;
1921 drdynvcPlugin* drdynvc = (drdynvcPlugin*)arg;
1925 ExitThread((DWORD)CHANNEL_RC_BAD_CHANNEL_HANDLE);
1926 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1931 if (!MessageQueue_Wait(drdynvc->queue))
1933 WLog_Print(drdynvc->log, WLOG_ERROR,
"MessageQueue_Wait failed!");
1934 error = ERROR_INTERNAL_ERROR;
1938 if (!MessageQueue_Peek(drdynvc->queue, &message, TRUE))
1940 WLog_Print(drdynvc->log, WLOG_ERROR,
"MessageQueue_Peek failed!");
1941 error = ERROR_INTERNAL_ERROR;
1945 if (message.id == WMQ_QUIT)
1948 if (message.id == 0)
1950 UINT32 ThreadingFlags = TRUE;
1951 data = (
wStream*)message.wParam;
1953 if ((error = drdynvc_order_recv(drdynvc, data, ThreadingFlags)))
1955 WLog_Print(drdynvc->log, WLOG_WARN,
1956 "drdynvc_order_recv failed with error %" PRIu32
"!", error);
1959 Stream_Release(data);
1969 HashTable_Clear(drdynvcMgr->channelsById);
1972 if (error && drdynvc->rdpcontext)
1973 setChannelError(drdynvc->rdpcontext, error,
1974 "drdynvc_virtual_channel_client_thread reported an error");
1976 ExitThread((DWORD)error);
1980static void drdynvc_queue_object_free(
void* obj)
1983 wMessage* msg = (wMessage*)obj;
1985 if (!msg || (msg->id != 0))
1995static UINT drdynvc_virtual_channel_event_initialized(drdynvcPlugin* drdynvc, LPVOID pData,
1999 WINPR_UNUSED(pData);
2000 WINPR_UNUSED(dataLength);
2005 drdynvc->queue = MessageQueue_New(
nullptr);
2007 if (!drdynvc->queue)
2009 WLog_Print(drdynvc->log, WLOG_ERROR,
"MessageQueue_New failed!");
2013 obj = MessageQueue_Object(drdynvc->queue);
2015 drdynvc->channel_mgr = dvcman_new(drdynvc);
2017 if (!drdynvc->channel_mgr)
2019 WLog_Print(drdynvc->log, WLOG_ERROR,
"dvcman_new failed!");
2023 return CHANNEL_RC_OK;
2025 return ERROR_INTERNAL_ERROR;
2034static UINT drdynvc_virtual_channel_event_connected(drdynvcPlugin* drdynvc, LPVOID pData,
2039 rdpSettings* settings =
nullptr;
2041 WINPR_ASSERT(drdynvc);
2042 WINPR_UNUSED(pData);
2043 WINPR_UNUSED(dataLength);
2046 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
2048 WINPR_ASSERT(drdynvc->channelEntryPoints.pVirtualChannelOpenEx);
2049 status = drdynvc->channelEntryPoints.pVirtualChannelOpenEx(
2050 drdynvc->InitHandle, &drdynvc->OpenHandle, drdynvc->channelDef.name,
2051 drdynvc_virtual_channel_open_event_ex);
2053 if (status != CHANNEL_RC_OK)
2055 WLog_Print(drdynvc->log, WLOG_ERROR,
"pVirtualChannelOpen failed with %s [%08" PRIX32
"]",
2056 WTSErrorToString(status), status);
2060 WINPR_ASSERT(drdynvc->rdpcontext);
2061 settings = drdynvc->rdpcontext->settings;
2062 WINPR_ASSERT(settings);
2064 for (UINT32 index = 0;
2068 freerdp_settings_get_pointer_array(settings, FreeRDP_DynamicChannelArray, index);
2069 error = dvcman_load_addin(drdynvc, drdynvc->channel_mgr, args, drdynvc->rdpcontext);
2071 if (CHANNEL_RC_OK != error)
2075 if ((error = dvcman_init(drdynvc, drdynvc->channel_mgr)))
2077 WLog_Print(drdynvc->log, WLOG_ERROR,
"dvcman_init failed with error %" PRIu32
"!", error);
2081 drdynvc->state = DRDYNVC_STATE_CAPABILITIES;
2085 if (!(drdynvc->thread = CreateThread(
nullptr, 0, drdynvc_virtual_channel_client_thread,
2086 (
void*)drdynvc, 0,
nullptr)))
2088 error = ERROR_INTERNAL_ERROR;
2089 WLog_Print(drdynvc->log, WLOG_ERROR,
"CreateThread failed!");
2093 if (!SetThreadPriority(drdynvc->thread, THREAD_PRIORITY_HIGHEST))
2094 WLog_Print(drdynvc->log, WLOG_WARN,
"SetThreadPriority failed, ignoring.");
2107static UINT drdynvc_virtual_channel_event_disconnected(drdynvcPlugin* drdynvc)
2112 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
2114 if (drdynvc->OpenHandle == 0)
2115 return CHANNEL_RC_OK;
2119 if (!MessageQueue_PostQuit(drdynvc->queue, 0))
2121 status = GetLastError();
2122 WLog_Print(drdynvc->log, WLOG_ERROR,
2123 "MessageQueue_PostQuit failed with error %" PRIu32
"", status);
2128 if (drdynvc->thread)
2130 if (WaitForSingleObject(drdynvc->thread, INFINITE) != WAIT_OBJECT_0)
2132 status = GetLastError();
2133 WLog_Print(drdynvc->log, WLOG_ERROR,
2134 "WaitForSingleObject failed with error %" PRIu32
"", status);
2138 (void)CloseHandle(drdynvc->thread);
2139 drdynvc->thread =
nullptr;
2149 HashTable_Clear(drdynvcMgr->channelsById);
2153 WINPR_ASSERT(drdynvc->channelEntryPoints.pVirtualChannelCloseEx);
2154 status = drdynvc->channelEntryPoints.pVirtualChannelCloseEx(drdynvc->InitHandle,
2155 drdynvc->OpenHandle);
2157 if (status != CHANNEL_RC_OK)
2159 WLog_Print(drdynvc->log, WLOG_ERROR,
"pVirtualChannelClose failed with %s [%08" PRIX32
"]",
2160 WTSErrorToString(status), status);
2163 dvcman_clear(drdynvc, drdynvc->channel_mgr);
2165 MessageQueue_Clear(drdynvc->queue);
2166 drdynvc->OpenHandle = 0;
2168 if (drdynvc->data_in)
2170 Stream_Release(drdynvc->data_in);
2171 drdynvc->data_in =
nullptr;
2183static UINT drdynvc_virtual_channel_event_terminated(drdynvcPlugin* drdynvc)
2186 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
2188 MessageQueue_Free(drdynvc->queue);
2189 drdynvc->queue =
nullptr;
2191 if (drdynvc->channel_mgr)
2193 dvcman_free(drdynvc, drdynvc->channel_mgr);
2194 drdynvc->channel_mgr =
nullptr;
2196 drdynvc->InitHandle =
nullptr;
2197 free(drdynvc->context);
2199 return CHANNEL_RC_OK;
2203static UINT drdynvc_virtual_channel_event_attached(drdynvcPlugin* drdynvc)
2205 UINT error = CHANNEL_RC_OK;
2206 DVCMAN* dvcman =
nullptr;
2209 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
2211 dvcman = (
DVCMAN*)drdynvc->channel_mgr;
2214 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
2216 ArrayList_Lock(dvcman->plugins);
2217 for (
size_t i = 0; i < ArrayList_Count(dvcman->plugins); i++)
2219 IWTSPlugin* pPlugin = ArrayList_GetItem(dvcman->plugins, i);
2221 error = IFCALLRESULT(CHANNEL_RC_OK, pPlugin->Attached, pPlugin);
2222 if (error != CHANNEL_RC_OK)
2224 WLog_Print(drdynvc->log, WLOG_ERROR,
"Attach failed with error %" PRIu32
"!", error);
2230 ArrayList_Unlock(dvcman->plugins);
2235static UINT drdynvc_virtual_channel_event_detached(drdynvcPlugin* drdynvc)
2237 UINT error = CHANNEL_RC_OK;
2238 DVCMAN* dvcman =
nullptr;
2241 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
2243 dvcman = (
DVCMAN*)drdynvc->channel_mgr;
2246 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
2248 ArrayList_Lock(dvcman->plugins);
2249 for (
size_t i = 0; i < ArrayList_Count(dvcman->plugins); i++)
2251 IWTSPlugin* pPlugin = ArrayList_GetItem(dvcman->plugins, i);
2253 error = IFCALLRESULT(CHANNEL_RC_OK, pPlugin->Detached, pPlugin);
2254 if (error != CHANNEL_RC_OK)
2256 WLog_Print(drdynvc->log, WLOG_ERROR,
"Detach failed with error %" PRIu32
"!", error);
2262 ArrayList_Unlock(dvcman->plugins);
2267static VOID VCAPITYPE drdynvc_virtual_channel_init_event_ex(LPVOID lpUserParam, LPVOID pInitHandle,
2268 UINT event, LPVOID pData,
2271 UINT error = CHANNEL_RC_OK;
2272 drdynvcPlugin* drdynvc = (drdynvcPlugin*)lpUserParam;
2274 if (!drdynvc || (drdynvc->InitHandle != pInitHandle))
2276 WLog_ERR(TAG,
"drdynvc_virtual_channel_init_event: error no match");
2282 case CHANNEL_EVENT_INITIALIZED:
2283 error = drdynvc_virtual_channel_event_initialized(drdynvc, pData, dataLength);
2285 case CHANNEL_EVENT_CONNECTED:
2286 if ((error = drdynvc_virtual_channel_event_connected(drdynvc, pData, dataLength)))
2287 WLog_Print(drdynvc->log, WLOG_ERROR,
2288 "drdynvc_virtual_channel_event_connected failed with error %" PRIu32
"",
2293 case CHANNEL_EVENT_DISCONNECTED:
2294 if ((error = drdynvc_virtual_channel_event_disconnected(drdynvc)))
2295 WLog_Print(drdynvc->log, WLOG_ERROR,
2296 "drdynvc_virtual_channel_event_disconnected failed with error %" PRIu32
2302 case CHANNEL_EVENT_TERMINATED:
2303 if ((error = drdynvc_virtual_channel_event_terminated(drdynvc)))
2304 WLog_Print(drdynvc->log, WLOG_ERROR,
2305 "drdynvc_virtual_channel_event_terminated failed with error %" PRIu32
"",
2310 case CHANNEL_EVENT_ATTACHED:
2311 if ((error = drdynvc_virtual_channel_event_attached(drdynvc)))
2312 WLog_Print(drdynvc->log, WLOG_ERROR,
2313 "drdynvc_virtual_channel_event_attached failed with error %" PRIu32
"",
2318 case CHANNEL_EVENT_DETACHED:
2319 if ((error = drdynvc_virtual_channel_event_detached(drdynvc)))
2320 WLog_Print(drdynvc->log, WLOG_ERROR,
2321 "drdynvc_virtual_channel_event_detached failed with error %" PRIu32
"",
2330 if (error && drdynvc->rdpcontext)
2331 setChannelError(drdynvc->rdpcontext, error,
2332 "drdynvc_virtual_channel_init_event_ex reported an error");
2340static int drdynvc_get_version(DrdynvcClientContext* context)
2342 WINPR_ASSERT(context);
2343 drdynvcPlugin* drdynvc = (drdynvcPlugin*)context->handle;
2344 WINPR_ASSERT(drdynvc);
2345 return drdynvc->version;
2349#define VirtualChannelEntryEx drdynvc_VirtualChannelEntryEx
2351FREERDP_ENTRY_POINT(BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS_EX pEntryPoints,
2355 drdynvcPlugin* drdynvc =
nullptr;
2356 DrdynvcClientContext* context =
nullptr;
2358 drdynvc = (drdynvcPlugin*)calloc(1,
sizeof(drdynvcPlugin));
2360 WINPR_ASSERT(pEntryPoints);
2363 WLog_ERR(TAG,
"calloc failed!");
2367 drdynvc->channelDef.options =
2368 CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP | CHANNEL_OPTION_COMPRESS_RDP;
2369 (void)sprintf_s(drdynvc->channelDef.name, ARRAYSIZE(drdynvc->channelDef.name),
2370 DRDYNVC_SVC_CHANNEL_NAME);
2371 drdynvc->state = DRDYNVC_STATE_INITIAL;
2375 (pEntryPointsEx->MagicNumber == FREERDP_CHANNEL_MAGIC_NUMBER))
2377 context = (DrdynvcClientContext*)calloc(1,
sizeof(DrdynvcClientContext));
2381 WLog_Print(drdynvc->log, WLOG_ERROR,
"calloc failed!");
2386 context->handle = (
void*)drdynvc;
2387 context->custom =
nullptr;
2388 drdynvc->context = context;
2389 context->GetVersion = drdynvc_get_version;
2390 context->GetChannelStats = drdynvc_get_channel_stats;
2391 drdynvc->rdpcontext = pEntryPointsEx->context;
2393 FreeRDP_TransportDumpReplay) &&
2395 FreeRDP_SynchronousDynamicChannels))
2396 drdynvc->async = TRUE;
2399 drdynvc->log = WLog_Get(TAG);
2400 WLog_Print(drdynvc->log, WLOG_DEBUG,
"VirtualChannelEntryEx");
2401 CopyMemory(&(drdynvc->channelEntryPoints), pEntryPoints,
2403 drdynvc->InitHandle = pInitHandle;
2405 WINPR_ASSERT(drdynvc->channelEntryPoints.pVirtualChannelInitEx);
2406 rc = drdynvc->channelEntryPoints.pVirtualChannelInitEx(
2407 drdynvc, context, pInitHandle, &drdynvc->channelDef, 1, VIRTUAL_CHANNEL_VERSION_WIN2000,
2408 drdynvc_virtual_channel_init_event_ex);
2410 if (CHANNEL_RC_OK != rc)
2412 WLog_Print(drdynvc->log, WLOG_ERROR,
"pVirtualChannelInit failed with %s [%08" PRIX32
"]",
2413 WTSErrorToString(rc), rc);
2414 free(drdynvc->context);
2419 drdynvc->channelEntryPoints.pInterface = context;
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.
Dynamic channel stats struct. Contains statistic information for a single dynamic channel.
This struct contains function pointer to initialize/free objects.
OBJECT_FREE_FN fnObjectFree
WINPR_ATTR_NODISCARD OBJECT_EQUALS_FN fnObjectEquals
WINPR_ATTR_NODISCARD OBJECT_NEW_FN fnObjectNew