Coverage Report

Created: 2025-07-01 06:46

/src/FreeRDP/channels/client/generic_dynvc.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * Dynamic channel
4
 *
5
 * Copyright 2022 David Fort <contact@hardening-consulting.com>
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
20
#include <freerdp/config.h>
21
#include <freerdp/log.h>
22
#include <freerdp/client/channels.h>
23
24
#define TAG FREERDP_TAG("genericdynvc")
25
26
static UINT generic_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
27
                                              IWTSVirtualChannel* pChannel,
28
                                              WINPR_ATTR_UNUSED BYTE* Data,
29
                                              WINPR_ATTR_UNUSED BOOL* pbAccept,
30
                                              IWTSVirtualChannelCallback** ppCallback)
31
0
{
32
0
  GENERIC_CHANNEL_CALLBACK* callback = NULL;
33
0
  GENERIC_DYNVC_PLUGIN* plugin = NULL;
34
0
  GENERIC_LISTENER_CALLBACK* listener_callback = (GENERIC_LISTENER_CALLBACK*)pListenerCallback;
35
36
0
  if (!listener_callback || !listener_callback->plugin)
37
0
    return ERROR_INTERNAL_ERROR;
38
39
0
  plugin = (GENERIC_DYNVC_PLUGIN*)listener_callback->plugin;
40
0
  WLog_Print(plugin->log, WLOG_TRACE, "...");
41
42
0
  callback = (GENERIC_CHANNEL_CALLBACK*)calloc(1, plugin->channelCallbackSize);
43
0
  if (!callback)
44
0
  {
45
0
    WLog_Print(plugin->log, WLOG_ERROR, "calloc failed!");
46
0
    return CHANNEL_RC_NO_MEMORY;
47
0
  }
48
49
  /* implant configured channel callbacks */
50
0
  callback->iface = *plugin->channel_callbacks;
51
52
0
  callback->plugin = listener_callback->plugin;
53
0
  callback->channel_mgr = listener_callback->channel_mgr;
54
0
  callback->channel = pChannel;
55
56
0
  listener_callback->channel_callback = callback;
57
0
  listener_callback->channel = pChannel;
58
59
0
  *ppCallback = (IWTSVirtualChannelCallback*)callback;
60
0
  return CHANNEL_RC_OK;
61
0
}
62
63
static UINT generic_dynvc_plugin_initialize(IWTSPlugin* pPlugin,
64
                                            IWTSVirtualChannelManager* pChannelMgr)
65
0
{
66
0
  UINT rc = 0;
67
0
  GENERIC_LISTENER_CALLBACK* listener_callback = NULL;
68
0
  GENERIC_DYNVC_PLUGIN* plugin = (GENERIC_DYNVC_PLUGIN*)pPlugin;
69
70
0
  if (!plugin)
71
0
    return CHANNEL_RC_BAD_CHANNEL_HANDLE;
72
73
0
  if (!pChannelMgr)
74
0
    return ERROR_INVALID_PARAMETER;
75
76
0
  if (plugin->initialized)
77
0
  {
78
0
    WLog_ERR(TAG, "[%s] channel initialized twice, aborting", plugin->dynvc_name);
79
0
    return ERROR_INVALID_DATA;
80
0
  }
81
82
0
  WLog_Print(plugin->log, WLOG_TRACE, "...");
83
0
  listener_callback = (GENERIC_LISTENER_CALLBACK*)calloc(1, sizeof(GENERIC_LISTENER_CALLBACK));
84
0
  if (!listener_callback)
85
0
  {
86
0
    WLog_Print(plugin->log, WLOG_ERROR, "calloc failed!");
87
0
    return CHANNEL_RC_NO_MEMORY;
88
0
  }
89
90
0
  plugin->listener_callback = listener_callback;
91
0
  listener_callback->iface.OnNewChannelConnection = generic_on_new_channel_connection;
92
0
  listener_callback->plugin = pPlugin;
93
0
  listener_callback->channel_mgr = pChannelMgr;
94
0
  rc = pChannelMgr->CreateListener(pChannelMgr, plugin->dynvc_name, 0, &listener_callback->iface,
95
0
                                   &plugin->listener);
96
97
0
  plugin->listener->pInterface = plugin->iface.pInterface;
98
0
  plugin->initialized = (rc == CHANNEL_RC_OK);
99
0
  return rc;
100
0
}
101
102
static UINT generic_plugin_terminated(IWTSPlugin* pPlugin)
103
0
{
104
0
  GENERIC_DYNVC_PLUGIN* plugin = (GENERIC_DYNVC_PLUGIN*)pPlugin;
105
0
  UINT error = CHANNEL_RC_OK;
106
107
0
  if (!plugin)
108
0
    return CHANNEL_RC_BAD_CHANNEL_HANDLE;
109
110
0
  WLog_Print(plugin->log, WLOG_TRACE, "...");
111
112
  /* some channels (namely rdpei), look at initialized to see if they should continue to run */
113
0
  plugin->initialized = FALSE;
114
115
0
  if (plugin->terminatePluginFn)
116
0
    plugin->terminatePluginFn(plugin);
117
118
0
  if (plugin->listener_callback)
119
0
  {
120
0
    IWTSVirtualChannelManager* mgr = plugin->listener_callback->channel_mgr;
121
0
    if (mgr)
122
0
      IFCALL(mgr->DestroyListener, mgr, plugin->listener);
123
0
  }
124
125
0
  free(plugin->listener_callback);
126
0
  free(plugin->dynvc_name);
127
0
  free(plugin);
128
0
  return error;
129
0
}
130
131
static UINT generic_dynvc_plugin_attached(IWTSPlugin* pPlugin)
132
0
{
133
0
  GENERIC_DYNVC_PLUGIN* pluginn = (GENERIC_DYNVC_PLUGIN*)pPlugin;
134
0
  UINT error = CHANNEL_RC_OK;
135
136
0
  if (!pluginn)
137
0
    return CHANNEL_RC_BAD_CHANNEL_HANDLE;
138
139
0
  pluginn->attached = TRUE;
140
0
  return error;
141
0
}
142
143
static UINT generic_dynvc_plugin_detached(IWTSPlugin* pPlugin)
144
0
{
145
0
  GENERIC_DYNVC_PLUGIN* plugin = (GENERIC_DYNVC_PLUGIN*)pPlugin;
146
0
  UINT error = CHANNEL_RC_OK;
147
148
0
  if (!plugin)
149
0
    return CHANNEL_RC_BAD_CHANNEL_HANDLE;
150
151
0
  plugin->attached = FALSE;
152
0
  return error;
153
0
}
154
155
UINT freerdp_generic_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints, const char* logTag,
156
                                    const char* name, size_t pluginSize, size_t channelCallbackSize,
157
                                    const IWTSVirtualChannelCallback* channel_callbacks,
158
                                    DYNVC_PLUGIN_INIT_FN initPluginFn,
159
                                    DYNVC_PLUGIN_TERMINATE_FN terminatePluginFn)
160
0
{
161
0
  GENERIC_DYNVC_PLUGIN* plugin = NULL;
162
0
  UINT error = CHANNEL_RC_INITIALIZATION_ERROR;
163
164
0
  WINPR_ASSERT(pEntryPoints);
165
0
  WINPR_ASSERT(pEntryPoints->GetPlugin);
166
0
  WINPR_ASSERT(logTag);
167
0
  WINPR_ASSERT(name);
168
0
  WINPR_ASSERT(pluginSize >= sizeof(*plugin));
169
0
  WINPR_ASSERT(channelCallbackSize >= sizeof(GENERIC_CHANNEL_CALLBACK));
170
171
0
  plugin = (GENERIC_DYNVC_PLUGIN*)pEntryPoints->GetPlugin(pEntryPoints, name);
172
0
  if (plugin != NULL)
173
0
    return CHANNEL_RC_ALREADY_INITIALIZED;
174
175
0
  plugin = (GENERIC_DYNVC_PLUGIN*)calloc(1, pluginSize);
176
0
  if (!plugin)
177
0
  {
178
0
    WLog_ERR(TAG, "calloc failed!");
179
0
    return CHANNEL_RC_NO_MEMORY;
180
0
  }
181
182
0
  plugin->log = WLog_Get(logTag);
183
0
  plugin->attached = TRUE;
184
0
  plugin->channel_callbacks = channel_callbacks;
185
0
  plugin->channelCallbackSize = channelCallbackSize;
186
0
  plugin->iface.Initialize = generic_dynvc_plugin_initialize;
187
0
  plugin->iface.Connected = NULL;
188
0
  plugin->iface.Disconnected = NULL;
189
0
  plugin->iface.Terminated = generic_plugin_terminated;
190
0
  plugin->iface.Attached = generic_dynvc_plugin_attached;
191
0
  plugin->iface.Detached = generic_dynvc_plugin_detached;
192
0
  plugin->terminatePluginFn = terminatePluginFn;
193
194
0
  if (initPluginFn)
195
0
  {
196
0
    rdpSettings* settings = pEntryPoints->GetRdpSettings(pEntryPoints);
197
0
    rdpContext* context = pEntryPoints->GetRdpContext(pEntryPoints);
198
199
0
    error = initPluginFn(plugin, context, settings);
200
0
    if (error != CHANNEL_RC_OK)
201
0
      goto error;
202
0
  }
203
204
0
  plugin->dynvc_name = _strdup(name);
205
0
  if (!plugin->dynvc_name)
206
0
    goto error;
207
208
0
  error = pEntryPoints->RegisterPlugin(pEntryPoints, name, &plugin->iface);
209
0
  if (error == CHANNEL_RC_OK)
210
0
    return error;
211
212
0
error:
213
0
  generic_plugin_terminated(&plugin->iface);
214
0
  return error;
215
0
}