summaryrefslogtreecommitdiffstats
path: root/channels/client/generic_dynvc.c
diff options
context:
space:
mode:
Diffstat (limited to 'channels/client/generic_dynvc.c')
-rw-r--r--channels/client/generic_dynvc.c212
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;
+}