Coverage Report

Created: 2026-10-15 06:27

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/channels/client/generic_dynvc.c
Line
Count
Source
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 BOOL generic_update_connected_state(GENERIC_DYNVC_PLUGIN* plugin, BOOL val)
27
5.31k
{
28
5.31k
  if (!plugin || !plugin->dynvc_name)
29
0
    return FALSE;
30
5.31k
  const size_t len = strlen(plugin->dynvc_name);
31
5.31k
  plugin->dynvc_name[len + 1] = (char)val;
32
5.31k
  return TRUE;
33
5.31k
}
34
35
WINPR_ATTR_NODISCARD
36
static BOOL generic_get_connected_state(GENERIC_DYNVC_PLUGIN* plugin)
37
2.65k
{
38
2.65k
  if (!plugin || !plugin->dynvc_name)
39
0
    return FALSE;
40
2.65k
  const size_t len = strlen(plugin->dynvc_name);
41
2.65k
  return plugin->dynvc_name[len + 1];
42
2.65k
}
43
44
static UINT generic_on_close(IWTSVirtualChannelCallback* pChannelCallback)
45
2.65k
{
46
2.65k
  GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
47
2.65k
  WINPR_ASSERT(callback);
48
49
2.65k
  GENERIC_DYNVC_PLUGIN* plugin = (GENERIC_DYNVC_PLUGIN*)callback->plugin;
50
2.65k
  WINPR_ASSERT(plugin);
51
52
  /* We're wrapping the originally set callback to update the channel state.
53
   * call the originally intended OnClose from the plugin
54
   */
55
2.65k
  WINPR_ASSERT(plugin->channel_callbacks);
56
2.65k
  const UINT rc =
57
2.65k
      IFCALLRESULT(CHANNEL_RC_OK, plugin->channel_callbacks->OnClose, pChannelCallback);
58
2.65k
  generic_update_connected_state(plugin, FALSE);
59
60
2.65k
  return rc;
61
2.65k
}
62
63
static UINT generic_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
64
                                              IWTSVirtualChannel* pChannel,
65
                                              WINPR_ATTR_UNUSED BYTE* Data,
66
                                              WINPR_ATTR_UNUSED BOOL* pbAccept,
67
                                              IWTSVirtualChannelCallback** ppCallback)
68
2.65k
{
69
2.65k
  GENERIC_LISTENER_CALLBACK* listener_callback = (GENERIC_LISTENER_CALLBACK*)pListenerCallback;
70
71
2.65k
  if (!listener_callback || !listener_callback->plugin)
72
0
    return ERROR_INTERNAL_ERROR;
73
74
2.65k
  GENERIC_DYNVC_PLUGIN* plugin = (GENERIC_DYNVC_PLUGIN*)listener_callback->plugin;
75
2.65k
  WLog_Print(plugin->log, WLOG_TRACE, "...");
76
77
2.65k
  if (generic_get_connected_state(plugin))
78
0
  {
79
0
    WLog_Print(plugin->log, WLOG_ERROR, "Channel already connected, terminating.");
80
0
    return ERROR_DEVICE_ALREADY_ATTACHED;
81
0
  }
82
83
2.65k
  GENERIC_CHANNEL_CALLBACK* callback =
84
2.65k
      (GENERIC_CHANNEL_CALLBACK*)calloc(1, plugin->channelCallbackSize);
85
2.65k
  if (!callback)
86
0
  {
87
0
    WLog_Print(plugin->log, WLOG_ERROR, "calloc failed!");
88
0
    return CHANNEL_RC_NO_MEMORY;
89
0
  }
90
91
  /* implant configured channel callbacks */
92
2.65k
  callback->iface = *plugin->channel_callbacks;
93
2.65k
  callback->iface.OnClose = generic_on_close;
94
95
2.65k
  callback->plugin = listener_callback->plugin;
96
2.65k
  callback->channel_mgr = listener_callback->channel_mgr;
97
2.65k
  callback->channel = pChannel;
98
99
2.65k
  listener_callback->channel_callback = callback;
100
2.65k
  listener_callback->channel = pChannel;
101
102
2.65k
  *ppCallback = &callback->iface;
103
2.65k
  generic_update_connected_state(plugin, TRUE);
104
2.65k
  return CHANNEL_RC_OK;
105
2.65k
}
106
107
static UINT generic_dynvc_plugin_initialize(IWTSPlugin* pPlugin,
108
                                            IWTSVirtualChannelManager* pChannelMgr)
109
2.65k
{
110
2.65k
  UINT rc = 0;
111
2.65k
  GENERIC_DYNVC_PLUGIN* plugin = (GENERIC_DYNVC_PLUGIN*)pPlugin;
112
113
2.65k
  if (!plugin)
114
0
    return CHANNEL_RC_BAD_CHANNEL_HANDLE;
115
116
2.65k
  if (!pChannelMgr)
117
0
    return ERROR_INVALID_PARAMETER;
118
119
2.65k
  if (plugin->initialized)
120
0
  {
121
0
    WLog_ERR(TAG, "[%s] channel initialized twice, aborting", plugin->dynvc_name);
122
0
    return ERROR_INVALID_DATA;
123
0
  }
124
125
2.65k
  WLog_Print(plugin->log, WLOG_TRACE, "...");
126
2.65k
  GENERIC_LISTENER_CALLBACK* listener_callback =
127
2.65k
      (GENERIC_LISTENER_CALLBACK*)calloc(1, sizeof(GENERIC_LISTENER_CALLBACK));
128
2.65k
  if (!listener_callback)
129
0
  {
130
0
    WLog_Print(plugin->log, WLOG_ERROR, "calloc failed!");
131
0
    return CHANNEL_RC_NO_MEMORY;
132
0
  }
133
134
2.65k
  plugin->listener_callback = listener_callback;
135
2.65k
  listener_callback->iface.OnNewChannelConnection = generic_on_new_channel_connection;
136
2.65k
  listener_callback->plugin = pPlugin;
137
2.65k
  listener_callback->channel_mgr = pChannelMgr;
138
2.65k
  rc = pChannelMgr->CreateListener(pChannelMgr, plugin->dynvc_name, 0, &listener_callback->iface,
139
2.65k
                                   &plugin->listener);
140
141
2.65k
  plugin->listener->pInterface = plugin->iface.pInterface;
142
2.65k
  plugin->initialized = (rc == CHANNEL_RC_OK);
143
2.65k
  return rc;
144
2.65k
}
145
146
static UINT generic_plugin_terminated(IWTSPlugin* pPlugin)
147
2.65k
{
148
2.65k
  GENERIC_DYNVC_PLUGIN* plugin = (GENERIC_DYNVC_PLUGIN*)pPlugin;
149
2.65k
  UINT error = CHANNEL_RC_OK;
150
151
2.65k
  if (!plugin)
152
0
    return CHANNEL_RC_BAD_CHANNEL_HANDLE;
153
154
2.65k
  WLog_Print(plugin->log, WLOG_TRACE, "...");
155
156
  /* some channels (namely rdpei), look at initialized to see if they should continue to run */
157
2.65k
  plugin->initialized = FALSE;
158
159
2.65k
  if (plugin->terminatePluginFn)
160
2.65k
    plugin->terminatePluginFn(plugin);
161
162
2.65k
  if (plugin->listener_callback)
163
2.65k
  {
164
2.65k
    IWTSVirtualChannelManager* mgr = plugin->listener_callback->channel_mgr;
165
2.65k
    if (mgr)
166
2.65k
      IFCALL(mgr->DestroyListener, mgr, plugin->listener);
167
2.65k
  }
168
169
2.65k
  free(plugin->listener_callback);
170
2.65k
  free(plugin->dynvc_name);
171
2.65k
  free(plugin);
172
2.65k
  return error;
173
2.65k
}
174
175
static UINT generic_dynvc_plugin_attached(IWTSPlugin* pPlugin)
176
0
{
177
0
  GENERIC_DYNVC_PLUGIN* pluginn = (GENERIC_DYNVC_PLUGIN*)pPlugin;
178
0
  UINT error = CHANNEL_RC_OK;
179
180
0
  if (!pluginn)
181
0
    return CHANNEL_RC_BAD_CHANNEL_HANDLE;
182
183
0
  pluginn->attached = TRUE;
184
0
  return error;
185
0
}
186
187
static UINT generic_dynvc_plugin_detached(IWTSPlugin* pPlugin)
188
0
{
189
0
  GENERIC_DYNVC_PLUGIN* plugin = (GENERIC_DYNVC_PLUGIN*)pPlugin;
190
0
  UINT error = CHANNEL_RC_OK;
191
192
0
  if (!plugin)
193
0
    return CHANNEL_RC_BAD_CHANNEL_HANDLE;
194
195
0
  plugin->attached = FALSE;
196
0
  return error;
197
0
}
198
199
UINT freerdp_generic_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints, const char* logTag,
200
                                    const char* name, size_t pluginSize, size_t channelCallbackSize,
201
                                    const IWTSVirtualChannelCallback* channel_callbacks,
202
                                    DYNVC_PLUGIN_INIT_FN initPluginFn,
203
                                    DYNVC_PLUGIN_TERMINATE_FN terminatePluginFn)
204
2.65k
{
205
2.65k
  GENERIC_DYNVC_PLUGIN* plugin = nullptr;
206
2.65k
  UINT error = CHANNEL_RC_INITIALIZATION_ERROR;
207
208
2.65k
  WINPR_ASSERT(pEntryPoints);
209
2.65k
  WINPR_ASSERT(pEntryPoints->GetPlugin);
210
2.65k
  WINPR_ASSERT(logTag);
211
2.65k
  WINPR_ASSERT(name);
212
2.65k
  WINPR_ASSERT(pluginSize >= sizeof(*plugin));
213
2.65k
  WINPR_ASSERT(channelCallbackSize >= sizeof(GENERIC_CHANNEL_CALLBACK));
214
215
2.65k
  plugin = (GENERIC_DYNVC_PLUGIN*)pEntryPoints->GetPlugin(pEntryPoints, name);
216
2.65k
  if (plugin != nullptr)
217
0
    return CHANNEL_RC_ALREADY_INITIALIZED;
218
219
2.65k
  plugin = (GENERIC_DYNVC_PLUGIN*)calloc(1, pluginSize);
220
2.65k
  if (!plugin)
221
0
  {
222
0
    WLog_ERR(TAG, "calloc failed!");
223
0
    return CHANNEL_RC_NO_MEMORY;
224
0
  }
225
226
2.65k
  plugin->log = WLog_Get(logTag);
227
2.65k
  plugin->attached = TRUE;
228
2.65k
  plugin->channel_callbacks = channel_callbacks;
229
2.65k
  plugin->channelCallbackSize = channelCallbackSize;
230
2.65k
  plugin->iface.Initialize = generic_dynvc_plugin_initialize;
231
2.65k
  plugin->iface.Connected = nullptr;
232
2.65k
  plugin->iface.Disconnected = nullptr;
233
2.65k
  plugin->iface.Terminated = generic_plugin_terminated;
234
2.65k
  plugin->iface.Attached = generic_dynvc_plugin_attached;
235
2.65k
  plugin->iface.Detached = generic_dynvc_plugin_detached;
236
2.65k
  plugin->terminatePluginFn = terminatePluginFn;
237
238
2.65k
  if (initPluginFn)
239
2.65k
  {
240
2.65k
    rdpSettings* settings = pEntryPoints->GetRdpSettings(pEntryPoints);
241
2.65k
    rdpContext* context = pEntryPoints->GetRdpContext(pEntryPoints);
242
243
2.65k
    error = initPluginFn(plugin, context, settings);
244
2.65k
    if (error != CHANNEL_RC_OK)
245
0
      goto error;
246
2.65k
  }
247
248
  /* Since we do not have a private struct append the connected state after the name of the
249
   * channel */
250
2.65k
  const size_t len = strlen(name);
251
2.65k
  plugin->dynvc_name = calloc(len + 2, sizeof(char));
252
2.65k
  if (!plugin->dynvc_name)
253
0
    goto error;
254
2.65k
  strncpy(plugin->dynvc_name, name, len);
255
256
2.65k
  error = pEntryPoints->RegisterPlugin(pEntryPoints, name, &plugin->iface);
257
2.65k
  if (error == CHANNEL_RC_OK)
258
2.65k
    return error;
259
260
0
error:
261
0
  generic_plugin_terminated(&plugin->iface);
262
0
  return error;
263
2.65k
}