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")
38static const char* channel_state2str(DVC_CHANNEL_STATE state)
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";
49 return "DVC_CHANNEL_UNKNOWN";
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);
63 free(listener->channel_name);
72static UINT dvcman_get_configuration(IWTSListener* pListener,
void** ppPropertyBag)
74 WINPR_ASSERT(ppPropertyBag);
75 WINPR_UNUSED(pListener);
76 *ppPropertyBag =
nullptr;
77 return ERROR_INTERNAL_ERROR;
85static UINT dvcman_create_listener(IWTSVirtualChannelManager* pChannelMgr,
86 const char* pszChannelName, ULONG ulFlags,
87 IWTSListenerCallback* pListenerCallback,
88 IWTSListener** ppListener)
94 WLog_DBG(TAG,
"create_listener: %" PRIuz
".%s.", HashTable_Count(dvcman->listeners) + 1,
100 WLog_ERR(TAG,
"calloc failed!");
101 return CHANNEL_RC_NO_MEMORY;
104 listener->iface.GetConfiguration = dvcman_get_configuration;
105 listener->iface.pInterface =
nullptr;
106 listener->dvcman = dvcman;
107 listener->channel_name = _strdup(pszChannelName);
109 if (!listener->channel_name)
111 WLog_ERR(TAG,
"_strdup failed!");
112 dvcman_wtslistener_free(listener);
113 return CHANNEL_RC_NO_MEMORY;
116 listener->flags = ulFlags;
117 listener->listener_callback = pListenerCallback;
120 *ppListener = (IWTSListener*)listener;
122 if (!HashTable_Insert(dvcman->listeners, listener->channel_name, listener))
124 dvcman_wtslistener_free(listener);
125 return ERROR_INTERNAL_ERROR;
129 return CHANNEL_RC_OK;
132static UINT dvcman_destroy_listener(IWTSVirtualChannelManager* pChannelMgr, IWTSListener* pListener)
136 WINPR_UNUSED(pChannelMgr);
140 DVCMAN* dvcman = listener->dvcman;
142 HashTable_Remove(dvcman->listeners, listener->channel_name);
145 return CHANNEL_RC_OK;
153static UINT dvcman_register_plugin(IDRDYNVC_ENTRY_POINTS* pEntryPoints,
const char* name,
156 WINPR_ASSERT(pEntryPoints);
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;
165 WLog_DBG(TAG,
"register_plugin: num_plugins %" PRIuz, ArrayList_Count(dvcman->plugins));
166 return CHANNEL_RC_OK;
169static IWTSPlugin* dvcman_get_plugin(IDRDYNVC_ENTRY_POINTS* pEntryPoints,
const char* name)
171 IWTSPlugin* plugin =
nullptr;
174 WINPR_ASSERT(pEntryPoints);
176 if (!dvcman || !pEntryPoints || !name)
179 nc = ArrayList_Count(dvcman->plugin_names);
180 pc = ArrayList_Count(dvcman->plugins);
184 ArrayList_Lock(dvcman->plugin_names);
185 ArrayList_Lock(dvcman->plugins);
186 for (
size_t i = 0; i < pc; i++)
188 const char* cur = ArrayList_GetItem(dvcman->plugin_names, i);
189 if (strcmp(cur, name) == 0)
191 plugin = ArrayList_GetItem(dvcman->plugins, i);
195 ArrayList_Unlock(dvcman->plugin_names);
196 ArrayList_Unlock(dvcman->plugins);
200static const ADDIN_ARGV* dvcman_get_plugin_data(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
202 WINPR_ASSERT(pEntryPoints);
206static rdpContext* dvcman_get_rdp_context(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
210 return entry->context;
213static rdpSettings* dvcman_get_rdp_settings(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
215 rdpContext* context = dvcman_get_rdp_context(pEntryPoints);
216 WINPR_ASSERT(context);
218 return context->settings;
221static UINT32 dvcman_get_channel_id(IWTSVirtualChannel* channel)
225 return dvc->channel_id;
228static const char* dvcman_get_channel_name(IWTSVirtualChannel* channel)
232 return dvc->channel_name;
235static DVCMAN_CHANNEL* dvcman_get_channel_by_id(IWTSVirtualChannelManager* pChannelMgr,
236 UINT32 ChannelId, BOOL doRef)
241 WINPR_ASSERT(dvcman);
242 HashTable_Lock(dvcman->channelsById);
243 dvcChannel = HashTable_GetItemValue(dvcman->channelsById, &ChannelId);
247 InterlockedIncrement(&dvcChannel->refCounter);
250 HashTable_Unlock(dvcman->channelsById);
254static IWTSVirtualChannel* dvcman_find_channel_by_id(IWTSVirtualChannelManager* pChannelMgr,
257 DVCMAN_CHANNEL* channel = dvcman_get_channel_by_id(pChannelMgr, ChannelId, FALSE);
261 return &channel->iface;
264static void dvcman_plugin_terminate(
void* plugin)
266 IWTSPlugin* pPlugin = plugin;
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);
274static void wts_listener_free(
void* arg)
277 dvcman_wtslistener_free(listener);
280static BOOL channelIdMatch(
const void* k1,
const void* k2)
284 return *((
const UINT32*)k1) == *((
const UINT32*)k2);
287static UINT32 channelIdHash(
const void*
id)
290 return *((
const UINT32*)
id);
293static void channelByIdCleanerFn(
void* value)
298 dvcman_channel_close(channel, FALSE, TRUE);
299 dvcman_channel_free(channel);
303static IWTSVirtualChannelManager* dvcman_new(drdynvcPlugin* plugin)
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);
319 if (!dvcman->channelsById)
322 if (!HashTable_SetHashFunction(dvcman->channelsById, channelIdHash))
325 obj = HashTable_KeyObject(dvcman->channelsById);
329 obj = HashTable_ValueObject(dvcman->channelsById);
333 dvcman->pool = StreamPool_New(TRUE, 10);
337 dvcman->listeners = HashTable_New(TRUE);
338 if (!dvcman->listeners)
341 if (!HashTable_SetHashFunction(dvcman->listeners, HashTable_StringHash))
344 obj = HashTable_KeyObject(dvcman->listeners);
347 obj = HashTable_ValueObject(dvcman->listeners);
350 dvcman->plugin_names = ArrayList_New(TRUE);
351 if (!dvcman->plugin_names)
353 obj = ArrayList_Object(dvcman->plugin_names);
357 dvcman->plugins = ArrayList_New(TRUE);
358 if (!dvcman->plugins)
360 obj = ArrayList_Object(dvcman->plugins);
362 return &dvcman->iface;
364 dvcman_free(plugin, &dvcman->iface);
373static UINT dvcman_load_addin(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr,
376 WINPR_ASSERT(drdynvc);
377 WINPR_ASSERT(pChannelMgr);
379 WINPR_ASSERT(context);
381 WLog_Print(drdynvc->log, WLOG_INFO,
"Loading Dynamic Virtual Channel %s", args->argv[0]);
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);
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);
402 return ERROR_INVALID_FUNCTION;
412 drdynvcPlugin* plugin = channel->dvcman->drdynvc;
415 rdpContext* context = plugin->rdpcontext;
418 ChannelTerminatedEventArgs e = WINPR_C_ARRAY_INIT;
419 EventArgsInit(&e,
"freerdp");
420 e.name = channel->channel_name;
421 e.pInterface = channel->pInterface;
423 const int rc = PubSub_OnChannelTerminated(context->pubSub, context, &e);
425 WLog_WARN(TAG,
"PubSub_OnChannelTerminated(%s) failed", channel->channel_name);
430 if (channel->dvc_data)
431 Stream_Release(channel->dvc_data);
433 zgfx_context_free(channel->decompressor);
434 DeleteCriticalSection(&(channel->lock));
435 free(channel->channel_name);
441 WINPR_ASSERT(channel);
442 if (InterlockedDecrement(&channel->refCounter))
445 DVCMAN* dvcman = channel->dvcman;
447 HashTable_Remove(dvcman->channelsById, &channel->channel_id);
452 WINPR_ASSERT(channel);
453 DVCMAN* dvcman = channel->dvcman;
454 drdynvcPlugin* drdynvc = dvcman->drdynvc;
455 wStream* s = StreamPool_Take(dvcman->pool, 5);
459 WLog_Print(drdynvc->log, WLOG_ERROR,
"StreamPool_Take failed!");
460 return CHANNEL_RC_NO_MEMORY;
463 Stream_Write_UINT8(s, (CLOSE_REQUEST_PDU << 4) | 0x02);
464 Stream_Write_UINT32(s, channel->channel_id);
465 return drdynvc_send(drdynvc, s);
470 WINPR_ASSERT(channel);
472 IWTSVirtualChannelCallback* cb = channel->channel_callback;
473 const char* name = channel->channel_name;
474 const UINT32
id = channel->channel_id;
477 if (!cb->OnOpen || !cb->OnClose || !cb->OnDataReceived)
478 WLog_VRB(TAG,
"{%s:%" PRIu32
"} OnOpen=%p, OnClose=%p, OnDataReceived=%p", name,
id,
479 WINPR_FUNC_PTR_CAST(cb->OnOpen,
const void*),
480 WINPR_FUNC_PTR_CAST(cb->OnClose,
const void*),
481 WINPR_FUNC_PTR_CAST(cb->OnDataReceived,
const void*));
486 WINPR_ASSERT(channel);
489 IWTSVirtualChannelCallback* cb = channel->channel_callback;
492 check_open_close_receive(channel);
493 WINPR_ASSERT(cb->OnDataReceived);
494 return cb->OnDataReceived(cb, data);
497static UINT dvcman_channel_close(
DVCMAN_CHANNEL* channel, BOOL perRequest, BOOL fromHashTableFn)
499 UINT error = CHANNEL_RC_OK;
500 DrdynvcClientContext* context =
nullptr;
502 WINPR_ASSERT(channel);
503 switch (channel->state)
505 case DVC_CHANNEL_INIT:
507 case DVC_CHANNEL_RUNNING:
510 drdynvcPlugin* drdynvc = channel->dvcman->drdynvc;
511 WINPR_ASSERT(drdynvc);
512 context = drdynvc->context;
514 WLog_Print(drdynvc->log, WLOG_DEBUG,
"sending close confirm for '%s'",
515 channel->channel_name);
517 error = dvcchannel_send_close(channel);
518 if (error != CHANNEL_RC_OK)
521 WLog_Print(drdynvc->log, WLOG_DEBUG,
522 "error when sending closeRequest for '%s'",
523 channel->channel_name);
525 WLog_Print(drdynvc->log, WLOG_DEBUG,
526 "error when sending close confirm for '%s'",
527 channel->channel_name);
529 WLog_Print(drdynvc->log, WLOG_DEBUG,
"listener %s destroyed channel %" PRIu32
"",
530 channel->channel_name, channel->channel_id);
533 channel->state = DVC_CHANNEL_CLOSED;
536 check_open_close_receive(channel);
538 IWTSVirtualChannelCallback* cb = channel->channel_callback;
539 channel->channel_callback =
nullptr;
541 error = IFCALLRESULT(CHANNEL_RC_OK, cb->OnClose, cb);
544 if (channel->dvcman && channel->dvcman->drdynvc)
548 IFCALLRET(context->OnChannelDisconnected, error, context, channel->channel_name,
549 channel->pInterface);
553 if (!fromHashTableFn)
554 dvcman_channel_unref(channel);
556 case DVC_CHANNEL_CLOSED:
566 IWTSVirtualChannelManager* pChannelMgr, UINT32 ChannelId,
567 const char* ChannelName)
569 WINPR_ASSERT(drdynvc);
570 WINPR_ASSERT(pChannelMgr);
576 channel->dvcman = (
DVCMAN*)pChannelMgr;
577 channel->channel_id = ChannelId;
578 channel->refCounter = 1;
579 channel->state = DVC_CHANNEL_INIT;
580 channel->channel_name = _strdup(ChannelName);
581 if (!channel->channel_name)
584 channel->decompressor = zgfx_context_new(FALSE);
585 if (!channel->decompressor)
588 if (!InitializeCriticalSectionEx(&(channel->lock), 0, 0))
593 rdpContext* context = drdynvc->rdpcontext;
596 ChannelInitializedEventArgs e = WINPR_C_ARRAY_INIT;
597 EventArgsInit(&e,
"freerdp");
598 e.name = channel->channel_name;
599 e.pInterface = channel->pInterface;
601 const int rc = PubSub_OnChannelInitialized(context->pubSub, context, &e);
603 WLog_WARN(TAG,
"PubSub_OnChannelInitialized(%s) failed", channel->channel_name);
609 dvcman_channel_free(channel);
613static void dvcman_clear(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr)
617 WINPR_ASSERT(dvcman);
618 WINPR_UNUSED(drdynvc);
620 HashTable_Clear(dvcman->channelsById);
621 ArrayList_Clear(dvcman->plugins);
622 ArrayList_Clear(dvcman->plugin_names);
623 HashTable_Clear(dvcman->listeners);
625static void dvcman_free(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr)
629 WINPR_ASSERT(dvcman);
630 WINPR_UNUSED(drdynvc);
632 HashTable_Free(dvcman->channelsById);
633 ArrayList_Free(dvcman->plugins);
634 ArrayList_Free(dvcman->plugin_names);
635 HashTable_Free(dvcman->listeners);
637 StreamPool_Free(dvcman->pool);
646static UINT dvcman_init(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr)
649 UINT error = CHANNEL_RC_OK;
651 WINPR_ASSERT(dvcman);
652 ArrayList_Lock(dvcman->plugins);
653 for (
size_t i = 0; i < ArrayList_Count(dvcman->plugins); i++)
655 IWTSPlugin* pPlugin = ArrayList_GetItem(dvcman->plugins, i);
657 error = IFCALLRESULT(CHANNEL_RC_OK, pPlugin->Initialize, pPlugin, pChannelMgr);
658 if (error != CHANNEL_RC_OK)
660 WLog_Print(drdynvc->log, WLOG_ERROR,
"Initialize failed with error %" PRIu32
"!",
667 ArrayList_Unlock(dvcman->plugins);
676static UINT dvcman_write_channel(IWTSVirtualChannel* pChannel, ULONG cbSize,
const BYTE* pBuffer,
683 WINPR_UNUSED(pReserved);
684 if (!channel || !channel->dvcman)
685 return CHANNEL_RC_BAD_CHANNEL;
687 EnterCriticalSection(&(channel->lock));
689 drdynvc_write_data(channel->dvcman->drdynvc, channel->channel_id, pBuffer, cbSize, &close);
690 LeaveCriticalSection(&(channel->lock));
693 dvcman_channel_close(channel, FALSE, FALSE);
703static UINT dvcman_close_channel_iface(IWTSVirtualChannel* pChannel)
708 return CHANNEL_RC_BAD_CHANNEL;
710 WLog_DBG(TAG,
"close_channel_iface: id=%" PRIu32
"", channel->channel_id);
711 return dvcman_channel_close(channel, FALSE, FALSE);
719static DVCMAN_CHANNEL* dvcman_create_channel(drdynvcPlugin* drdynvc,
720 IWTSVirtualChannelManager* pChannelMgr,
721 UINT32 ChannelId,
const char* ChannelName, UINT* res)
725 DrdynvcClientContext* context =
nullptr;
728 IWTSVirtualChannelCallback* pCallback =
nullptr;
730 WINPR_ASSERT(dvcman);
733 HashTable_Lock(dvcman->listeners);
734 listener = (
DVCMAN_LISTENER*)HashTable_GetItemValue(dvcman->listeners, ChannelName);
737 *res = ERROR_NOT_FOUND;
741 channel = dvcman_get_channel_by_id(pChannelMgr, ChannelId, FALSE);
744 switch (channel->state)
746 case DVC_CHANNEL_RUNNING:
747 WLog_Print(drdynvc->log, WLOG_ERROR,
748 "Protocol error: Duplicated ChannelId %" PRIu32
" (%s)!", ChannelId,
750 *res = CHANNEL_RC_ALREADY_OPEN;
753 case DVC_CHANNEL_CLOSED:
754 case DVC_CHANNEL_INIT:
757 WLog_Print(drdynvc->log, WLOG_ERROR,
"not expecting a createChannel from state %s",
758 channel_state2str(channel->state));
759 *res = CHANNEL_RC_INITIALIZATION_ERROR;
766 if (!(channel = dvcman_channel_new(drdynvc, pChannelMgr, ChannelId, ChannelName)))
768 WLog_Print(drdynvc->log, WLOG_ERROR,
"dvcman_channel_new failed!");
769 *res = CHANNEL_RC_NO_MEMORY;
774 if (!HashTable_Insert(dvcman->channelsById, &channel->channel_id, channel))
776 WLog_Print(drdynvc->log, WLOG_ERROR,
"unable to register channel in our channel list");
777 *res = ERROR_INTERNAL_ERROR;
778 dvcman_channel_free(channel);
783 channel->iface.Write = dvcman_write_channel;
784 channel->iface.Close = dvcman_close_channel_iface;
787 *res = listener->listener_callback->OnNewChannelConnection(
788 listener->listener_callback, &channel->iface,
nullptr, &bAccept, &pCallback);
790 if (*res != CHANNEL_RC_OK)
792 WLog_Print(drdynvc->log, WLOG_ERROR,
793 "OnNewChannelConnection failed with error %" PRIu32
"!", *res);
794 *res = ERROR_INTERNAL_ERROR;
795 dvcman_channel_unref(channel);
801 WLog_Print(drdynvc->log, WLOG_ERROR,
"OnNewChannelConnection returned with bAccept FALSE!");
802 *res = ERROR_INTERNAL_ERROR;
803 dvcman_channel_unref(channel);
808 WLog_Print(drdynvc->log, WLOG_DEBUG,
"listener %s created new channel %" PRIu32
"",
809 listener->channel_name, channel->channel_id);
810 channel->state = DVC_CHANNEL_RUNNING;
811 channel->channel_callback = pCallback;
812 channel->pInterface = listener->iface.pInterface;
813 context = dvcman->drdynvc->context;
815 IFCALLRET(context->OnChannelConnected, *res, context, ChannelName, listener->iface.pInterface);
816 if (*res != CHANNEL_RC_OK)
818 WLog_Print(drdynvc->log, WLOG_ERROR,
819 "context.OnChannelConnected failed with error %" PRIu32
"", *res);
823 HashTable_Unlock(dvcman->listeners);
833static UINT dvcman_open_channel(drdynvcPlugin* drdynvc,
DVCMAN_CHANNEL* channel)
835 UINT error = CHANNEL_RC_OK;
837 WINPR_ASSERT(drdynvc);
838 WINPR_ASSERT(channel);
839 if (channel->state == DVC_CHANNEL_RUNNING)
841 IWTSVirtualChannelCallback* pCallback = channel->channel_callback;
843 if (pCallback->OnOpen)
845 check_open_close_receive(channel);
846 error = pCallback->OnOpen(pCallback);
849 WLog_Print(drdynvc->log, WLOG_ERROR,
"OnOpen failed with error %" PRIu32
"!",
855 WLog_Print(drdynvc->log, WLOG_DEBUG,
"open_channel: ChannelId %" PRIu32
"",
856 channel->channel_id);
868static UINT dvcman_receive_channel_data_first(
DVCMAN_CHANNEL* channel, UINT32 length)
870 WINPR_ASSERT(channel);
871 WINPR_ASSERT(channel->dvcman);
872 if (channel->dvc_data)
873 Stream_Release(channel->dvc_data);
875 channel->dvc_data = StreamPool_Take(channel->dvcman->pool, length);
877 if (!channel->dvc_data)
879 drdynvcPlugin* drdynvc = channel->dvcman->drdynvc;
880 WLog_Print(drdynvc->log, WLOG_ERROR,
"StreamPool_Take failed!");
881 return CHANNEL_RC_NO_MEMORY;
884 channel->dvc_data_length = length;
885 return CHANNEL_RC_OK;
894 WINPR_ATTR_UNUSED UINT32 ThreadingFlags)
896 UINT status = CHANNEL_RC_OK;
897 size_t dataSize = Stream_GetRemainingLength(data);
899 WINPR_ASSERT(channel);
900 WINPR_ASSERT(channel->dvcman);
901 if (channel->dvc_data)
903 drdynvcPlugin* drdynvc = channel->dvcman->drdynvc;
906 if (Stream_GetPosition(channel->dvc_data) + dataSize > channel->dvc_data_length)
908 WLog_Print(drdynvc->log, WLOG_ERROR,
"data exceeding declared length!");
909 Stream_Release(channel->dvc_data);
910 channel->dvc_data =
nullptr;
911 status = ERROR_INVALID_DATA;
915 Stream_Copy(data, channel->dvc_data, dataSize);
917 if (Stream_GetPosition(channel->dvc_data) >= channel->dvc_data_length)
919 Stream_SealLength(channel->dvc_data);
920 Stream_ResetPosition(channel->dvc_data);
922 status = dvcman_call_on_receive(channel, channel->dvc_data);
923 Stream_Release(channel->dvc_data);
924 channel->dvc_data =
nullptr;
928 status = dvcman_call_on_receive(channel, data);
934static UINT8 drdynvc_write_variable_uint(
wStream* s, UINT32 val)
941 Stream_Write_UINT8(s, (UINT8)val);
943 else if (val <= 0xFFFF)
946 Stream_Write_UINT16(s, (UINT16)val);
951 Stream_Write_UINT32(s, val);
962static UINT drdynvc_send(drdynvcPlugin* drdynvc,
wStream* s)
967 status = CHANNEL_RC_BAD_CHANNEL_HANDLE;
970 WINPR_ASSERT(drdynvc->channelEntryPoints.pVirtualChannelWriteEx);
971 status = drdynvc->channelEntryPoints.pVirtualChannelWriteEx(
972 drdynvc->InitHandle, drdynvc->OpenHandle, Stream_Buffer(s),
973 (UINT32)Stream_GetPosition(s), s);
979 return CHANNEL_RC_OK;
981 case CHANNEL_RC_NOT_CONNECTED:
983 return CHANNEL_RC_OK;
985 case CHANNEL_RC_BAD_CHANNEL_HANDLE:
987 WLog_ERR(TAG,
"VirtualChannelWriteEx failed with CHANNEL_RC_BAD_CHANNEL_HANDLE");
992 WLog_Print(drdynvc->log, WLOG_ERROR,
993 "VirtualChannelWriteEx failed with %s [%08" PRIX32
"]",
994 WTSErrorToString(status), status);
1004static UINT drdynvc_write_data(drdynvcPlugin* drdynvc, UINT32 ChannelId,
const BYTE* data,
1005 UINT32 dataSize, BOOL* close)
1010 UINT status = CHANNEL_RC_BAD_INIT_HANDLE;
1011 DVCMAN* dvcman =
nullptr;
1014 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1016 dvcman = (
DVCMAN*)drdynvc->channel_mgr;
1017 WINPR_ASSERT(dvcman);
1019 WLog_Print(drdynvc->log, WLOG_TRACE,
"write_data: ChannelId=%" PRIu32
" size=%" PRIu32
"",
1020 ChannelId, dataSize);
1021 wStream* data_out = StreamPool_Take(dvcman->pool, CHANNEL_CHUNK_LENGTH);
1025 WLog_Print(drdynvc->log, WLOG_ERROR,
"StreamPool_Take failed!");
1026 return CHANNEL_RC_NO_MEMORY;
1029 if (!Stream_SetPosition(data_out, 1))
1031 Stream_Release(data_out);
1032 return ERROR_INVALID_DATA;
1034 cbChId = drdynvc_write_variable_uint(data_out, ChannelId);
1035 pos = Stream_GetPosition(data_out);
1041 Stream_Release(data_out);
1043 else if (dataSize <= CHANNEL_CHUNK_LENGTH - pos)
1045 Stream_ResetPosition(data_out);
1046 Stream_Write_UINT8(data_out, (DATA_PDU << 4) | cbChId);
1047 if (!Stream_SetPosition(data_out, pos))
1049 Stream_Release(data_out);
1050 return ERROR_INVALID_DATA;
1052 Stream_Write(data_out, data, dataSize);
1053 status = drdynvc_send(drdynvc, data_out);
1058 cbLen = drdynvc_write_variable_uint(data_out, dataSize);
1059 pos = Stream_GetPosition(data_out);
1060 Stream_ResetPosition(data_out);
1062 const INT32 pdu = (DATA_FIRST_PDU << 4) | cbChId | (cbLen << 2);
1063 Stream_Write_UINT8(data_out, WINPR_ASSERTING_INT_CAST(UINT8, pdu));
1064 if (!Stream_SetPosition(data_out, pos))
1066 Stream_Release(data_out);
1067 return ERROR_INVALID_DATA;
1071 WINPR_ASSERT(pos <= CHANNEL_CHUNK_LENGTH);
1072 const uint32_t chunkLength =
1073 CHANNEL_CHUNK_LENGTH - WINPR_ASSERTING_INT_CAST(uint32_t, pos);
1074 Stream_Write(data_out, data, chunkLength);
1076 data += chunkLength;
1077 dataSize -= chunkLength;
1079 status = drdynvc_send(drdynvc, data_out);
1081 while (status == CHANNEL_RC_OK && dataSize > 0)
1083 data_out = StreamPool_Take(dvcman->pool, CHANNEL_CHUNK_LENGTH);
1087 WLog_Print(drdynvc->log, WLOG_ERROR,
"StreamPool_Take failed!");
1088 return CHANNEL_RC_NO_MEMORY;
1091 if (!Stream_SetPosition(data_out, 1))
1093 Stream_Release(data_out);
1094 return ERROR_INVALID_DATA;
1097 cbChId = drdynvc_write_variable_uint(data_out, ChannelId);
1098 pos = Stream_GetPosition(data_out);
1099 Stream_ResetPosition(data_out);
1100 Stream_Write_UINT8(data_out, (DATA_PDU << 4) | cbChId);
1101 if (!Stream_SetPosition(data_out, pos))
1103 Stream_Release(data_out);
1104 return ERROR_INVALID_DATA;
1107 uint32_t chunkLength = dataSize;
1109 WINPR_ASSERT(pos <= CHANNEL_CHUNK_LENGTH);
1110 if (chunkLength > CHANNEL_CHUNK_LENGTH - pos)
1111 chunkLength = CHANNEL_CHUNK_LENGTH - WINPR_ASSERTING_INT_CAST(uint32_t, pos);
1113 Stream_Write(data_out, data, chunkLength);
1114 data += chunkLength;
1115 dataSize -= chunkLength;
1116 status = drdynvc_send(drdynvc, data_out);
1120 if (status != CHANNEL_RC_OK)
1122 WLog_Print(drdynvc->log, WLOG_ERROR,
"VirtualChannelWriteEx failed with %s [%08" PRIX32
"]",
1123 WTSErrorToString(status), status);
1127 return CHANNEL_RC_OK;
1135static UINT drdynvc_send_capability_response(drdynvcPlugin* drdynvc)
1139 DVCMAN* dvcman =
nullptr;
1142 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1144 dvcman = (
DVCMAN*)drdynvc->channel_mgr;
1145 WINPR_ASSERT(dvcman);
1147 WLog_Print(drdynvc->log, WLOG_TRACE,
"capability_response");
1148 s = StreamPool_Take(dvcman->pool, 4);
1152 WLog_Print(drdynvc->log, WLOG_ERROR,
"Stream_New failed!");
1153 return CHANNEL_RC_NO_MEMORY;
1156 Stream_Write_UINT16(s, 0x0050);
1157 Stream_Write_UINT16(s, drdynvc->version);
1158 status = drdynvc_send(drdynvc, s);
1160 if (status != CHANNEL_RC_OK)
1162 WLog_Print(drdynvc->log, WLOG_ERROR,
"VirtualChannelWriteEx failed with %s [%08" PRIX32
"]",
1163 WTSErrorToString(status), status);
1174static UINT drdynvc_process_capability_request(drdynvcPlugin* drdynvc,
int Sp,
int cbChId,
1180 return CHANNEL_RC_BAD_INIT_HANDLE;
1182 if (!Stream_CheckAndLogRequiredLength(TAG, s, 3))
1183 return ERROR_INVALID_DATA;
1185 WLog_Print(drdynvc->log, WLOG_TRACE,
"capability_request Sp=%d cbChId=%d", Sp, cbChId);
1187 Stream_Read_UINT16(s, drdynvc->version);
1192 if ((drdynvc->version == 2) || (drdynvc->version == 3))
1194 if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
1195 return ERROR_INVALID_DATA;
1197 Stream_Read_UINT16(s, drdynvc->PriorityCharge0);
1198 Stream_Read_UINT16(s, drdynvc->PriorityCharge1);
1199 Stream_Read_UINT16(s, drdynvc->PriorityCharge2);
1200 Stream_Read_UINT16(s, drdynvc->PriorityCharge3);
1203 status = drdynvc_send_capability_response(drdynvc);
1204 drdynvc->state = DRDYNVC_STATE_READY;
1208static UINT32 drdynvc_cblen_to_bytes(
int cbLen)
1223static UINT32 drdynvc_read_variable_uint(
wStream* s,
int cbLen)
1230 Stream_Read_UINT8(s, val);
1234 Stream_Read_UINT16(s, val);
1238 Stream_Read_UINT32(s, val);
1250static UINT drdynvc_process_create_request(drdynvcPlugin* drdynvc, UINT8 Sp, UINT8 cbChId,
1255 UINT channel_status = 0;
1256 DVCMAN* dvcman =
nullptr;
1258 INT32 retStatus = 0;
1262 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1264 dvcman = (
DVCMAN*)drdynvc->channel_mgr;
1265 WINPR_ASSERT(dvcman);
1267 if (drdynvc->state == DRDYNVC_STATE_CAPABILITIES)
1274 drdynvc->version = 3;
1276 if ((status = drdynvc_send_capability_response(drdynvc)))
1278 WLog_Print(drdynvc->log, WLOG_ERROR,
"drdynvc_send_capability_response failed!");
1282 drdynvc->state = DRDYNVC_STATE_READY;
1285 if (!Stream_CheckAndLogRequiredLength(TAG, s, drdynvc_cblen_to_bytes(cbChId)))
1286 return ERROR_INVALID_DATA;
1288 const UINT32 ChannelId = drdynvc_read_variable_uint(s, cbChId);
1289 const size_t pos = Stream_GetPosition(s);
1290 const char* name = Stream_ConstPointer(s);
1291 const size_t length = Stream_GetRemainingLength(s);
1293 if (strnlen(name, length) >= length)
1294 return ERROR_INVALID_DATA;
1296 WLog_Print(drdynvc->log, WLOG_DEBUG,
1297 "process_create_request: ChannelId=%" PRIu32
" ChannelName=%s", ChannelId, name);
1299 data_out = StreamPool_Take(dvcman->pool, pos + 4);
1302 WLog_Print(drdynvc->log, WLOG_ERROR,
"StreamPool_Take failed!");
1303 return CHANNEL_RC_NO_MEMORY;
1306 Stream_Write_UINT8(data_out, (CREATE_REQUEST_PDU << 4) | cbChId);
1307 if (!Stream_SetPosition(s, 1))
1308 return ERROR_INVALID_DATA;
1309 Stream_Copy(s, data_out, pos - 1);
1312 dvcman_create_channel(drdynvc, drdynvc->channel_mgr, ChannelId, name, &channel_status);
1313 switch (channel_status)
1316 WLog_Print(drdynvc->log, WLOG_DEBUG,
"channel created");
1319 case CHANNEL_RC_NO_MEMORY:
1320 WLog_Print(drdynvc->log, WLOG_DEBUG,
"not enough memory for channel creation");
1321 retStatus = STATUS_NO_MEMORY;
1323 case ERROR_NOT_FOUND:
1324 WLog_Print(drdynvc->log, WLOG_DEBUG,
"no listener for '%s'", name);
1325 retStatus = STATUS_NOT_FOUND;
1328 WLog_Print(drdynvc->log, WLOG_DEBUG,
"channel creation error");
1329 retStatus = STATUS_UNSUCCESSFUL;
1332 Stream_Write_INT32(data_out, retStatus);
1334 status = drdynvc_send(drdynvc, data_out);
1335 if (status != CHANNEL_RC_OK)
1337 WLog_Print(drdynvc->log, WLOG_ERROR,
"VirtualChannelWriteEx failed with %s [%08" PRIX32
"]",
1338 WTSErrorToString(status), status);
1339 dvcman_channel_unref(channel);
1343 if (channel_status == CHANNEL_RC_OK)
1345 if ((status = dvcman_open_channel(drdynvc, channel)))
1347 WLog_Print(drdynvc->log, WLOG_ERROR,
1348 "dvcman_open_channel failed with error %" PRIu32
"!", status);
1361static UINT drdynvc_process_data_first(drdynvcPlugin* drdynvc,
int Sp,
int cbChId,
wStream* s,
1362 BOOL compressed, UINT32 ThreadingFlags)
1364 WINPR_ASSERT(drdynvc);
1365 if (!Stream_CheckAndLogRequiredLength(
1366 TAG, s, drdynvc_cblen_to_bytes(cbChId) + drdynvc_cblen_to_bytes(Sp)))
1367 return ERROR_INVALID_DATA;
1369 UINT32 ChannelId = drdynvc_read_variable_uint(s, cbChId);
1370 UINT32 Length = drdynvc_read_variable_uint(s, Sp);
1371 WLog_Print(drdynvc->log, WLOG_TRACE,
1372 "process_data_first: Sp=%d cbChId=%d, ChannelId=%" PRIu32
" Length=%" PRIu32
"", Sp,
1373 cbChId, ChannelId, Length);
1375 DVCMAN_CHANNEL* channel = dvcman_get_channel_by_id(drdynvc->channel_mgr, ChannelId, TRUE);
1383 WLog_Print(drdynvc->log, WLOG_ERROR,
"ChannelId %" PRIu32
" not found!", ChannelId);
1384 return CHANNEL_RC_OK;
1387 UINT status = CHANNEL_RC_OK;
1388 BOOL shouldFree = FALSE;
1389 if (channel->state != DVC_CHANNEL_RUNNING)
1394 BYTE* data =
nullptr;
1395 UINT32 dataSize = 0;
1396 if (zgfx_decompress(channel->decompressor, Stream_Pointer(s),
1397 WINPR_ASSERTING_INT_CAST(UINT32, Stream_GetRemainingLength(s)), &data,
1400 status = ERROR_INVALID_DATA;
1401 WLog_Print(drdynvc->log, WLOG_ERROR,
"error de-compressing first packet");
1405 s = Stream_New(data, dataSize);
1408 status = CHANNEL_RC_NO_MEMORY;
1409 WLog_Print(drdynvc->log, WLOG_ERROR,
"error allocating new Stream(len=%" PRIu32
")",
1417 status = dvcman_receive_channel_data_first(channel, Length);
1419 if (status == CHANNEL_RC_OK)
1420 status = dvcman_receive_channel_data(channel, s, ThreadingFlags);
1422 if (status != CHANNEL_RC_OK)
1423 status = dvcman_channel_close(channel, FALSE, FALSE);
1427 Stream_Free(s, TRUE);
1428 dvcman_channel_unref(channel);
1437static UINT drdynvc_process_data(drdynvcPlugin* drdynvc,
int Sp,
int cbChId,
wStream* s,
1438 BOOL compressed, UINT32 ThreadingFlags)
1440 WINPR_ASSERT(drdynvc);
1441 if (!Stream_CheckAndLogRequiredLength(TAG, s, drdynvc_cblen_to_bytes(cbChId)))
1442 return ERROR_INVALID_DATA;
1444 UINT32 ChannelId = drdynvc_read_variable_uint(s, cbChId);
1445 WLog_Print(drdynvc->log, WLOG_TRACE,
"process_data: Sp=%d cbChId=%d, ChannelId=%" PRIu32
"", Sp,
1448 DVCMAN_CHANNEL* channel = dvcman_get_channel_by_id(drdynvc->channel_mgr, ChannelId, TRUE);
1456 WLog_Print(drdynvc->log, WLOG_ERROR,
"ChannelId %" PRIu32
" not found!", ChannelId);
1457 return CHANNEL_RC_OK;
1460 BOOL shouldFree = FALSE;
1461 UINT status = CHANNEL_RC_OK;
1462 if (channel->state != DVC_CHANNEL_RUNNING)
1467 BYTE* data =
nullptr;
1468 UINT32 dataSize = 0;
1470 if (zgfx_decompress(channel->decompressor, Stream_Pointer(s),
1471 WINPR_ASSERTING_INT_CAST(UINT32, Stream_GetRemainingLength(s)), &data,
1474 status = ERROR_INVALID_DATA;
1475 WLog_Print(drdynvc->log, WLOG_ERROR,
"error de-compressing data packet");
1479 s = Stream_New(data, dataSize);
1482 status = CHANNEL_RC_NO_MEMORY;
1483 WLog_Print(drdynvc->log, WLOG_ERROR,
"error allocating new Stream(len=%" PRIu32
")",
1491 status = dvcman_receive_channel_data(channel, s, ThreadingFlags);
1492 if (status != CHANNEL_RC_OK)
1493 status = dvcman_channel_close(channel, FALSE, FALSE);
1497 Stream_Free(s, TRUE);
1498 dvcman_channel_unref(channel);
1507static UINT drdynvc_process_close_request(drdynvcPlugin* drdynvc,
int Sp,
int cbChId,
wStream* s)
1509 UINT32 ChannelId = 0;
1512 WINPR_ASSERT(drdynvc);
1513 if (!Stream_CheckAndLogRequiredLength(TAG, s, drdynvc_cblen_to_bytes(cbChId)))
1514 return ERROR_INVALID_DATA;
1516 ChannelId = drdynvc_read_variable_uint(s, cbChId);
1517 WLog_Print(drdynvc->log, WLOG_DEBUG,
1518 "process_close_request: Sp=%d cbChId=%d, ChannelId=%" PRIu32
"", Sp, cbChId,
1521 channel = dvcman_get_channel_by_id(drdynvc->channel_mgr, ChannelId, TRUE);
1524 WLog_Print(drdynvc->log, WLOG_ERROR,
"dvcman_close_request channel %" PRIu32
" not present",
1526 return CHANNEL_RC_OK;
1529 dvcman_channel_close(channel, TRUE, FALSE);
1530 dvcman_channel_unref(channel);
1531 return CHANNEL_RC_OK;
1539static UINT drdynvc_order_recv(drdynvcPlugin* drdynvc,
wStream* s, UINT32 ThreadingFlags)
1541 WINPR_ASSERT(drdynvc);
1542 if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
1543 return ERROR_INVALID_DATA;
1545 UINT8 value = Stream_Get_UINT8(s);
1546 const UINT8 Cmd = (value & 0xf0) >> 4;
1547 const UINT8 Sp = (value & 0x0c) >> 2;
1548 const UINT8 cbChId = (value & 0x03) >> 0;
1549 WLog_Print(drdynvc->log, WLOG_TRACE,
"order_recv: Cmd=%s, Sp=%" PRIu8
" cbChId=%" PRIu8,
1550 drdynvc_get_packet_type(Cmd), Sp, cbChId);
1554 case CAPABILITY_REQUEST_PDU:
1555 return drdynvc_process_capability_request(drdynvc, Sp, cbChId, s);
1557 case CREATE_REQUEST_PDU:
1558 return drdynvc_process_create_request(drdynvc, Sp, cbChId, s);
1560 case DATA_FIRST_PDU:
1561 case DATA_FIRST_COMPRESSED_PDU:
1562 return drdynvc_process_data_first(drdynvc, Sp, cbChId, s,
1563 (Cmd == DATA_FIRST_COMPRESSED_PDU), ThreadingFlags);
1566 case DATA_COMPRESSED_PDU:
1567 return drdynvc_process_data(drdynvc, Sp, cbChId, s, (Cmd == DATA_COMPRESSED_PDU),
1570 case CLOSE_REQUEST_PDU:
1571 return drdynvc_process_close_request(drdynvc, Sp, cbChId, s);
1573 case SOFT_SYNC_RESPONSE_PDU:
1574 WLog_Print(drdynvc->log, WLOG_ERROR,
1575 "not expecting a SOFT_SYNC_RESPONSE_PDU as a client");
1576 return ERROR_INTERNAL_ERROR;
1579 WLog_Print(drdynvc->log, WLOG_ERROR,
"unknown drdynvc cmd 0x%x", Cmd);
1580 return ERROR_INTERNAL_ERROR;
1589static UINT drdynvc_virtual_channel_event_data_received(drdynvcPlugin* drdynvc,
void* pData,
1590 UINT32 dataLength, UINT32 totalLength,
1595 WINPR_ASSERT(drdynvc);
1596 if ((dataFlags & CHANNEL_FLAG_SUSPEND) || (dataFlags & CHANNEL_FLAG_RESUME))
1598 return CHANNEL_RC_OK;
1601 if (dataFlags & CHANNEL_FLAG_FIRST)
1604 if (drdynvc->data_in)
1605 Stream_Release(drdynvc->data_in);
1607 drdynvc->data_in = StreamPool_Take(mgr->pool, totalLength);
1610 if (!(data_in = drdynvc->data_in))
1612 WLog_Print(drdynvc->log, WLOG_ERROR,
"StreamPool_Take failed!");
1613 return CHANNEL_RC_NO_MEMORY;
1616 if (!Stream_EnsureRemainingCapacity(data_in, dataLength))
1618 WLog_Print(drdynvc->log, WLOG_ERROR,
"Stream_EnsureRemainingCapacity failed!");
1619 Stream_Release(drdynvc->data_in);
1620 drdynvc->data_in =
nullptr;
1621 return ERROR_INTERNAL_ERROR;
1624 Stream_Write(data_in, pData, dataLength);
1626 if (dataFlags & CHANNEL_FLAG_LAST)
1628 const size_t cap = Stream_Capacity(data_in);
1629 const size_t pos = Stream_GetPosition(data_in);
1632 WLog_Print(drdynvc->log, WLOG_ERROR,
"drdynvc_plugin_process_received: read error");
1633 return ERROR_INVALID_DATA;
1636 drdynvc->data_in =
nullptr;
1637 Stream_SealLength(data_in);
1638 Stream_ResetPosition(data_in);
1642 if (!MessageQueue_Post(drdynvc->queue,
nullptr, 0, (
void*)data_in,
nullptr))
1644 WLog_Print(drdynvc->log, WLOG_ERROR,
"MessageQueue_Post failed!");
1645 return ERROR_INTERNAL_ERROR;
1650 UINT error = drdynvc_order_recv(drdynvc, data_in, TRUE);
1651 Stream_Release(data_in);
1655 WLog_Print(drdynvc->log, WLOG_WARN,
1656 "drdynvc_order_recv failed with error %" PRIu32
"!", error);
1662 return CHANNEL_RC_OK;
1665static void VCAPITYPE drdynvc_virtual_channel_open_event_ex(LPVOID lpUserParam, DWORD openHandle,
1666 UINT event, LPVOID pData,
1667 UINT32 dataLength, UINT32 totalLength,
1670 UINT error = CHANNEL_RC_OK;
1671 drdynvcPlugin* drdynvc = (drdynvcPlugin*)lpUserParam;
1673 WINPR_ASSERT(drdynvc);
1676 case CHANNEL_EVENT_DATA_RECEIVED:
1677 if (!drdynvc || (drdynvc->OpenHandle != openHandle))
1679 WLog_ERR(TAG,
"drdynvc_virtual_channel_open_event: error no match");
1682 if ((error = drdynvc_virtual_channel_event_data_received(drdynvc, pData, dataLength,
1683 totalLength, dataFlags)))
1684 WLog_Print(drdynvc->log, WLOG_ERROR,
1685 "drdynvc_virtual_channel_event_data_received failed with error %" PRIu32
1691 case CHANNEL_EVENT_WRITE_CANCELLED:
1692 case CHANNEL_EVENT_WRITE_COMPLETE:
1699 case CHANNEL_EVENT_USER:
1705 if (error && drdynvc && drdynvc->rdpcontext)
1706 setChannelError(drdynvc->rdpcontext, error,
1707 "drdynvc_virtual_channel_open_event reported an error");
1710static DWORD WINAPI drdynvc_virtual_channel_client_thread(LPVOID arg)
1714 wMessage message = WINPR_C_ARRAY_INIT;
1715 UINT error = CHANNEL_RC_OK;
1716 drdynvcPlugin* drdynvc = (drdynvcPlugin*)arg;
1720 ExitThread((DWORD)CHANNEL_RC_BAD_CHANNEL_HANDLE);
1721 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1726 if (!MessageQueue_Wait(drdynvc->queue))
1728 WLog_Print(drdynvc->log, WLOG_ERROR,
"MessageQueue_Wait failed!");
1729 error = ERROR_INTERNAL_ERROR;
1733 if (!MessageQueue_Peek(drdynvc->queue, &message, TRUE))
1735 WLog_Print(drdynvc->log, WLOG_ERROR,
"MessageQueue_Peek failed!");
1736 error = ERROR_INTERNAL_ERROR;
1740 if (message.id == WMQ_QUIT)
1743 if (message.id == 0)
1745 UINT32 ThreadingFlags = TRUE;
1746 data = (
wStream*)message.wParam;
1748 if ((error = drdynvc_order_recv(drdynvc, data, ThreadingFlags)))
1750 WLog_Print(drdynvc->log, WLOG_WARN,
1751 "drdynvc_order_recv failed with error %" PRIu32
"!", error);
1754 Stream_Release(data);
1764 HashTable_Clear(drdynvcMgr->channelsById);
1767 if (error && drdynvc->rdpcontext)
1768 setChannelError(drdynvc->rdpcontext, error,
1769 "drdynvc_virtual_channel_client_thread reported an error");
1771 ExitThread((DWORD)error);
1775static void drdynvc_queue_object_free(
void* obj)
1778 wMessage* msg = (wMessage*)obj;
1780 if (!msg || (msg->id != 0))
1789static UINT drdynvc_virtual_channel_event_initialized(drdynvcPlugin* drdynvc, LPVOID pData,
1793 WINPR_UNUSED(pData);
1794 WINPR_UNUSED(dataLength);
1799 drdynvc->queue = MessageQueue_New(
nullptr);
1801 if (!drdynvc->queue)
1803 WLog_Print(drdynvc->log, WLOG_ERROR,
"MessageQueue_New failed!");
1807 obj = MessageQueue_Object(drdynvc->queue);
1809 drdynvc->channel_mgr = dvcman_new(drdynvc);
1811 if (!drdynvc->channel_mgr)
1813 WLog_Print(drdynvc->log, WLOG_ERROR,
"dvcman_new failed!");
1817 return CHANNEL_RC_OK;
1819 return ERROR_INTERNAL_ERROR;
1827static UINT drdynvc_virtual_channel_event_connected(drdynvcPlugin* drdynvc, LPVOID pData,
1832 rdpSettings* settings =
nullptr;
1834 WINPR_ASSERT(drdynvc);
1835 WINPR_UNUSED(pData);
1836 WINPR_UNUSED(dataLength);
1839 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1841 WINPR_ASSERT(drdynvc->channelEntryPoints.pVirtualChannelOpenEx);
1842 status = drdynvc->channelEntryPoints.pVirtualChannelOpenEx(
1843 drdynvc->InitHandle, &drdynvc->OpenHandle, drdynvc->channelDef.name,
1844 drdynvc_virtual_channel_open_event_ex);
1846 if (status != CHANNEL_RC_OK)
1848 WLog_Print(drdynvc->log, WLOG_ERROR,
"pVirtualChannelOpen failed with %s [%08" PRIX32
"]",
1849 WTSErrorToString(status), status);
1853 WINPR_ASSERT(drdynvc->rdpcontext);
1854 settings = drdynvc->rdpcontext->settings;
1855 WINPR_ASSERT(settings);
1857 for (UINT32 index = 0;
1861 freerdp_settings_get_pointer_array(settings, FreeRDP_DynamicChannelArray, index);
1862 error = dvcman_load_addin(drdynvc, drdynvc->channel_mgr, args, drdynvc->rdpcontext);
1864 if (CHANNEL_RC_OK != error)
1868 if ((error = dvcman_init(drdynvc, drdynvc->channel_mgr)))
1870 WLog_Print(drdynvc->log, WLOG_ERROR,
"dvcman_init failed with error %" PRIu32
"!", error);
1874 drdynvc->state = DRDYNVC_STATE_CAPABILITIES;
1878 if (!(drdynvc->thread = CreateThread(
nullptr, 0, drdynvc_virtual_channel_client_thread,
1879 (
void*)drdynvc, 0,
nullptr)))
1881 error = ERROR_INTERNAL_ERROR;
1882 WLog_Print(drdynvc->log, WLOG_ERROR,
"CreateThread failed!");
1886 if (!SetThreadPriority(drdynvc->thread, THREAD_PRIORITY_HIGHEST))
1887 WLog_Print(drdynvc->log, WLOG_WARN,
"SetThreadPriority failed, ignoring.");
1899static UINT drdynvc_virtual_channel_event_disconnected(drdynvcPlugin* drdynvc)
1904 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1906 if (drdynvc->OpenHandle == 0)
1907 return CHANNEL_RC_OK;
1911 if (!MessageQueue_PostQuit(drdynvc->queue, 0))
1913 status = GetLastError();
1914 WLog_Print(drdynvc->log, WLOG_ERROR,
1915 "MessageQueue_PostQuit failed with error %" PRIu32
"", status);
1920 if (drdynvc->thread)
1922 if (WaitForSingleObject(drdynvc->thread, INFINITE) != WAIT_OBJECT_0)
1924 status = GetLastError();
1925 WLog_Print(drdynvc->log, WLOG_ERROR,
1926 "WaitForSingleObject failed with error %" PRIu32
"", status);
1930 (void)CloseHandle(drdynvc->thread);
1931 drdynvc->thread =
nullptr;
1941 HashTable_Clear(drdynvcMgr->channelsById);
1945 WINPR_ASSERT(drdynvc->channelEntryPoints.pVirtualChannelCloseEx);
1946 status = drdynvc->channelEntryPoints.pVirtualChannelCloseEx(drdynvc->InitHandle,
1947 drdynvc->OpenHandle);
1949 if (status != CHANNEL_RC_OK)
1951 WLog_Print(drdynvc->log, WLOG_ERROR,
"pVirtualChannelClose failed with %s [%08" PRIX32
"]",
1952 WTSErrorToString(status), status);
1955 dvcman_clear(drdynvc, drdynvc->channel_mgr);
1957 MessageQueue_Clear(drdynvc->queue);
1958 drdynvc->OpenHandle = 0;
1960 if (drdynvc->data_in)
1962 Stream_Release(drdynvc->data_in);
1963 drdynvc->data_in =
nullptr;
1974static UINT drdynvc_virtual_channel_event_terminated(drdynvcPlugin* drdynvc)
1977 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1979 MessageQueue_Free(drdynvc->queue);
1980 drdynvc->queue =
nullptr;
1982 if (drdynvc->channel_mgr)
1984 dvcman_free(drdynvc, drdynvc->channel_mgr);
1985 drdynvc->channel_mgr =
nullptr;
1987 drdynvc->InitHandle =
nullptr;
1988 free(drdynvc->context);
1990 return CHANNEL_RC_OK;
1993static UINT drdynvc_virtual_channel_event_attached(drdynvcPlugin* drdynvc)
1995 UINT error = CHANNEL_RC_OK;
1996 DVCMAN* dvcman =
nullptr;
1999 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
2001 dvcman = (
DVCMAN*)drdynvc->channel_mgr;
2004 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
2006 ArrayList_Lock(dvcman->plugins);
2007 for (
size_t i = 0; i < ArrayList_Count(dvcman->plugins); i++)
2009 IWTSPlugin* pPlugin = ArrayList_GetItem(dvcman->plugins, i);
2011 error = IFCALLRESULT(CHANNEL_RC_OK, pPlugin->Attached, pPlugin);
2012 if (error != CHANNEL_RC_OK)
2014 WLog_Print(drdynvc->log, WLOG_ERROR,
"Attach failed with error %" PRIu32
"!", error);
2020 ArrayList_Unlock(dvcman->plugins);
2024static UINT drdynvc_virtual_channel_event_detached(drdynvcPlugin* drdynvc)
2026 UINT error = CHANNEL_RC_OK;
2027 DVCMAN* dvcman =
nullptr;
2030 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
2032 dvcman = (
DVCMAN*)drdynvc->channel_mgr;
2035 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
2037 ArrayList_Lock(dvcman->plugins);
2038 for (
size_t i = 0; i < ArrayList_Count(dvcman->plugins); i++)
2040 IWTSPlugin* pPlugin = ArrayList_GetItem(dvcman->plugins, i);
2042 error = IFCALLRESULT(CHANNEL_RC_OK, pPlugin->Detached, pPlugin);
2043 if (error != CHANNEL_RC_OK)
2045 WLog_Print(drdynvc->log, WLOG_ERROR,
"Detach failed with error %" PRIu32
"!", error);
2051 ArrayList_Unlock(dvcman->plugins);
2056static VOID VCAPITYPE drdynvc_virtual_channel_init_event_ex(LPVOID lpUserParam, LPVOID pInitHandle,
2057 UINT event, LPVOID pData,
2060 UINT error = CHANNEL_RC_OK;
2061 drdynvcPlugin* drdynvc = (drdynvcPlugin*)lpUserParam;
2063 if (!drdynvc || (drdynvc->InitHandle != pInitHandle))
2065 WLog_ERR(TAG,
"drdynvc_virtual_channel_init_event: error no match");
2071 case CHANNEL_EVENT_INITIALIZED:
2072 error = drdynvc_virtual_channel_event_initialized(drdynvc, pData, dataLength);
2074 case CHANNEL_EVENT_CONNECTED:
2075 if ((error = drdynvc_virtual_channel_event_connected(drdynvc, pData, dataLength)))
2076 WLog_Print(drdynvc->log, WLOG_ERROR,
2077 "drdynvc_virtual_channel_event_connected failed with error %" PRIu32
"",
2082 case CHANNEL_EVENT_DISCONNECTED:
2083 if ((error = drdynvc_virtual_channel_event_disconnected(drdynvc)))
2084 WLog_Print(drdynvc->log, WLOG_ERROR,
2085 "drdynvc_virtual_channel_event_disconnected failed with error %" PRIu32
2091 case CHANNEL_EVENT_TERMINATED:
2092 if ((error = drdynvc_virtual_channel_event_terminated(drdynvc)))
2093 WLog_Print(drdynvc->log, WLOG_ERROR,
2094 "drdynvc_virtual_channel_event_terminated failed with error %" PRIu32
"",
2099 case CHANNEL_EVENT_ATTACHED:
2100 if ((error = drdynvc_virtual_channel_event_attached(drdynvc)))
2101 WLog_Print(drdynvc->log, WLOG_ERROR,
2102 "drdynvc_virtual_channel_event_attached failed with error %" PRIu32
"",
2107 case CHANNEL_EVENT_DETACHED:
2108 if ((error = drdynvc_virtual_channel_event_detached(drdynvc)))
2109 WLog_Print(drdynvc->log, WLOG_ERROR,
2110 "drdynvc_virtual_channel_event_detached failed with error %" PRIu32
"",
2119 if (error && drdynvc->rdpcontext)
2120 setChannelError(drdynvc->rdpcontext, error,
2121 "drdynvc_virtual_channel_init_event_ex reported an error");
2128static int drdynvc_get_version(DrdynvcClientContext* context)
2130 WINPR_ASSERT(context);
2131 drdynvcPlugin* drdynvc = (drdynvcPlugin*)context->handle;
2132 WINPR_ASSERT(drdynvc);
2133 return drdynvc->version;
2137#define VirtualChannelEntryEx drdynvc_VirtualChannelEntryEx
2139FREERDP_ENTRY_POINT(BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS_EX pEntryPoints,
2143 drdynvcPlugin* drdynvc =
nullptr;
2144 DrdynvcClientContext* context =
nullptr;
2146 drdynvc = (drdynvcPlugin*)calloc(1,
sizeof(drdynvcPlugin));
2148 WINPR_ASSERT(pEntryPoints);
2151 WLog_ERR(TAG,
"calloc failed!");
2155 drdynvc->channelDef.options =
2156 CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP | CHANNEL_OPTION_COMPRESS_RDP;
2157 (void)sprintf_s(drdynvc->channelDef.name, ARRAYSIZE(drdynvc->channelDef.name),
2158 DRDYNVC_SVC_CHANNEL_NAME);
2159 drdynvc->state = DRDYNVC_STATE_INITIAL;
2163 (pEntryPointsEx->MagicNumber == FREERDP_CHANNEL_MAGIC_NUMBER))
2165 context = (DrdynvcClientContext*)calloc(1,
sizeof(DrdynvcClientContext));
2169 WLog_Print(drdynvc->log, WLOG_ERROR,
"calloc failed!");
2174 context->handle = (
void*)drdynvc;
2175 context->custom =
nullptr;
2176 drdynvc->context = context;
2177 context->GetVersion = drdynvc_get_version;
2178 drdynvc->rdpcontext = pEntryPointsEx->context;
2180 FreeRDP_TransportDumpReplay) &&
2182 FreeRDP_SynchronousDynamicChannels))
2183 drdynvc->async = TRUE;
2186 drdynvc->log = WLog_Get(TAG);
2187 WLog_Print(drdynvc->log, WLOG_DEBUG,
"VirtualChannelEntryEx");
2188 CopyMemory(&(drdynvc->channelEntryPoints), pEntryPoints,
2190 drdynvc->InitHandle = pInitHandle;
2192 WINPR_ASSERT(drdynvc->channelEntryPoints.pVirtualChannelInitEx);
2193 rc = drdynvc->channelEntryPoints.pVirtualChannelInitEx(
2194 drdynvc, context, pInitHandle, &drdynvc->channelDef, 1, VIRTUAL_CHANNEL_VERSION_WIN2000,
2195 drdynvc_virtual_channel_init_event_ex);
2197 if (CHANNEL_RC_OK != rc)
2199 WLog_Print(drdynvc->log, WLOG_ERROR,
"pVirtualChannelInit failed with %s [%08" PRIX32
"]",
2200 WTSErrorToString(rc), rc);
2201 free(drdynvc->context);
2206 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.
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