/** * FreeRDP: A Remote Desktop Protocol Implementation * Dynamic channel * * Copyright 2022 David Fort * * 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 #include #include #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; listener_callback->channel = pChannel; *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; }