diff options
Diffstat (limited to 'channels/client/generic_dynvc.c')
-rw-r--r-- | channels/client/generic_dynvc.c | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/channels/client/generic_dynvc.c b/channels/client/generic_dynvc.c new file mode 100644 index 0000000..263b5ce --- /dev/null +++ b/channels/client/generic_dynvc.c @@ -0,0 +1,212 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Dynamic channel + * + * Copyright 2022 David Fort <contact@hardening-consulting.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <freerdp/config.h> +#include <freerdp/log.h> +#include <freerdp/client/channels.h> + +#define TAG FREERDP_TAG("genericdynvc") + +static UINT generic_on_new_channel_connection(IWTSListenerCallback* pListenerCallback, + IWTSVirtualChannel* pChannel, BYTE* Data, + BOOL* pbAccept, + IWTSVirtualChannelCallback** ppCallback) +{ + GENERIC_CHANNEL_CALLBACK* callback = NULL; + GENERIC_DYNVC_PLUGIN* plugin = NULL; + GENERIC_LISTENER_CALLBACK* listener_callback = (GENERIC_LISTENER_CALLBACK*)pListenerCallback; + + if (!listener_callback || !listener_callback->plugin) + return ERROR_INTERNAL_ERROR; + + plugin = (GENERIC_DYNVC_PLUGIN*)listener_callback->plugin; + WLog_Print(plugin->log, WLOG_TRACE, "..."); + + callback = (GENERIC_CHANNEL_CALLBACK*)calloc(1, plugin->channelCallbackSize); + if (!callback) + { + WLog_Print(plugin->log, WLOG_ERROR, "calloc failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + /* implant configured channel callbacks */ + callback->iface = *plugin->channel_callbacks; + + callback->plugin = listener_callback->plugin; + callback->channel_mgr = listener_callback->channel_mgr; + callback->channel = pChannel; + + listener_callback->channel_callback = callback; + *ppCallback = (IWTSVirtualChannelCallback*)callback; + return CHANNEL_RC_OK; +} + +static UINT generic_dynvc_plugin_initialize(IWTSPlugin* pPlugin, + IWTSVirtualChannelManager* pChannelMgr) +{ + UINT rc = 0; + GENERIC_LISTENER_CALLBACK* listener_callback = NULL; + GENERIC_DYNVC_PLUGIN* plugin = (GENERIC_DYNVC_PLUGIN*)pPlugin; + + if (!plugin) + return CHANNEL_RC_BAD_CHANNEL_HANDLE; + + if (!pChannelMgr) + return ERROR_INVALID_PARAMETER; + + if (plugin->initialized) + { + WLog_ERR(TAG, "[%s] channel initialized twice, aborting", plugin->dynvc_name); + return ERROR_INVALID_DATA; + } + + WLog_Print(plugin->log, WLOG_TRACE, "..."); + listener_callback = (GENERIC_LISTENER_CALLBACK*)calloc(1, sizeof(GENERIC_LISTENER_CALLBACK)); + if (!listener_callback) + { + WLog_Print(plugin->log, WLOG_ERROR, "calloc failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + plugin->listener_callback = listener_callback; + listener_callback->iface.OnNewChannelConnection = generic_on_new_channel_connection; + listener_callback->plugin = pPlugin; + listener_callback->channel_mgr = pChannelMgr; + rc = pChannelMgr->CreateListener(pChannelMgr, plugin->dynvc_name, 0, &listener_callback->iface, + &plugin->listener); + + plugin->listener->pInterface = plugin->iface.pInterface; + plugin->initialized = (rc == CHANNEL_RC_OK); + return rc; +} + +static UINT generic_plugin_terminated(IWTSPlugin* pPlugin) +{ + GENERIC_DYNVC_PLUGIN* plugin = (GENERIC_DYNVC_PLUGIN*)pPlugin; + UINT error = CHANNEL_RC_OK; + + if (!plugin) + return CHANNEL_RC_BAD_CHANNEL_HANDLE; + + WLog_Print(plugin->log, WLOG_TRACE, "..."); + + /* some channels (namely rdpei), look at initialized to see if they should continue to run */ + plugin->initialized = FALSE; + + if (plugin->terminatePluginFn) + plugin->terminatePluginFn(plugin); + + if (plugin->listener_callback) + { + IWTSVirtualChannelManager* mgr = plugin->listener_callback->channel_mgr; + if (mgr) + IFCALL(mgr->DestroyListener, mgr, plugin->listener); + } + + free(plugin->listener_callback); + free(plugin->dynvc_name); + free(plugin); + return error; +} + +static UINT generic_dynvc_plugin_attached(IWTSPlugin* pPlugin) +{ + GENERIC_DYNVC_PLUGIN* pluginn = (GENERIC_DYNVC_PLUGIN*)pPlugin; + UINT error = CHANNEL_RC_OK; + + if (!pluginn) + return CHANNEL_RC_BAD_CHANNEL_HANDLE; + + pluginn->attached = TRUE; + return error; +} + +static UINT generic_dynvc_plugin_detached(IWTSPlugin* pPlugin) +{ + GENERIC_DYNVC_PLUGIN* plugin = (GENERIC_DYNVC_PLUGIN*)pPlugin; + UINT error = CHANNEL_RC_OK; + + if (!plugin) + return CHANNEL_RC_BAD_CHANNEL_HANDLE; + + plugin->attached = FALSE; + return error; +} + +UINT freerdp_generic_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints, const char* logTag, + const char* name, size_t pluginSize, size_t channelCallbackSize, + const IWTSVirtualChannelCallback* channel_callbacks, + DYNVC_PLUGIN_INIT_FN initPluginFn, + DYNVC_PLUGIN_TERMINATE_FN terminatePluginFn) +{ + GENERIC_DYNVC_PLUGIN* plugin = NULL; + UINT error = CHANNEL_RC_INITIALIZATION_ERROR; + + WINPR_ASSERT(pEntryPoints); + WINPR_ASSERT(pEntryPoints->GetPlugin); + WINPR_ASSERT(logTag); + WINPR_ASSERT(name); + WINPR_ASSERT(pluginSize >= sizeof(*plugin)); + WINPR_ASSERT(channelCallbackSize >= sizeof(GENERIC_CHANNEL_CALLBACK)); + + plugin = (GENERIC_DYNVC_PLUGIN*)pEntryPoints->GetPlugin(pEntryPoints, name); + if (plugin != NULL) + return CHANNEL_RC_ALREADY_INITIALIZED; + + plugin = (GENERIC_DYNVC_PLUGIN*)calloc(1, pluginSize); + if (!plugin) + { + WLog_ERR(TAG, "calloc failed!"); + return CHANNEL_RC_NO_MEMORY; + } + + plugin->log = WLog_Get(logTag); + plugin->attached = TRUE; + plugin->channel_callbacks = channel_callbacks; + plugin->channelCallbackSize = channelCallbackSize; + plugin->iface.Initialize = generic_dynvc_plugin_initialize; + plugin->iface.Connected = NULL; + plugin->iface.Disconnected = NULL; + plugin->iface.Terminated = generic_plugin_terminated; + plugin->iface.Attached = generic_dynvc_plugin_attached; + plugin->iface.Detached = generic_dynvc_plugin_detached; + plugin->terminatePluginFn = terminatePluginFn; + + if (initPluginFn) + { + rdpSettings* settings = pEntryPoints->GetRdpSettings(pEntryPoints); + rdpContext* context = pEntryPoints->GetRdpContext(pEntryPoints); + + error = initPluginFn(plugin, context, settings); + if (error != CHANNEL_RC_OK) + goto error; + } + + plugin->dynvc_name = _strdup(name); + if (!plugin->dynvc_name) + goto error; + + error = pEntryPoints->RegisterPlugin(pEntryPoints, name, &plugin->iface); + if (error == CHANNEL_RC_OK) + return error; + +error: + generic_plugin_terminated(&plugin->iface); + return error; +} |