FreeRDP
Loading...
Searching...
No Matches
generic_dynvc.c
1
20#include <freerdp/config.h>
21#include <freerdp/log.h>
22#include <freerdp/client/channels.h>
23
24#define TAG FREERDP_TAG("genericdynvc")
25
26static char magic[32] = "**GENERIC_DYNVC_PLUGIN_PRIVATE*";
27static const size_t maxPrivateOffset = 0x100000; /* 1MB should not be reached. */
28
29typedef struct
30{
31 char magic[32];
32 size_t pluginSize;
33 DWORD flags;
34 BOOL connected;
35} GENERIC_DYNVC_PLUGIN_PRIVATE;
36
37WINPR_ATTR_NODISCARD
38static GENERIC_DYNVC_PLUGIN_PRIVATE* getFromGeneric(GENERIC_DYNVC_PLUGIN* plugin)
39{
40 if (!plugin)
41 return nullptr;
42
43 /* We do not know the exact size used to allocate this struct (+ some channel custom data
44 * afterward) but we know that the private segment starts with a certain magic.
45 *
46 * So search for the magic from the end of GENERIC_DYNVC_PLUGIN until found.
47 */
48 char* ptr = (char*)(&plugin[1]);
49
50 for (size_t count = 0; count <= maxPrivateOffset; count++, ptr++)
51 {
52 /* Only bytes matching the first element of the magic pattern get the full memcmp. A
53 * byte that matches it alone is skipped like any other. */
54 if ((*ptr == magic[0]) && (memcmp(ptr, magic, sizeof(magic)) == 0))
55 return WINPR_PACKED_ALIGN_CAST(GENERIC_DYNVC_PLUGIN_PRIVATE*, ptr);
56 }
57
58 WLog_ERR(TAG,
59 "Failed to find GENERIC_DYNVC_PLUGIN_PRIVATE offset in the first %" PRIuz
60 " bytes after GENERIC_DYNVC_PLUGIN, giving up.",
61 maxPrivateOffset);
62 WINPR_ASSERT(FALSE);
63 return nullptr;
64}
65
66WINPR_ATTR_NODISCARD
67static BOOL generic_connected_allow_multiple(GENERIC_DYNVC_PLUGIN* plugin)
68{
69 GENERIC_DYNVC_PLUGIN_PRIVATE* priv = getFromGeneric(plugin);
70 if (!priv)
71 return FALSE;
72 return (priv->flags & GENERIC_CHANNEL_ENTRY_OPTION_MULTI_INSTANCE) != 0;
73}
74
75static BOOL generic_update_connected_state(GENERIC_DYNVC_PLUGIN* plugin, BOOL val)
76{
77 GENERIC_DYNVC_PLUGIN_PRIVATE* priv = getFromGeneric(plugin);
78 if (!priv)
79 return FALSE;
80 priv->connected = val;
81 return TRUE;
82}
83
84WINPR_ATTR_NODISCARD
85static BOOL generic_get_connected_state(GENERIC_DYNVC_PLUGIN* plugin)
86{
87 GENERIC_DYNVC_PLUGIN_PRIVATE* priv = getFromGeneric(plugin);
88 if (!priv)
89 return FALSE;
90 return priv->connected;
91}
92
93static UINT generic_on_close(IWTSVirtualChannelCallback* pChannelCallback)
94{
95 GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
96 WINPR_ASSERT(callback);
97
98 GENERIC_DYNVC_PLUGIN* plugin = (GENERIC_DYNVC_PLUGIN*)callback->plugin;
99 WINPR_ASSERT(plugin);
100
101 /* We're wrapping the originally set callback to update the channel state.
102 * call the originally intended OnClose from the plugin
103 */
104 WINPR_ASSERT(plugin->channel_callbacks);
105 const UINT rc =
106 IFCALLRESULT(CHANNEL_RC_OK, plugin->channel_callbacks->OnClose, pChannelCallback);
107 generic_update_connected_state(plugin, FALSE);
108
109 return rc;
110}
111
112static UINT generic_on_new_channel_connection(IWTSListenerCallback* pListenerCallback,
113 IWTSVirtualChannel* pChannel,
114 WINPR_ATTR_UNUSED BYTE* Data,
115 WINPR_ATTR_UNUSED BOOL* pbAccept,
116 IWTSVirtualChannelCallback** ppCallback)
117{
118 GENERIC_LISTENER_CALLBACK* listener_callback = (GENERIC_LISTENER_CALLBACK*)pListenerCallback;
119
120 if (!listener_callback || !listener_callback->plugin)
121 return ERROR_INTERNAL_ERROR;
122
123 GENERIC_DYNVC_PLUGIN* plugin = (GENERIC_DYNVC_PLUGIN*)listener_callback->plugin;
124 WLog_Print(plugin->log, WLOG_TRACE, "...");
125
126 if (!generic_connected_allow_multiple(plugin) && generic_get_connected_state(plugin))
127 {
128 WLog_Print(plugin->log, WLOG_ERROR, "Channel already connected, terminating.");
129 return ERROR_DEVICE_ALREADY_ATTACHED;
130 }
131
132 GENERIC_CHANNEL_CALLBACK* callback =
133 (GENERIC_CHANNEL_CALLBACK*)calloc(1, plugin->channelCallbackSize);
134 if (!callback)
135 {
136 WLog_Print(plugin->log, WLOG_ERROR, "calloc failed!");
137 return CHANNEL_RC_NO_MEMORY;
138 }
139
140 /* implant configured channel callbacks */
141 callback->iface = *plugin->channel_callbacks;
142 callback->iface.OnClose = generic_on_close;
143
144 callback->plugin = listener_callback->plugin;
145 callback->channel_mgr = listener_callback->channel_mgr;
146 callback->channel = pChannel;
147
148 listener_callback->channel_callback = callback;
149 listener_callback->channel = pChannel;
150
151 *ppCallback = &callback->iface;
152 generic_update_connected_state(plugin, TRUE);
153 return CHANNEL_RC_OK;
154}
155
156static UINT generic_dynvc_plugin_initialize(IWTSPlugin* pPlugin,
157 IWTSVirtualChannelManager* pChannelMgr)
158{
159 UINT rc = 0;
160 GENERIC_DYNVC_PLUGIN* plugin = (GENERIC_DYNVC_PLUGIN*)pPlugin;
161
162 if (!plugin)
163 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
164
165 if (!pChannelMgr)
166 return ERROR_INVALID_PARAMETER;
167
168 if (plugin->initialized)
169 {
170 WLog_ERR(TAG, "[%s] channel initialized twice, aborting", plugin->dynvc_name);
171 return ERROR_INVALID_DATA;
172 }
173
174 WLog_Print(plugin->log, WLOG_TRACE, "...");
175 GENERIC_LISTENER_CALLBACK* listener_callback =
177 if (!listener_callback)
178 {
179 WLog_Print(plugin->log, WLOG_ERROR, "calloc failed!");
180 return CHANNEL_RC_NO_MEMORY;
181 }
182
183 plugin->listener_callback = listener_callback;
184 listener_callback->iface.OnNewChannelConnection = generic_on_new_channel_connection;
185 listener_callback->plugin = pPlugin;
186 listener_callback->channel_mgr = pChannelMgr;
187 rc = pChannelMgr->CreateListener(pChannelMgr, plugin->dynvc_name, 0, &listener_callback->iface,
188 &plugin->listener);
189
190 plugin->listener->pInterface = plugin->iface.pInterface;
191 plugin->initialized = (rc == CHANNEL_RC_OK);
192 return rc;
193}
194
195static UINT generic_plugin_terminated(IWTSPlugin* pPlugin)
196{
197 GENERIC_DYNVC_PLUGIN* plugin = (GENERIC_DYNVC_PLUGIN*)pPlugin;
198 UINT error = CHANNEL_RC_OK;
199
200 if (!plugin)
201 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
202
203 WLog_Print(plugin->log, WLOG_TRACE, "...");
204
205 /* some channels (namely rdpei), look at initialized to see if they should continue to run */
206 plugin->initialized = FALSE;
207
208 if (plugin->terminatePluginFn)
209 plugin->terminatePluginFn(plugin);
210
211 if (plugin->listener_callback)
212 {
213 IWTSVirtualChannelManager* mgr = plugin->listener_callback->channel_mgr;
214 if (mgr)
215 IFCALL(mgr->DestroyListener, mgr, plugin->listener);
216 }
217
218 free(plugin->listener_callback);
219 free(plugin->dynvc_name);
220 free(plugin);
221 return error;
222}
223
224static UINT generic_dynvc_plugin_attached(IWTSPlugin* pPlugin)
225{
226 GENERIC_DYNVC_PLUGIN* pluginn = (GENERIC_DYNVC_PLUGIN*)pPlugin;
227 UINT error = CHANNEL_RC_OK;
228
229 if (!pluginn)
230 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
231
232 pluginn->attached = TRUE;
233 return error;
234}
235
236static UINT generic_dynvc_plugin_detached(IWTSPlugin* pPlugin)
237{
238 GENERIC_DYNVC_PLUGIN* plugin = (GENERIC_DYNVC_PLUGIN*)pPlugin;
239 UINT error = CHANNEL_RC_OK;
240
241 if (!plugin)
242 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
243
244 plugin->attached = FALSE;
245 return error;
246}
247
248UINT freerdp_generic_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints, const char* logTag,
249 const char* name, size_t pluginSize, size_t channelCallbackSize,
250 const IWTSVirtualChannelCallback* channel_callbacks,
251 DYNVC_PLUGIN_INIT_FN initPluginFn,
252 DYNVC_PLUGIN_TERMINATE_FN terminatePluginFn)
253{
254 return freerdp_generic_DVCPluginEntryWithOptions(pEntryPoints, logTag, name, pluginSize,
255 channelCallbackSize, channel_callbacks,
256 initPluginFn, terminatePluginFn, 0);
257}
258
259UINT freerdp_generic_DVCPluginEntryWithOptions(
260 IDRDYNVC_ENTRY_POINTS* pEntryPoints, const char* logTag, const char* name, size_t pluginSize,
261 size_t channelCallbackSize, const IWTSVirtualChannelCallback* channel_callbacks,
262 DYNVC_PLUGIN_INIT_FN initPluginFn, DYNVC_PLUGIN_TERMINATE_FN terminatePluginFn, UINT32 flags)
263{
264 UINT error = CHANNEL_RC_INITIALIZATION_ERROR;
265
266 WINPR_ASSERT(pEntryPoints);
267 WINPR_ASSERT(pEntryPoints->GetPlugin);
268 WINPR_ASSERT(logTag);
269 WINPR_ASSERT(name);
270 WINPR_ASSERT(pluginSize >= sizeof(GENERIC_DYNVC_PLUGIN));
271 WINPR_ASSERT(channelCallbackSize >= sizeof(GENERIC_CHANNEL_CALLBACK));
272
273 GENERIC_DYNVC_PLUGIN* plugin =
274 (GENERIC_DYNVC_PLUGIN*)pEntryPoints->GetPlugin(pEntryPoints, name);
275 if (plugin != nullptr)
276 return CHANNEL_RC_ALREADY_INITIALIZED;
277
278 /* Ensure 32 byte alignment.
279 */
280 size_t size = pluginSize;
281 if ((size % 32) != 0)
282 size += 32 - size % 32;
283 size += sizeof(GENERIC_DYNVC_PLUGIN_PRIVATE);
284 plugin = (GENERIC_DYNVC_PLUGIN*)calloc(1, size);
285 if (!plugin)
286 {
287 WLog_ERR(TAG, "calloc failed!");
288 return CHANNEL_RC_NO_MEMORY;
289 }
290
291 BYTE* ptr = &(((BYTE*)plugin)[size - sizeof(GENERIC_DYNVC_PLUGIN_PRIVATE)]);
292 GENERIC_DYNVC_PLUGIN_PRIVATE* priv =
293 WINPR_PACKED_ALIGN_CAST(GENERIC_DYNVC_PLUGIN_PRIVATE*, ptr);
294 if (!priv)
295 goto error;
296
297 WINPR_STATIC_ASSERT(sizeof(magic) == sizeof(priv->magic));
298 memcpy(priv->magic, magic, sizeof(priv->magic));
299 priv->pluginSize = pluginSize;
300 priv->flags = flags;
301
302 plugin->log = WLog_Get(logTag);
303 plugin->attached = TRUE;
304 plugin->channel_callbacks = channel_callbacks;
305 plugin->channelCallbackSize = channelCallbackSize;
306 plugin->iface.Initialize = generic_dynvc_plugin_initialize;
307 plugin->iface.Connected = nullptr;
308 plugin->iface.Disconnected = nullptr;
309 plugin->iface.Terminated = generic_plugin_terminated;
310 plugin->iface.Attached = generic_dynvc_plugin_attached;
311 plugin->iface.Detached = generic_dynvc_plugin_detached;
312 plugin->terminatePluginFn = terminatePluginFn;
313
314 if (initPluginFn)
315 {
316 rdpSettings* settings = pEntryPoints->GetRdpSettings(pEntryPoints);
317 rdpContext* context = pEntryPoints->GetRdpContext(pEntryPoints);
318
319 error = initPluginFn(plugin, context, settings);
320 if (error != CHANNEL_RC_OK)
321 goto error;
322 }
323
324 plugin->dynvc_name = _strdup(name);
325 if (!plugin->dynvc_name)
326 goto error;
327
328 error = pEntryPoints->RegisterPlugin(pEntryPoints, name, &plugin->iface);
329 if (error == CHANNEL_RC_OK)
330 return error;
331
332error:
333 generic_plugin_terminated(&plugin->iface);
334 return error;
335}