1 /**
2 * FreeRDP: A Remote Desktop Protocol Implementation
3 * Dynamic Virtual Channel
4 *
5 * Copyright 2010-2011 Vic Lee
6 * Copyright 2015 Thincast Technologies GmbH
7 * Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
8 *
9 * Licensed under the Apache License, Version 2.0 (the "License");
10 * you may not use this file except in compliance with the License.
11 * You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include <winpr/crt.h>
27 #include <winpr/stream.h>
28
29 #include "drdynvc_main.h"
30
31 #define TAG CHANNELS_TAG("drdynvc.client")
32
33 static UINT dvcman_close_channel(IWTSVirtualChannelManager* pChannelMgr, UINT32 ChannelId,
34 BOOL bSendClosePDU);
35 static void dvcman_free(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr);
36 static void dvcman_channel_free(void* channel);
37 static UINT drdynvc_write_data(drdynvcPlugin* drdynvc, UINT32 ChannelId, const BYTE* data,
38 UINT32 dataSize, BOOL* close);
39 static UINT drdynvc_send(drdynvcPlugin* drdynvc, wStream* s);
40
dvcman_wtslistener_free(DVCMAN_LISTENER * listener)41 static void dvcman_wtslistener_free(DVCMAN_LISTENER* listener)
42 {
43 if (listener)
44 free(listener->channel_name);
45
46 free(listener);
47 }
48
49 /**
50 * Function description
51 *
52 * @return 0 on success, otherwise a Win32 error code
53 */
dvcman_get_configuration(IWTSListener * pListener,void ** ppPropertyBag)54 static UINT dvcman_get_configuration(IWTSListener* pListener, void** ppPropertyBag)
55 {
56 WINPR_UNUSED(pListener);
57 *ppPropertyBag = NULL;
58 return ERROR_INTERNAL_ERROR;
59 }
60
61 /**
62 * Function description
63 *
64 * @return 0 on success, otherwise a Win32 error code
65 */
dvcman_create_listener(IWTSVirtualChannelManager * pChannelMgr,const char * pszChannelName,ULONG ulFlags,IWTSListenerCallback * pListenerCallback,IWTSListener ** ppListener)66 static UINT dvcman_create_listener(IWTSVirtualChannelManager* pChannelMgr,
67 const char* pszChannelName, ULONG ulFlags,
68 IWTSListenerCallback* pListenerCallback,
69 IWTSListener** ppListener)
70 {
71 DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
72 DVCMAN_LISTENER* listener;
73
74 WLog_DBG(TAG, "create_listener: %d.%s.", ArrayList_Count(dvcman->listeners) + 1,
75 pszChannelName);
76 listener = (DVCMAN_LISTENER*)calloc(1, sizeof(DVCMAN_LISTENER));
77
78 if (!listener)
79 {
80 WLog_ERR(TAG, "calloc failed!");
81 return CHANNEL_RC_NO_MEMORY;
82 }
83
84 listener->iface.GetConfiguration = dvcman_get_configuration;
85 listener->iface.pInterface = NULL;
86 listener->dvcman = dvcman;
87 listener->channel_name = _strdup(pszChannelName);
88
89 if (!listener->channel_name)
90 {
91 WLog_ERR(TAG, "_strdup failed!");
92 dvcman_wtslistener_free(listener);
93 return CHANNEL_RC_NO_MEMORY;
94 }
95
96 listener->flags = ulFlags;
97 listener->listener_callback = pListenerCallback;
98
99 if (ppListener)
100 *ppListener = (IWTSListener*)listener;
101
102 if (ArrayList_Add(dvcman->listeners, listener) < 0)
103 return ERROR_INTERNAL_ERROR;
104 return CHANNEL_RC_OK;
105 }
106
dvcman_destroy_listener(IWTSVirtualChannelManager * pChannelMgr,IWTSListener * pListener)107 static UINT dvcman_destroy_listener(IWTSVirtualChannelManager* pChannelMgr, IWTSListener* pListener)
108 {
109 DVCMAN_LISTENER* listener = (DVCMAN_LISTENER*)pListener;
110
111 WINPR_UNUSED(pChannelMgr);
112
113 if (listener)
114 {
115 DVCMAN* dvcman = listener->dvcman;
116 if (dvcman)
117 ArrayList_Remove(dvcman->listeners, listener);
118 }
119
120 return CHANNEL_RC_OK;
121 }
122
123 /**
124 * Function description
125 *
126 * @return 0 on success, otherwise a Win32 error code
127 */
dvcman_register_plugin(IDRDYNVC_ENTRY_POINTS * pEntryPoints,const char * name,IWTSPlugin * pPlugin)128 static UINT dvcman_register_plugin(IDRDYNVC_ENTRY_POINTS* pEntryPoints, const char* name,
129 IWTSPlugin* pPlugin)
130 {
131 DVCMAN* dvcman = ((DVCMAN_ENTRY_POINTS*)pEntryPoints)->dvcman;
132
133 if (ArrayList_Add(dvcman->plugin_names, _strdup(name)) < 0)
134 return ERROR_INTERNAL_ERROR;
135 if (ArrayList_Add(dvcman->plugins, pPlugin) < 0)
136 return ERROR_INTERNAL_ERROR;
137
138 WLog_DBG(TAG, "register_plugin: num_plugins %d", ArrayList_Count(dvcman->plugins));
139 return CHANNEL_RC_OK;
140 }
141
dvcman_get_plugin(IDRDYNVC_ENTRY_POINTS * pEntryPoints,const char * name)142 static IWTSPlugin* dvcman_get_plugin(IDRDYNVC_ENTRY_POINTS* pEntryPoints, const char* name)
143 {
144 IWTSPlugin* plugin = NULL;
145 size_t i, nc, pc;
146 DVCMAN* dvcman = ((DVCMAN_ENTRY_POINTS*)pEntryPoints)->dvcman;
147 if (!dvcman || !pEntryPoints || !name)
148 return NULL;
149
150 nc = ArrayList_Count(dvcman->plugin_names);
151 pc = ArrayList_Count(dvcman->plugins);
152 if (nc != pc)
153 return NULL;
154
155 ArrayList_Lock(dvcman->plugin_names);
156 ArrayList_Lock(dvcman->plugins);
157 for (i = 0; i < pc; i++)
158 {
159 const char* cur = ArrayList_GetItem(dvcman->plugin_names, i);
160 if (strcmp(cur, name) == 0)
161 {
162 plugin = ArrayList_GetItem(dvcman->plugins, i);
163 break;
164 }
165 }
166 ArrayList_Unlock(dvcman->plugin_names);
167 ArrayList_Unlock(dvcman->plugins);
168 return plugin;
169 }
170
dvcman_get_plugin_data(IDRDYNVC_ENTRY_POINTS * pEntryPoints)171 static ADDIN_ARGV* dvcman_get_plugin_data(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
172 {
173 return ((DVCMAN_ENTRY_POINTS*)pEntryPoints)->args;
174 }
175
dvcman_get_rdp_settings(IDRDYNVC_ENTRY_POINTS * pEntryPoints)176 static void* dvcman_get_rdp_settings(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
177 {
178 return (void*)((DVCMAN_ENTRY_POINTS*)pEntryPoints)->settings;
179 }
180
dvcman_get_channel_id(IWTSVirtualChannel * channel)181 static UINT32 dvcman_get_channel_id(IWTSVirtualChannel* channel)
182 {
183 DVCMAN_CHANNEL* dvc = (DVCMAN_CHANNEL*)channel;
184 return dvc->channel_id;
185 }
186
dvcman_get_channel_name(IWTSVirtualChannel * channel)187 static const char* dvcman_get_channel_name(IWTSVirtualChannel* channel)
188 {
189 DVCMAN_CHANNEL* dvc = (DVCMAN_CHANNEL*)channel;
190 return dvc->channel_name;
191 }
192
dvcman_find_channel_by_id(IWTSVirtualChannelManager * pChannelMgr,UINT32 ChannelId)193 static IWTSVirtualChannel* dvcman_find_channel_by_id(IWTSVirtualChannelManager* pChannelMgr,
194 UINT32 ChannelId)
195 {
196 int index;
197 IWTSVirtualChannel* channel = NULL;
198 DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
199 ArrayList_Lock(dvcman->channels);
200 for (index = 0; index < ArrayList_Count(dvcman->channels); index++)
201 {
202 DVCMAN_CHANNEL* cur = (DVCMAN_CHANNEL*)ArrayList_GetItem(dvcman->channels, index);
203 if (cur->channel_id == ChannelId)
204 {
205 channel = &cur->iface;
206 break;
207 }
208 }
209
210 ArrayList_Unlock(dvcman->channels);
211 return channel;
212 }
213
dvcman_plugin_terminate(void * plugin)214 static void dvcman_plugin_terminate(void* plugin)
215 {
216 IWTSPlugin* pPlugin = plugin;
217
218 UINT error = IFCALLRESULT(CHANNEL_RC_OK, pPlugin->Terminated, pPlugin);
219 if (error != CHANNEL_RC_OK)
220 WLog_ERR(TAG, "Terminated failed with error %" PRIu32 "!", error);
221 }
222
wts_listener_free(void * arg)223 static void wts_listener_free(void* arg)
224 {
225 DVCMAN_LISTENER* listener = (DVCMAN_LISTENER*)arg;
226 dvcman_wtslistener_free(listener);
227 }
dvcman_new(drdynvcPlugin * plugin)228 static IWTSVirtualChannelManager* dvcman_new(drdynvcPlugin* plugin)
229 {
230 wObject* obj;
231 DVCMAN* dvcman;
232 dvcman = (DVCMAN*)calloc(1, sizeof(DVCMAN));
233
234 if (!dvcman)
235 return NULL;
236
237 dvcman->iface.CreateListener = dvcman_create_listener;
238 dvcman->iface.DestroyListener = dvcman_destroy_listener;
239 dvcman->iface.FindChannelById = dvcman_find_channel_by_id;
240 dvcman->iface.GetChannelId = dvcman_get_channel_id;
241 dvcman->iface.GetChannelName = dvcman_get_channel_name;
242 dvcman->drdynvc = plugin;
243 dvcman->channels = ArrayList_New(TRUE);
244
245 if (!dvcman->channels)
246 goto fail;
247
248 obj = ArrayList_Object(dvcman->channels);
249 obj->fnObjectFree = dvcman_channel_free;
250
251 dvcman->pool = StreamPool_New(TRUE, 10);
252 if (!dvcman->pool)
253 goto fail;
254
255 dvcman->listeners = ArrayList_New(TRUE);
256 if (!dvcman->listeners)
257 goto fail;
258 obj = ArrayList_Object(dvcman->listeners);
259 obj->fnObjectFree = wts_listener_free;
260
261 dvcman->plugin_names = ArrayList_New(TRUE);
262 if (!dvcman->plugin_names)
263 goto fail;
264 obj = ArrayList_Object(dvcman->plugin_names);
265 obj->fnObjectFree = free;
266
267 dvcman->plugins = ArrayList_New(TRUE);
268 if (!dvcman->plugins)
269 goto fail;
270 obj = ArrayList_Object(dvcman->plugins);
271 obj->fnObjectFree = dvcman_plugin_terminate;
272 return &dvcman->iface;
273 fail:
274 dvcman_free(plugin, &dvcman->iface);
275 return NULL;
276 }
277
278 /**
279 * Function description
280 *
281 * @return 0 on success, otherwise a Win32 error code
282 */
dvcman_load_addin(drdynvcPlugin * drdynvc,IWTSVirtualChannelManager * pChannelMgr,ADDIN_ARGV * args,rdpSettings * settings)283 static UINT dvcman_load_addin(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr,
284 ADDIN_ARGV* args, rdpSettings* settings)
285 {
286 DVCMAN_ENTRY_POINTS entryPoints;
287 PDVC_PLUGIN_ENTRY pDVCPluginEntry = NULL;
288 WLog_Print(drdynvc->log, WLOG_INFO, "Loading Dynamic Virtual Channel %s", args->argv[0]);
289 pDVCPluginEntry = (PDVC_PLUGIN_ENTRY)freerdp_load_channel_addin_entry(
290 args->argv[0], NULL, NULL, FREERDP_ADDIN_CHANNEL_DYNAMIC);
291
292 if (pDVCPluginEntry)
293 {
294 entryPoints.iface.RegisterPlugin = dvcman_register_plugin;
295 entryPoints.iface.GetPlugin = dvcman_get_plugin;
296 entryPoints.iface.GetPluginData = dvcman_get_plugin_data;
297 entryPoints.iface.GetRdpSettings = dvcman_get_rdp_settings;
298 entryPoints.dvcman = (DVCMAN*)pChannelMgr;
299 entryPoints.args = args;
300 entryPoints.settings = settings;
301 return pDVCPluginEntry(&entryPoints.iface);
302 }
303
304 return ERROR_INVALID_FUNCTION;
305 }
306
dvcman_channel_new(drdynvcPlugin * drdynvc,IWTSVirtualChannelManager * pChannelMgr,UINT32 ChannelId,const char * ChannelName)307 static DVCMAN_CHANNEL* dvcman_channel_new(drdynvcPlugin* drdynvc,
308 IWTSVirtualChannelManager* pChannelMgr, UINT32 ChannelId,
309 const char* ChannelName)
310 {
311 DVCMAN_CHANNEL* channel;
312
313 if (dvcman_find_channel_by_id(pChannelMgr, ChannelId))
314 {
315 WLog_Print(drdynvc->log, WLOG_ERROR,
316 "Protocol error: Duplicated ChannelId %" PRIu32 " (%s)!", ChannelId,
317 ChannelName);
318 return NULL;
319 }
320
321 channel = (DVCMAN_CHANNEL*)calloc(1, sizeof(DVCMAN_CHANNEL));
322
323 if (!channel)
324 goto fail;
325
326 channel->dvcman = (DVCMAN*)pChannelMgr;
327 channel->channel_id = ChannelId;
328 channel->channel_name = _strdup(ChannelName);
329
330 if (!channel->channel_name)
331 goto fail;
332
333 if (!InitializeCriticalSectionEx(&(channel->lock), 0, 0))
334 goto fail;
335
336 return channel;
337 fail:
338 dvcman_channel_free(channel);
339 return NULL;
340 }
341
dvcman_channel_free(void * arg)342 static void dvcman_channel_free(void* arg)
343 {
344 DVCMAN_CHANNEL* channel = (DVCMAN_CHANNEL*)arg;
345 UINT error = CHANNEL_RC_OK;
346
347 if (channel)
348 {
349 if (channel->channel_callback)
350 {
351 IFCALL(channel->channel_callback->OnClose, channel->channel_callback);
352 channel->channel_callback = NULL;
353 }
354
355 if (channel->status == CHANNEL_RC_OK)
356 {
357 IWTSVirtualChannel* ichannel = (IWTSVirtualChannel*)channel;
358
359 if (channel->dvcman && channel->dvcman->drdynvc)
360 {
361 DrdynvcClientContext* context = channel->dvcman->drdynvc->context;
362
363 if (context)
364 {
365 IFCALLRET(context->OnChannelDisconnected, error, context, channel->channel_name,
366 channel->pInterface);
367 }
368 }
369
370 error = IFCALLRESULT(CHANNEL_RC_OK, ichannel->Close, ichannel);
371
372 if (error != CHANNEL_RC_OK)
373 WLog_ERR(TAG, "Close failed with error %" PRIu32 "!", error);
374 }
375
376 if (channel->dvc_data)
377 Stream_Release(channel->dvc_data);
378
379 DeleteCriticalSection(&(channel->lock));
380 free(channel->channel_name);
381 }
382
383 free(channel);
384 }
385
dvcman_clear(drdynvcPlugin * drdynvc,IWTSVirtualChannelManager * pChannelMgr)386 static void dvcman_clear(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr)
387 {
388 DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
389
390 WINPR_UNUSED(drdynvc);
391
392 ArrayList_Clear(dvcman->plugins);
393 ArrayList_Clear(dvcman->channels);
394 ArrayList_Clear(dvcman->plugin_names);
395 ArrayList_Clear(dvcman->listeners);
396 }
397
dvcman_free(drdynvcPlugin * drdynvc,IWTSVirtualChannelManager * pChannelMgr)398 static void dvcman_free(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr)
399 {
400 DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
401
402 WINPR_UNUSED(drdynvc);
403
404 ArrayList_Free(dvcman->plugins);
405 ArrayList_Free(dvcman->channels);
406 ArrayList_Free(dvcman->plugin_names);
407 ArrayList_Free(dvcman->listeners);
408
409 StreamPool_Free(dvcman->pool);
410 free(dvcman);
411 }
412
413 /**
414 * Function description
415 *
416 * @return 0 on success, otherwise a Win32 error code
417 */
dvcman_init(drdynvcPlugin * drdynvc,IWTSVirtualChannelManager * pChannelMgr)418 static UINT dvcman_init(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr)
419 {
420 int i;
421 DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
422 UINT error = CHANNEL_RC_OK;
423
424 ArrayList_Lock(dvcman->plugins);
425 for (i = 0; i < ArrayList_Count(dvcman->plugins); i++)
426 {
427 IWTSPlugin* pPlugin = ArrayList_GetItem(dvcman->plugins, i);
428
429 error = IFCALLRESULT(CHANNEL_RC_OK, pPlugin->Initialize, pPlugin, pChannelMgr);
430 if (error != CHANNEL_RC_OK)
431 {
432 WLog_Print(drdynvc->log, WLOG_ERROR, "Initialize failed with error %" PRIu32 "!",
433 error);
434 goto fail;
435 }
436 }
437
438 fail:
439 ArrayList_Unlock(dvcman->plugins);
440 return error;
441 }
442
443 /**
444 * Function description
445 *
446 * @return 0 on success, otherwise a Win32 error code
447 */
dvcman_write_channel(IWTSVirtualChannel * pChannel,ULONG cbSize,const BYTE * pBuffer,void * pReserved)448 static UINT dvcman_write_channel(IWTSVirtualChannel* pChannel, ULONG cbSize, const BYTE* pBuffer,
449 void* pReserved)
450 {
451 BOOL close = FALSE;
452 UINT status;
453 DVCMAN_CHANNEL* channel = (DVCMAN_CHANNEL*)pChannel;
454
455 WINPR_UNUSED(pReserved);
456 if (!channel || !channel->dvcman)
457 return CHANNEL_RC_BAD_CHANNEL;
458
459 EnterCriticalSection(&(channel->lock));
460 status =
461 drdynvc_write_data(channel->dvcman->drdynvc, channel->channel_id, pBuffer, cbSize, &close);
462 LeaveCriticalSection(&(channel->lock));
463 /* Close delayed, it removes the channel struct */
464 if (close)
465 dvcman_close_channel(channel->dvcman->drdynvc->channel_mgr, channel->channel_id, TRUE);
466 return status;
467 }
468
469 /**
470 * Function description
471 *
472 * @return 0 on success, otherwise a Win32 error code
473 */
dvcman_close_channel_iface(IWTSVirtualChannel * pChannel)474 static UINT dvcman_close_channel_iface(IWTSVirtualChannel* pChannel)
475 {
476 DVCMAN_CHANNEL* channel = (DVCMAN_CHANNEL*)pChannel;
477
478 if (!channel)
479 return CHANNEL_RC_BAD_CHANNEL;
480
481 WLog_DBG(TAG, "close_channel_iface: id=%" PRIu32 "", channel->channel_id);
482 return CHANNEL_RC_OK;
483 }
484
485 /**
486 * Function description
487 *
488 * @return 0 on success, otherwise a Win32 error code
489 */
dvcman_create_channel(drdynvcPlugin * drdynvc,IWTSVirtualChannelManager * pChannelMgr,UINT32 ChannelId,const char * ChannelName)490 static UINT dvcman_create_channel(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr,
491 UINT32 ChannelId, const char* ChannelName)
492 {
493 int i;
494 BOOL bAccept;
495 DVCMAN_CHANNEL* channel;
496 DrdynvcClientContext* context;
497 DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
498 UINT error;
499
500 if (!(channel = dvcman_channel_new(drdynvc, pChannelMgr, ChannelId, ChannelName)))
501 {
502 WLog_Print(drdynvc->log, WLOG_ERROR, "dvcman_channel_new failed!");
503 return CHANNEL_RC_NO_MEMORY;
504 }
505
506 channel->status = ERROR_NOT_CONNECTED;
507 if (ArrayList_Add(dvcman->channels, channel) < 0)
508 return ERROR_INTERNAL_ERROR;
509
510 ArrayList_Lock(dvcman->listeners);
511 for (i = 0; i < ArrayList_Count(dvcman->listeners); i++)
512 {
513 DVCMAN_LISTENER* listener = (DVCMAN_LISTENER*)ArrayList_GetItem(dvcman->listeners, i);
514
515 if (strcmp(listener->channel_name, ChannelName) == 0)
516 {
517 IWTSVirtualChannelCallback* pCallback = NULL;
518 channel->iface.Write = dvcman_write_channel;
519 channel->iface.Close = dvcman_close_channel_iface;
520 bAccept = TRUE;
521
522 if ((error = listener->listener_callback->OnNewChannelConnection(
523 listener->listener_callback, &channel->iface, NULL, &bAccept, &pCallback)) ==
524 CHANNEL_RC_OK &&
525 bAccept)
526 {
527 WLog_Print(drdynvc->log, WLOG_DEBUG, "listener %s created new channel %" PRIu32 "",
528 listener->channel_name, channel->channel_id);
529 channel->status = CHANNEL_RC_OK;
530 channel->channel_callback = pCallback;
531 channel->pInterface = listener->iface.pInterface;
532 context = dvcman->drdynvc->context;
533 IFCALLRET(context->OnChannelConnected, error, context, ChannelName,
534 listener->iface.pInterface);
535
536 if (error)
537 WLog_Print(drdynvc->log, WLOG_ERROR,
538 "context.OnChannelConnected failed with error %" PRIu32 "", error);
539
540 goto fail;
541 }
542 else
543 {
544 if (error)
545 {
546 WLog_Print(drdynvc->log, WLOG_ERROR,
547 "OnNewChannelConnection failed with error %" PRIu32 "!", error);
548 goto fail;
549 }
550 else
551 {
552 WLog_Print(drdynvc->log, WLOG_ERROR,
553 "OnNewChannelConnection returned with bAccept FALSE!");
554 error = ERROR_INTERNAL_ERROR;
555 goto fail;
556 }
557 }
558 }
559 }
560 error = ERROR_INTERNAL_ERROR;
561 fail:
562 ArrayList_Unlock(dvcman->listeners);
563
564 return error;
565 }
566
567 /**
568 * Function description
569 *
570 * @return 0 on success, otherwise a Win32 error code
571 */
dvcman_open_channel(drdynvcPlugin * drdynvc,IWTSVirtualChannelManager * pChannelMgr,UINT32 ChannelId)572 static UINT dvcman_open_channel(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr,
573 UINT32 ChannelId)
574 {
575 DVCMAN_CHANNEL* channel;
576 IWTSVirtualChannelCallback* pCallback;
577 UINT error;
578 channel = (DVCMAN_CHANNEL*)dvcman_find_channel_by_id(pChannelMgr, ChannelId);
579
580 if (!channel)
581 {
582 WLog_Print(drdynvc->log, WLOG_ERROR, "ChannelId %" PRIu32 " not found!", ChannelId);
583 return ERROR_INTERNAL_ERROR;
584 }
585
586 if (channel->status == CHANNEL_RC_OK)
587 {
588 pCallback = channel->channel_callback;
589
590 if (pCallback->OnOpen)
591 {
592 error = pCallback->OnOpen(pCallback);
593 if (error)
594 {
595 WLog_Print(drdynvc->log, WLOG_ERROR, "OnOpen failed with error %" PRIu32 "!",
596 error);
597 return error;
598 }
599 }
600
601 WLog_Print(drdynvc->log, WLOG_DEBUG, "open_channel: ChannelId %" PRIu32 "", ChannelId);
602 }
603
604 return CHANNEL_RC_OK;
605 }
606
607 /**
608 * Function description
609 *
610 * @return 0 on success, otherwise a Win32 error code
611 */
dvcman_close_channel(IWTSVirtualChannelManager * pChannelMgr,UINT32 ChannelId,BOOL bSendClosePDU)612 UINT dvcman_close_channel(IWTSVirtualChannelManager* pChannelMgr, UINT32 ChannelId,
613 BOOL bSendClosePDU)
614 {
615 DVCMAN_CHANNEL* channel;
616 UINT error = CHANNEL_RC_OK;
617 DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
618 drdynvcPlugin* drdynvc = dvcman->drdynvc;
619 channel = (DVCMAN_CHANNEL*)dvcman_find_channel_by_id(pChannelMgr, ChannelId);
620
621 if (!channel)
622 {
623 // WLog_Print(drdynvc->log, WLOG_ERROR, "ChannelId %"PRIu32" not found!", ChannelId);
624 /**
625 * Windows 8 / Windows Server 2012 send close requests for channels that failed to be
626 * created. Do not warn, simply return success here.
627 */
628 return CHANNEL_RC_OK;
629 }
630
631 if (drdynvc && bSendClosePDU)
632 {
633 wStream* s = StreamPool_Take(dvcman->pool, 5);
634 if (!s)
635 {
636 WLog_Print(drdynvc->log, WLOG_ERROR, "StreamPool_Take failed!");
637 error = CHANNEL_RC_NO_MEMORY;
638 }
639 else
640 {
641 Stream_Write_UINT8(s, (CLOSE_REQUEST_PDU << 4) | 0x02);
642 Stream_Write_UINT32(s, ChannelId);
643 error = drdynvc_send(drdynvc, s);
644 }
645 }
646
647 ArrayList_Remove(dvcman->channels, channel);
648 return error;
649 }
650
651 /**
652 * Function description
653 *
654 * @return 0 on success, otherwise a Win32 error code
655 */
dvcman_receive_channel_data_first(drdynvcPlugin * drdynvc,IWTSVirtualChannelManager * pChannelMgr,UINT32 ChannelId,UINT32 length)656 static UINT dvcman_receive_channel_data_first(drdynvcPlugin* drdynvc,
657 IWTSVirtualChannelManager* pChannelMgr,
658 UINT32 ChannelId, UINT32 length)
659 {
660 DVCMAN_CHANNEL* channel;
661 channel = (DVCMAN_CHANNEL*)dvcman_find_channel_by_id(pChannelMgr, ChannelId);
662
663 if (!channel)
664 {
665 /**
666 * Windows Server 2012 R2 can send some messages over
667 * Microsoft::Windows::RDS::Geometry::v08.01 even if the dynamic virtual channel wasn't
668 * registered on our side. Ignoring it works.
669 */
670 WLog_Print(drdynvc->log, WLOG_ERROR, "ChannelId %" PRIu32 " not found!", ChannelId);
671 return CHANNEL_RC_OK;
672 }
673
674 if (channel->dvc_data)
675 Stream_Release(channel->dvc_data);
676
677 channel->dvc_data = StreamPool_Take(channel->dvcman->pool, length);
678
679 if (!channel->dvc_data)
680 {
681 WLog_Print(drdynvc->log, WLOG_ERROR, "StreamPool_Take failed!");
682 return CHANNEL_RC_NO_MEMORY;
683 }
684
685 channel->dvc_data_length = length;
686 return CHANNEL_RC_OK;
687 }
688
689 /**
690 * Function description
691 *
692 * @return 0 on success, otherwise a Win32 error code
693 */
dvcman_receive_channel_data(drdynvcPlugin * drdynvc,IWTSVirtualChannelManager * pChannelMgr,UINT32 ChannelId,wStream * data)694 static UINT dvcman_receive_channel_data(drdynvcPlugin* drdynvc,
695 IWTSVirtualChannelManager* pChannelMgr, UINT32 ChannelId,
696 wStream* data)
697 {
698 UINT status = CHANNEL_RC_OK;
699 DVCMAN_CHANNEL* channel;
700 size_t dataSize = Stream_GetRemainingLength(data);
701 channel = (DVCMAN_CHANNEL*)dvcman_find_channel_by_id(pChannelMgr, ChannelId);
702
703 if (!channel)
704 {
705 /* Windows 8.1 tries to open channels not created.
706 * Ignore cases like this. */
707 WLog_Print(drdynvc->log, WLOG_ERROR, "ChannelId %" PRIu32 " not found!", ChannelId);
708 return CHANNEL_RC_OK;
709 }
710
711 if (channel->dvc_data)
712 {
713 /* Fragmented data */
714 if (Stream_GetPosition(channel->dvc_data) + dataSize > channel->dvc_data_length)
715 {
716 WLog_Print(drdynvc->log, WLOG_ERROR, "data exceeding declared length!");
717 Stream_Release(channel->dvc_data);
718 channel->dvc_data = NULL;
719 return ERROR_INVALID_DATA;
720 }
721
722 Stream_Copy(data, channel->dvc_data, dataSize);
723
724 if (Stream_GetPosition(channel->dvc_data) >= channel->dvc_data_length)
725 {
726 Stream_SealLength(channel->dvc_data);
727 Stream_SetPosition(channel->dvc_data, 0);
728 status = channel->channel_callback->OnDataReceived(channel->channel_callback,
729 channel->dvc_data);
730 Stream_Release(channel->dvc_data);
731 channel->dvc_data = NULL;
732 }
733 }
734 else
735 {
736 status = channel->channel_callback->OnDataReceived(channel->channel_callback, data);
737 }
738
739 return status;
740 }
741
drdynvc_write_variable_uint(wStream * s,UINT32 val)742 static UINT8 drdynvc_write_variable_uint(wStream* s, UINT32 val)
743 {
744 UINT8 cb;
745
746 if (val <= 0xFF)
747 {
748 cb = 0;
749 Stream_Write_UINT8(s, (UINT8)val);
750 }
751 else if (val <= 0xFFFF)
752 {
753 cb = 1;
754 Stream_Write_UINT16(s, (UINT16)val);
755 }
756 else
757 {
758 cb = 2;
759 Stream_Write_UINT32(s, val);
760 }
761
762 return cb;
763 }
764
765 /**
766 * Function description
767 *
768 * @return 0 on success, otherwise a Win32 error code
769 */
drdynvc_send(drdynvcPlugin * drdynvc,wStream * s)770 static UINT drdynvc_send(drdynvcPlugin* drdynvc, wStream* s)
771 {
772 UINT status;
773
774 if (!drdynvc)
775 status = CHANNEL_RC_BAD_CHANNEL_HANDLE;
776 else
777 {
778 status = drdynvc->channelEntryPoints.pVirtualChannelWriteEx(
779 drdynvc->InitHandle, drdynvc->OpenHandle, Stream_Buffer(s),
780 (UINT32)Stream_GetPosition(s), s);
781 }
782
783 switch (status)
784 {
785 case CHANNEL_RC_OK:
786 return CHANNEL_RC_OK;
787
788 case CHANNEL_RC_NOT_CONNECTED:
789 Stream_Release(s);
790 return CHANNEL_RC_OK;
791
792 case CHANNEL_RC_BAD_CHANNEL_HANDLE:
793 Stream_Release(s);
794 WLog_ERR(TAG, "VirtualChannelWriteEx failed with CHANNEL_RC_BAD_CHANNEL_HANDLE");
795 return status;
796
797 default:
798 Stream_Release(s);
799 WLog_Print(drdynvc->log, WLOG_ERROR,
800 "VirtualChannelWriteEx failed with %s [%08" PRIX32 "]",
801 WTSErrorToString(status), status);
802 return status;
803 }
804 }
805
806 /**
807 * Function description
808 *
809 * @return 0 on success, otherwise a Win32 error code
810 */
drdynvc_write_data(drdynvcPlugin * drdynvc,UINT32 ChannelId,const BYTE * data,UINT32 dataSize,BOOL * close)811 static UINT drdynvc_write_data(drdynvcPlugin* drdynvc, UINT32 ChannelId, const BYTE* data,
812 UINT32 dataSize, BOOL* close)
813 {
814 wStream* data_out;
815 size_t pos;
816 UINT8 cbChId;
817 UINT8 cbLen;
818 unsigned long chunkLength;
819 UINT status = CHANNEL_RC_BAD_INIT_HANDLE;
820 DVCMAN* dvcman;
821
822 if (!drdynvc)
823 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
824
825 dvcman = (DVCMAN*)drdynvc->channel_mgr;
826
827 WLog_Print(drdynvc->log, WLOG_DEBUG, "write_data: ChannelId=%" PRIu32 " size=%" PRIu32 "",
828 ChannelId, dataSize);
829 data_out = StreamPool_Take(dvcman->pool, CHANNEL_CHUNK_LENGTH);
830
831 if (!data_out)
832 {
833 WLog_Print(drdynvc->log, WLOG_ERROR, "StreamPool_Take failed!");
834 return CHANNEL_RC_NO_MEMORY;
835 }
836
837 Stream_SetPosition(data_out, 1);
838 cbChId = drdynvc_write_variable_uint(data_out, ChannelId);
839 pos = Stream_GetPosition(data_out);
840
841 if (dataSize == 0)
842 {
843 *close = TRUE;
844 Stream_Release(data_out);
845 }
846 else if (dataSize <= CHANNEL_CHUNK_LENGTH - pos)
847 {
848 Stream_SetPosition(data_out, 0);
849 Stream_Write_UINT8(data_out, (DATA_PDU << 4) | cbChId);
850 Stream_SetPosition(data_out, pos);
851 Stream_Write(data_out, data, dataSize);
852 status = drdynvc_send(drdynvc, data_out);
853 }
854 else
855 {
856 /* Fragment the data */
857 cbLen = drdynvc_write_variable_uint(data_out, dataSize);
858 pos = Stream_GetPosition(data_out);
859 Stream_SetPosition(data_out, 0);
860 Stream_Write_UINT8(data_out, (DATA_FIRST_PDU << 4) | cbChId | (cbLen << 2));
861 Stream_SetPosition(data_out, pos);
862 chunkLength = CHANNEL_CHUNK_LENGTH - pos;
863 Stream_Write(data_out, data, chunkLength);
864 data += chunkLength;
865 dataSize -= chunkLength;
866 status = drdynvc_send(drdynvc, data_out);
867
868 while (status == CHANNEL_RC_OK && dataSize > 0)
869 {
870 data_out = StreamPool_Take(dvcman->pool, CHANNEL_CHUNK_LENGTH);
871
872 if (!data_out)
873 {
874 WLog_Print(drdynvc->log, WLOG_ERROR, "StreamPool_Take failed!");
875 return CHANNEL_RC_NO_MEMORY;
876 }
877
878 Stream_SetPosition(data_out, 1);
879 cbChId = drdynvc_write_variable_uint(data_out, ChannelId);
880 pos = Stream_GetPosition(data_out);
881 Stream_SetPosition(data_out, 0);
882 Stream_Write_UINT8(data_out, (DATA_PDU << 4) | cbChId);
883 Stream_SetPosition(data_out, pos);
884 chunkLength = dataSize;
885
886 if (chunkLength > CHANNEL_CHUNK_LENGTH - pos)
887 chunkLength = CHANNEL_CHUNK_LENGTH - pos;
888
889 Stream_Write(data_out, data, chunkLength);
890 data += chunkLength;
891 dataSize -= chunkLength;
892 status = drdynvc_send(drdynvc, data_out);
893 }
894 }
895
896 if (status != CHANNEL_RC_OK)
897 {
898 WLog_Print(drdynvc->log, WLOG_ERROR, "VirtualChannelWriteEx failed with %s [%08" PRIX32 "]",
899 WTSErrorToString(status), status);
900 return status;
901 }
902
903 return CHANNEL_RC_OK;
904 }
905
906 /**
907 * Function description
908 *
909 * @return 0 on success, otherwise a Win32 error code
910 */
drdynvc_send_capability_response(drdynvcPlugin * drdynvc)911 static UINT drdynvc_send_capability_response(drdynvcPlugin* drdynvc)
912 {
913 UINT status;
914 wStream* s;
915 DVCMAN* dvcman;
916
917 if (!drdynvc)
918 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
919
920 dvcman = (DVCMAN*)drdynvc->channel_mgr;
921 WLog_Print(drdynvc->log, WLOG_TRACE, "capability_response");
922 s = StreamPool_Take(dvcman->pool, 4);
923
924 if (!s)
925 {
926 WLog_Print(drdynvc->log, WLOG_ERROR, "Stream_Ndrdynvc_write_variable_uintew failed!");
927 return CHANNEL_RC_NO_MEMORY;
928 }
929
930 Stream_Write_UINT16(s, 0x0050); /* Cmd+Sp+cbChId+Pad. Note: MSTSC sends 0x005c */
931 Stream_Write_UINT16(s, drdynvc->version);
932 status = drdynvc_send(drdynvc, s);
933
934 if (status != CHANNEL_RC_OK)
935 {
936 WLog_Print(drdynvc->log, WLOG_ERROR, "VirtualChannelWriteEx failed with %s [%08" PRIX32 "]",
937 WTSErrorToString(status), status);
938 }
939
940 return status;
941 }
942
943 /**
944 * Function description
945 *
946 * @return 0 on success, otherwise a Win32 error code
947 */
drdynvc_process_capability_request(drdynvcPlugin * drdynvc,int Sp,int cbChId,wStream * s)948 static UINT drdynvc_process_capability_request(drdynvcPlugin* drdynvc, int Sp, int cbChId,
949 wStream* s)
950 {
951 UINT status;
952
953 if (!drdynvc)
954 return CHANNEL_RC_BAD_INIT_HANDLE;
955
956 if (Stream_GetRemainingLength(s) < 3)
957 return ERROR_INVALID_DATA;
958
959 WLog_Print(drdynvc->log, WLOG_TRACE, "capability_request Sp=%d cbChId=%d", Sp, cbChId);
960 Stream_Seek(s, 1); /* pad */
961 Stream_Read_UINT16(s, drdynvc->version);
962
963 /* RDP8 servers offer version 3, though Microsoft forgot to document it
964 * in their early documents. It behaves the same as version 2.
965 */
966 if ((drdynvc->version == 2) || (drdynvc->version == 3))
967 {
968 if (Stream_GetRemainingLength(s) < 8)
969 return ERROR_INVALID_DATA;
970
971 Stream_Read_UINT16(s, drdynvc->PriorityCharge0);
972 Stream_Read_UINT16(s, drdynvc->PriorityCharge1);
973 Stream_Read_UINT16(s, drdynvc->PriorityCharge2);
974 Stream_Read_UINT16(s, drdynvc->PriorityCharge3);
975 }
976
977 status = drdynvc_send_capability_response(drdynvc);
978 drdynvc->state = DRDYNVC_STATE_READY;
979 return status;
980 }
981
drdynvc_cblen_to_bytes(int cbLen)982 static UINT32 drdynvc_cblen_to_bytes(int cbLen)
983 {
984 switch (cbLen)
985 {
986 case 0:
987 return 1;
988
989 case 1:
990 return 2;
991
992 default:
993 return 4;
994 }
995 }
996
drdynvc_read_variable_uint(wStream * s,int cbLen)997 static UINT32 drdynvc_read_variable_uint(wStream* s, int cbLen)
998 {
999 UINT32 val;
1000
1001 switch (cbLen)
1002 {
1003 case 0:
1004 Stream_Read_UINT8(s, val);
1005 break;
1006
1007 case 1:
1008 Stream_Read_UINT16(s, val);
1009 break;
1010
1011 default:
1012 Stream_Read_UINT32(s, val);
1013 break;
1014 }
1015
1016 return val;
1017 }
1018
1019 /**
1020 * Function description
1021 *
1022 * @return 0 on success, otherwise a Win32 error code
1023 */
drdynvc_process_create_request(drdynvcPlugin * drdynvc,int Sp,int cbChId,wStream * s)1024 static UINT drdynvc_process_create_request(drdynvcPlugin* drdynvc, int Sp, int cbChId, wStream* s)
1025 {
1026 size_t pos;
1027 UINT status;
1028 UINT32 ChannelId;
1029 wStream* data_out;
1030 UINT channel_status;
1031 char* name;
1032 size_t length;
1033 DVCMAN* dvcman;
1034
1035 WINPR_UNUSED(Sp);
1036 if (!drdynvc)
1037 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1038
1039 dvcman = (DVCMAN*)drdynvc->channel_mgr;
1040 if (drdynvc->state == DRDYNVC_STATE_CAPABILITIES)
1041 {
1042 /**
1043 * For some reason the server does not always send the
1044 * capabilities pdu as it should. When this happens,
1045 * send a capabilities response.
1046 */
1047 drdynvc->version = 3;
1048
1049 if ((status = drdynvc_send_capability_response(drdynvc)))
1050 {
1051 WLog_Print(drdynvc->log, WLOG_ERROR, "drdynvc_send_capability_response failed!");
1052 return status;
1053 }
1054
1055 drdynvc->state = DRDYNVC_STATE_READY;
1056 }
1057
1058 if (Stream_GetRemainingLength(s) < drdynvc_cblen_to_bytes(cbChId))
1059 return ERROR_INVALID_DATA;
1060
1061 ChannelId = drdynvc_read_variable_uint(s, cbChId);
1062 pos = Stream_GetPosition(s);
1063 name = (char*)Stream_Pointer(s);
1064 length = Stream_GetRemainingLength(s);
1065
1066 if (strnlen(name, length) >= length)
1067 return ERROR_INVALID_DATA;
1068
1069 WLog_Print(drdynvc->log, WLOG_DEBUG,
1070 "process_create_request: ChannelId=%" PRIu32 " ChannelName=%s", ChannelId, name);
1071 channel_status = dvcman_create_channel(drdynvc, drdynvc->channel_mgr, ChannelId, name);
1072 data_out = StreamPool_Take(dvcman->pool, pos + 4);
1073
1074 if (!data_out)
1075 {
1076 WLog_Print(drdynvc->log, WLOG_ERROR, "StreamPool_Take failed!");
1077 return CHANNEL_RC_NO_MEMORY;
1078 }
1079
1080 Stream_Write_UINT8(data_out, (CREATE_REQUEST_PDU << 4) | cbChId);
1081 Stream_SetPosition(s, 1);
1082 Stream_Copy(s, data_out, pos - 1);
1083
1084 if (channel_status == CHANNEL_RC_OK)
1085 {
1086 WLog_Print(drdynvc->log, WLOG_DEBUG, "channel created");
1087 Stream_Write_UINT32(data_out, 0);
1088 }
1089 else
1090 {
1091 WLog_Print(drdynvc->log, WLOG_DEBUG, "no listener");
1092 Stream_Write_UINT32(data_out, (UINT32)0xC0000001); /* same code used by mstsc */
1093 }
1094
1095 status = drdynvc_send(drdynvc, data_out);
1096
1097 if (status != CHANNEL_RC_OK)
1098 {
1099 WLog_Print(drdynvc->log, WLOG_ERROR, "VirtualChannelWriteEx failed with %s [%08" PRIX32 "]",
1100 WTSErrorToString(status), status);
1101 return status;
1102 }
1103
1104 if (channel_status == CHANNEL_RC_OK)
1105 {
1106 if ((status = dvcman_open_channel(drdynvc, drdynvc->channel_mgr, ChannelId)))
1107 {
1108 WLog_Print(drdynvc->log, WLOG_ERROR,
1109 "dvcman_open_channel failed with error %" PRIu32 "!", status);
1110 return status;
1111 }
1112 }
1113 else
1114 {
1115 if ((status = dvcman_close_channel(drdynvc->channel_mgr, ChannelId, FALSE)))
1116 WLog_Print(drdynvc->log, WLOG_ERROR,
1117 "dvcman_close_channel failed with error %" PRIu32 "!", status);
1118 }
1119
1120 return status;
1121 }
1122
1123 /**
1124 * Function description
1125 *
1126 * @return 0 on success, otherwise a Win32 error code
1127 */
drdynvc_process_data_first(drdynvcPlugin * drdynvc,int Sp,int cbChId,wStream * s)1128 static UINT drdynvc_process_data_first(drdynvcPlugin* drdynvc, int Sp, int cbChId, wStream* s)
1129 {
1130 UINT status;
1131 UINT32 Length;
1132 UINT32 ChannelId;
1133
1134 if (Stream_GetRemainingLength(s) < drdynvc_cblen_to_bytes(cbChId) + drdynvc_cblen_to_bytes(Sp))
1135 return ERROR_INVALID_DATA;
1136
1137 ChannelId = drdynvc_read_variable_uint(s, cbChId);
1138 Length = drdynvc_read_variable_uint(s, Sp);
1139 WLog_Print(drdynvc->log, WLOG_DEBUG,
1140 "process_data_first: Sp=%d cbChId=%d, ChannelId=%" PRIu32 " Length=%" PRIu32 "", Sp,
1141 cbChId, ChannelId, Length);
1142 status = dvcman_receive_channel_data_first(drdynvc, drdynvc->channel_mgr, ChannelId, Length);
1143
1144 if (status == CHANNEL_RC_OK)
1145 status = dvcman_receive_channel_data(drdynvc, drdynvc->channel_mgr, ChannelId, s);
1146
1147 if (status != CHANNEL_RC_OK)
1148 status = dvcman_close_channel(drdynvc->channel_mgr, ChannelId, TRUE);
1149
1150 return status;
1151 }
1152
1153 /**
1154 * Function description
1155 *
1156 * @return 0 on success, otherwise a Win32 error code
1157 */
drdynvc_process_data(drdynvcPlugin * drdynvc,int Sp,int cbChId,wStream * s)1158 static UINT drdynvc_process_data(drdynvcPlugin* drdynvc, int Sp, int cbChId, wStream* s)
1159 {
1160 UINT32 ChannelId;
1161 UINT status;
1162
1163 if (Stream_GetRemainingLength(s) < drdynvc_cblen_to_bytes(cbChId))
1164 return ERROR_INVALID_DATA;
1165
1166 ChannelId = drdynvc_read_variable_uint(s, cbChId);
1167 WLog_Print(drdynvc->log, WLOG_TRACE, "process_data: Sp=%d cbChId=%d, ChannelId=%" PRIu32 "", Sp,
1168 cbChId, ChannelId);
1169 status = dvcman_receive_channel_data(drdynvc, drdynvc->channel_mgr, ChannelId, s);
1170
1171 if (status != CHANNEL_RC_OK)
1172 status = dvcman_close_channel(drdynvc->channel_mgr, ChannelId, TRUE);
1173
1174 return status;
1175 }
1176
1177 /**
1178 * Function description
1179 *
1180 * @return 0 on success, otherwise a Win32 error code
1181 */
drdynvc_process_close_request(drdynvcPlugin * drdynvc,int Sp,int cbChId,wStream * s)1182 static UINT drdynvc_process_close_request(drdynvcPlugin* drdynvc, int Sp, int cbChId, wStream* s)
1183 {
1184 UINT error;
1185 UINT32 ChannelId;
1186
1187 if (Stream_GetRemainingLength(s) < drdynvc_cblen_to_bytes(cbChId))
1188 return ERROR_INVALID_DATA;
1189
1190 ChannelId = drdynvc_read_variable_uint(s, cbChId);
1191 WLog_Print(drdynvc->log, WLOG_DEBUG,
1192 "process_close_request: Sp=%d cbChId=%d, ChannelId=%" PRIu32 "", Sp, cbChId,
1193 ChannelId);
1194
1195 if ((error = dvcman_close_channel(drdynvc->channel_mgr, ChannelId, TRUE)))
1196 WLog_Print(drdynvc->log, WLOG_ERROR, "dvcman_close_channel failed with error %" PRIu32 "!",
1197 error);
1198
1199 return error;
1200 }
1201
1202 /**
1203 * Function description
1204 *
1205 * @return 0 on success, otherwise a Win32 error code
1206 */
drdynvc_order_recv(drdynvcPlugin * drdynvc,wStream * s)1207 static UINT drdynvc_order_recv(drdynvcPlugin* drdynvc, wStream* s)
1208 {
1209 int value;
1210 int Cmd;
1211 int Sp;
1212 int cbChId;
1213
1214 if (Stream_GetRemainingLength(s) < 1)
1215 return ERROR_INVALID_DATA;
1216
1217 Stream_Read_UINT8(s, value);
1218 Cmd = (value & 0xf0) >> 4;
1219 Sp = (value & 0x0c) >> 2;
1220 cbChId = (value & 0x03) >> 0;
1221 WLog_Print(drdynvc->log, WLOG_DEBUG, "order_recv: Cmd=0x%x, Sp=%d cbChId=%d", Cmd, Sp, cbChId);
1222
1223 switch (Cmd)
1224 {
1225 case CAPABILITY_REQUEST_PDU:
1226 return drdynvc_process_capability_request(drdynvc, Sp, cbChId, s);
1227
1228 case CREATE_REQUEST_PDU:
1229 return drdynvc_process_create_request(drdynvc, Sp, cbChId, s);
1230
1231 case DATA_FIRST_PDU:
1232 return drdynvc_process_data_first(drdynvc, Sp, cbChId, s);
1233
1234 case DATA_PDU:
1235 return drdynvc_process_data(drdynvc, Sp, cbChId, s);
1236
1237 case CLOSE_REQUEST_PDU:
1238 return drdynvc_process_close_request(drdynvc, Sp, cbChId, s);
1239
1240 default:
1241 WLog_Print(drdynvc->log, WLOG_ERROR, "unknown drdynvc cmd 0x%x", Cmd);
1242 return ERROR_INTERNAL_ERROR;
1243 }
1244 }
1245
1246 /**
1247 * Function description
1248 *
1249 * @return 0 on success, otherwise a Win32 error code
1250 */
drdynvc_virtual_channel_event_data_received(drdynvcPlugin * drdynvc,void * pData,UINT32 dataLength,UINT32 totalLength,UINT32 dataFlags)1251 static UINT drdynvc_virtual_channel_event_data_received(drdynvcPlugin* drdynvc, void* pData,
1252 UINT32 dataLength, UINT32 totalLength,
1253 UINT32 dataFlags)
1254 {
1255 wStream* data_in;
1256
1257 if ((dataFlags & CHANNEL_FLAG_SUSPEND) || (dataFlags & CHANNEL_FLAG_RESUME))
1258 {
1259 return CHANNEL_RC_OK;
1260 }
1261
1262 if (dataFlags & CHANNEL_FLAG_FIRST)
1263 {
1264 DVCMAN* mgr = (DVCMAN*)drdynvc->channel_mgr;
1265 if (drdynvc->data_in)
1266 Stream_Release(drdynvc->data_in);
1267
1268 drdynvc->data_in = StreamPool_Take(mgr->pool, totalLength);
1269 }
1270
1271 if (!(data_in = drdynvc->data_in))
1272 {
1273 WLog_Print(drdynvc->log, WLOG_ERROR, "StreamPool_Take failed!");
1274 return CHANNEL_RC_NO_MEMORY;
1275 }
1276
1277 if (!Stream_EnsureRemainingCapacity(data_in, dataLength))
1278 {
1279 WLog_Print(drdynvc->log, WLOG_ERROR, "Stream_EnsureRemainingCapacity failed!");
1280 Stream_Release(drdynvc->data_in);
1281 drdynvc->data_in = NULL;
1282 return ERROR_INTERNAL_ERROR;
1283 }
1284
1285 Stream_Write(data_in, pData, dataLength);
1286
1287 if (dataFlags & CHANNEL_FLAG_LAST)
1288 {
1289 const size_t cap = Stream_Capacity(data_in);
1290 const size_t pos = Stream_GetPosition(data_in);
1291 if (cap < pos)
1292 {
1293 WLog_Print(drdynvc->log, WLOG_ERROR, "drdynvc_plugin_process_received: read error");
1294 return ERROR_INVALID_DATA;
1295 }
1296
1297 drdynvc->data_in = NULL;
1298 Stream_SealLength(data_in);
1299 Stream_SetPosition(data_in, 0);
1300
1301 if (!MessageQueue_Post(drdynvc->queue, NULL, 0, (void*)data_in, NULL))
1302 {
1303 WLog_Print(drdynvc->log, WLOG_ERROR, "MessageQueue_Post failed!");
1304 return ERROR_INTERNAL_ERROR;
1305 }
1306 }
1307
1308 return CHANNEL_RC_OK;
1309 }
1310
drdynvc_virtual_channel_open_event_ex(LPVOID lpUserParam,DWORD openHandle,UINT event,LPVOID pData,UINT32 dataLength,UINT32 totalLength,UINT32 dataFlags)1311 static void VCAPITYPE drdynvc_virtual_channel_open_event_ex(LPVOID lpUserParam, DWORD openHandle,
1312 UINT event, LPVOID pData,
1313 UINT32 dataLength, UINT32 totalLength,
1314 UINT32 dataFlags)
1315 {
1316 UINT error = CHANNEL_RC_OK;
1317 drdynvcPlugin* drdynvc = (drdynvcPlugin*)lpUserParam;
1318
1319 switch (event)
1320 {
1321 case CHANNEL_EVENT_DATA_RECEIVED:
1322 if (!drdynvc || (drdynvc->OpenHandle != openHandle))
1323 {
1324 WLog_ERR(TAG, "drdynvc_virtual_channel_open_event: error no match");
1325 return;
1326 }
1327 if ((error = drdynvc_virtual_channel_event_data_received(drdynvc, pData, dataLength,
1328 totalLength, dataFlags)))
1329 WLog_Print(drdynvc->log, WLOG_ERROR,
1330 "drdynvc_virtual_channel_event_data_received failed with error %" PRIu32
1331 "",
1332 error);
1333
1334 break;
1335
1336 case CHANNEL_EVENT_WRITE_CANCELLED:
1337 case CHANNEL_EVENT_WRITE_COMPLETE:
1338 {
1339 wStream* s = (wStream*)pData;
1340 Stream_Release(s);
1341 }
1342 break;
1343
1344 case CHANNEL_EVENT_USER:
1345 break;
1346 }
1347
1348 if (error && drdynvc && drdynvc->rdpcontext)
1349 setChannelError(drdynvc->rdpcontext, error,
1350 "drdynvc_virtual_channel_open_event reported an error");
1351 }
1352
drdynvc_virtual_channel_client_thread(LPVOID arg)1353 static DWORD WINAPI drdynvc_virtual_channel_client_thread(LPVOID arg)
1354 {
1355 wStream* data;
1356 wMessage message;
1357 UINT error = CHANNEL_RC_OK;
1358 drdynvcPlugin* drdynvc = (drdynvcPlugin*)arg;
1359
1360 if (!drdynvc)
1361 {
1362 ExitThread((DWORD)CHANNEL_RC_BAD_CHANNEL_HANDLE);
1363 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1364 }
1365
1366 while (1)
1367 {
1368 if (!MessageQueue_Wait(drdynvc->queue))
1369 {
1370 WLog_Print(drdynvc->log, WLOG_ERROR, "MessageQueue_Wait failed!");
1371 error = ERROR_INTERNAL_ERROR;
1372 break;
1373 }
1374
1375 if (!MessageQueue_Peek(drdynvc->queue, &message, TRUE))
1376 {
1377 WLog_Print(drdynvc->log, WLOG_ERROR, "MessageQueue_Peek failed!");
1378 error = ERROR_INTERNAL_ERROR;
1379 break;
1380 }
1381
1382 if (message.id == WMQ_QUIT)
1383 break;
1384
1385 if (message.id == 0)
1386 {
1387 data = (wStream*)message.wParam;
1388
1389 if ((error = drdynvc_order_recv(drdynvc, data)))
1390 {
1391 WLog_Print(drdynvc->log, WLOG_WARN,
1392 "drdynvc_order_recv failed with error %" PRIu32 "!", error);
1393 }
1394
1395 Stream_Release(data);
1396 }
1397 }
1398
1399 {
1400 /* Disconnect remaining dynamic channels that the server did not.
1401 * This is required to properly shut down channels by calling the appropriate
1402 * event handlers. */
1403 size_t count = 0;
1404 DVCMAN* drdynvcMgr = (DVCMAN*)drdynvc->channel_mgr;
1405
1406 do
1407 {
1408 ArrayList_Lock(drdynvcMgr->channels);
1409 count = ArrayList_Count(drdynvcMgr->channels);
1410 if (count > 0)
1411 {
1412 IWTSVirtualChannel* channel =
1413 (IWTSVirtualChannel*)ArrayList_GetItem(drdynvcMgr->channels, 0);
1414 const UINT32 ChannelId = drdynvc->channel_mgr->GetChannelId(channel);
1415 dvcman_close_channel(drdynvc->channel_mgr, ChannelId, FALSE);
1416 count--;
1417 }
1418 ArrayList_Unlock(drdynvcMgr->channels);
1419 } while (count > 0);
1420 }
1421
1422 if (error && drdynvc->rdpcontext)
1423 setChannelError(drdynvc->rdpcontext, error,
1424 "drdynvc_virtual_channel_client_thread reported an error");
1425
1426 ExitThread((DWORD)error);
1427 return error;
1428 }
1429
drdynvc_queue_object_free(void * obj)1430 static void drdynvc_queue_object_free(void* obj)
1431 {
1432 wStream* s;
1433 wMessage* msg = (wMessage*)obj;
1434
1435 if (!msg || (msg->id != 0))
1436 return;
1437
1438 s = (wStream*)msg->wParam;
1439
1440 if (s)
1441 Stream_Release(s);
1442 }
1443
drdynvc_virtual_channel_event_initialized(drdynvcPlugin * drdynvc,LPVOID pData,UINT32 dataLength)1444 static UINT drdynvc_virtual_channel_event_initialized(drdynvcPlugin* drdynvc, LPVOID pData,
1445 UINT32 dataLength)
1446 {
1447 UINT error = CHANNEL_RC_OK;
1448 WINPR_UNUSED(pData);
1449 WINPR_UNUSED(dataLength);
1450
1451 if (!drdynvc)
1452 goto error;
1453
1454 drdynvc->queue = MessageQueue_New(NULL);
1455
1456 if (!drdynvc->queue)
1457 {
1458 error = CHANNEL_RC_NO_MEMORY;
1459 WLog_Print(drdynvc->log, WLOG_ERROR, "MessageQueue_New failed!");
1460 goto error;
1461 }
1462
1463 drdynvc->queue->object.fnObjectFree = drdynvc_queue_object_free;
1464 drdynvc->channel_mgr = dvcman_new(drdynvc);
1465
1466 if (!drdynvc->channel_mgr)
1467 {
1468 error = CHANNEL_RC_NO_MEMORY;
1469 WLog_Print(drdynvc->log, WLOG_ERROR, "dvcman_new failed!");
1470 goto error;
1471 }
1472
1473 return CHANNEL_RC_OK;
1474 error:
1475 return ERROR_INTERNAL_ERROR;
1476 }
1477
1478 /**
1479 * Function description
1480 *
1481 * @return 0 on success, otherwise a Win32 error code
1482 */
drdynvc_virtual_channel_event_connected(drdynvcPlugin * drdynvc,LPVOID pData,UINT32 dataLength)1483 static UINT drdynvc_virtual_channel_event_connected(drdynvcPlugin* drdynvc, LPVOID pData,
1484 UINT32 dataLength)
1485 {
1486 UINT error;
1487 UINT32 status;
1488 UINT32 index;
1489 ADDIN_ARGV* args;
1490 rdpSettings* settings;
1491
1492 WINPR_UNUSED(pData);
1493 WINPR_UNUSED(dataLength);
1494
1495 if (!drdynvc)
1496 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1497
1498 status = drdynvc->channelEntryPoints.pVirtualChannelOpenEx(
1499 drdynvc->InitHandle, &drdynvc->OpenHandle, drdynvc->channelDef.name,
1500 drdynvc_virtual_channel_open_event_ex);
1501
1502 if (status != CHANNEL_RC_OK)
1503 {
1504 WLog_Print(drdynvc->log, WLOG_ERROR, "pVirtualChannelOpen failed with %s [%08" PRIX32 "]",
1505 WTSErrorToString(status), status);
1506 return status;
1507 }
1508
1509 settings = (rdpSettings*)drdynvc->channelEntryPoints.pExtendedData;
1510
1511 for (index = 0; index < settings->DynamicChannelCount; index++)
1512 {
1513 args = settings->DynamicChannelArray[index];
1514 error = dvcman_load_addin(drdynvc, drdynvc->channel_mgr, args, settings);
1515
1516 if (CHANNEL_RC_OK != error)
1517 goto error;
1518 }
1519
1520 if ((error = dvcman_init(drdynvc, drdynvc->channel_mgr)))
1521 {
1522 WLog_Print(drdynvc->log, WLOG_ERROR, "dvcman_init failed with error %" PRIu32 "!", error);
1523 goto error;
1524 }
1525
1526 drdynvc->state = DRDYNVC_STATE_CAPABILITIES;
1527
1528 if (!(drdynvc->thread = CreateThread(NULL, 0, drdynvc_virtual_channel_client_thread,
1529 (void*)drdynvc, 0, NULL)))
1530 {
1531 error = ERROR_INTERNAL_ERROR;
1532 WLog_Print(drdynvc->log, WLOG_ERROR, "CreateThread failed!");
1533 goto error;
1534 }
1535
1536 error:
1537 return error;
1538 }
1539
1540 /**
1541 * Function description
1542 *
1543 * @return 0 on success, otherwise a Win32 error code
1544 */
drdynvc_virtual_channel_event_disconnected(drdynvcPlugin * drdynvc)1545 static UINT drdynvc_virtual_channel_event_disconnected(drdynvcPlugin* drdynvc)
1546 {
1547 UINT status;
1548
1549 if (!drdynvc)
1550 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1551
1552 if (drdynvc->OpenHandle == 0)
1553 return CHANNEL_RC_OK;
1554
1555 if (!MessageQueue_PostQuit(drdynvc->queue, 0))
1556 {
1557 status = GetLastError();
1558 WLog_Print(drdynvc->log, WLOG_ERROR, "MessageQueue_PostQuit failed with error %" PRIu32 "",
1559 status);
1560 return status;
1561 }
1562
1563 if (WaitForSingleObject(drdynvc->thread, INFINITE) != WAIT_OBJECT_0)
1564 {
1565 status = GetLastError();
1566 WLog_Print(drdynvc->log, WLOG_ERROR, "WaitForSingleObject failed with error %" PRIu32 "",
1567 status);
1568 return status;
1569 }
1570
1571 CloseHandle(drdynvc->thread);
1572 drdynvc->thread = NULL;
1573
1574 status = drdynvc->channelEntryPoints.pVirtualChannelCloseEx(drdynvc->InitHandle,
1575 drdynvc->OpenHandle);
1576
1577 if (status != CHANNEL_RC_OK)
1578 {
1579 WLog_Print(drdynvc->log, WLOG_ERROR, "pVirtualChannelClose failed with %s [%08" PRIX32 "]",
1580 WTSErrorToString(status), status);
1581 }
1582
1583 dvcman_clear(drdynvc, drdynvc->channel_mgr);
1584 MessageQueue_Clear(drdynvc->queue);
1585 drdynvc->OpenHandle = 0;
1586
1587 if (drdynvc->data_in)
1588 {
1589 Stream_Release(drdynvc->data_in);
1590 drdynvc->data_in = NULL;
1591 }
1592
1593 return status;
1594 }
1595
1596 /**
1597 * Function description
1598 *
1599 * @return 0 on success, otherwise a Win32 error code
1600 */
drdynvc_virtual_channel_event_terminated(drdynvcPlugin * drdynvc)1601 static UINT drdynvc_virtual_channel_event_terminated(drdynvcPlugin* drdynvc)
1602 {
1603 if (!drdynvc)
1604 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1605
1606 MessageQueue_Free(drdynvc->queue);
1607 drdynvc->queue = NULL;
1608
1609 if (drdynvc->channel_mgr)
1610 {
1611 dvcman_free(drdynvc, drdynvc->channel_mgr);
1612 drdynvc->channel_mgr = NULL;
1613 }
1614
1615 drdynvc->InitHandle = 0;
1616 free(drdynvc->context);
1617 free(drdynvc);
1618 return CHANNEL_RC_OK;
1619 }
1620
drdynvc_virtual_channel_event_attached(drdynvcPlugin * drdynvc)1621 static UINT drdynvc_virtual_channel_event_attached(drdynvcPlugin* drdynvc)
1622 {
1623 UINT error = CHANNEL_RC_OK;
1624 int i;
1625 DVCMAN* dvcman;
1626
1627 if (!drdynvc)
1628 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1629
1630 dvcman = (DVCMAN*)drdynvc->channel_mgr;
1631
1632 if (!dvcman)
1633 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1634
1635 ArrayList_Lock(dvcman->plugins);
1636 for (i = 0; i < ArrayList_Count(dvcman->plugins); i++)
1637 {
1638 IWTSPlugin* pPlugin = ArrayList_GetItem(dvcman->plugins, i);
1639
1640 error = IFCALLRESULT(CHANNEL_RC_OK, pPlugin->Attached, pPlugin);
1641 if (error != CHANNEL_RC_OK)
1642 {
1643 WLog_Print(drdynvc->log, WLOG_ERROR, "Attach failed with error %" PRIu32 "!", error);
1644 goto fail;
1645 }
1646 }
1647
1648 fail:
1649 ArrayList_Unlock(dvcman->plugins);
1650 return error;
1651 }
1652
drdynvc_virtual_channel_event_detached(drdynvcPlugin * drdynvc)1653 static UINT drdynvc_virtual_channel_event_detached(drdynvcPlugin* drdynvc)
1654 {
1655 UINT error = CHANNEL_RC_OK;
1656 int i;
1657 DVCMAN* dvcman;
1658
1659 if (!drdynvc)
1660 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1661
1662 dvcman = (DVCMAN*)drdynvc->channel_mgr;
1663
1664 if (!dvcman)
1665 return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1666
1667 ArrayList_Lock(dvcman->plugins);
1668 for (i = 0; i < ArrayList_Count(dvcman->plugins); i++)
1669 {
1670 IWTSPlugin* pPlugin = ArrayList_GetItem(dvcman->plugins, i);
1671
1672 error = IFCALLRESULT(CHANNEL_RC_OK, pPlugin->Detached, pPlugin);
1673 if (error != CHANNEL_RC_OK)
1674 {
1675 WLog_Print(drdynvc->log, WLOG_ERROR, "Detach failed with error %" PRIu32 "!", error);
1676 goto fail;
1677 }
1678 }
1679
1680 fail:
1681 ArrayList_Unlock(dvcman->plugins);
1682
1683 return error;
1684 }
1685
drdynvc_virtual_channel_init_event_ex(LPVOID lpUserParam,LPVOID pInitHandle,UINT event,LPVOID pData,UINT dataLength)1686 static VOID VCAPITYPE drdynvc_virtual_channel_init_event_ex(LPVOID lpUserParam, LPVOID pInitHandle,
1687 UINT event, LPVOID pData,
1688 UINT dataLength)
1689 {
1690 UINT error = CHANNEL_RC_OK;
1691 drdynvcPlugin* drdynvc = (drdynvcPlugin*)lpUserParam;
1692
1693 if (!drdynvc || (drdynvc->InitHandle != pInitHandle))
1694 {
1695 WLog_ERR(TAG, "drdynvc_virtual_channel_init_event: error no match");
1696 return;
1697 }
1698
1699 switch (event)
1700 {
1701 case CHANNEL_EVENT_INITIALIZED:
1702 error = drdynvc_virtual_channel_event_initialized(drdynvc, pData, dataLength);
1703 break;
1704 case CHANNEL_EVENT_CONNECTED:
1705 if ((error = drdynvc_virtual_channel_event_connected(drdynvc, pData, dataLength)))
1706 WLog_Print(drdynvc->log, WLOG_ERROR,
1707 "drdynvc_virtual_channel_event_connected failed with error %" PRIu32 "",
1708 error);
1709
1710 break;
1711
1712 case CHANNEL_EVENT_DISCONNECTED:
1713 if ((error = drdynvc_virtual_channel_event_disconnected(drdynvc)))
1714 WLog_Print(drdynvc->log, WLOG_ERROR,
1715 "drdynvc_virtual_channel_event_disconnected failed with error %" PRIu32
1716 "",
1717 error);
1718
1719 break;
1720
1721 case CHANNEL_EVENT_TERMINATED:
1722 if ((error = drdynvc_virtual_channel_event_terminated(drdynvc)))
1723 WLog_Print(drdynvc->log, WLOG_ERROR,
1724 "drdynvc_virtual_channel_event_terminated failed with error %" PRIu32 "",
1725 error);
1726
1727 break;
1728
1729 case CHANNEL_EVENT_ATTACHED:
1730 if ((error = drdynvc_virtual_channel_event_attached(drdynvc)))
1731 WLog_Print(drdynvc->log, WLOG_ERROR,
1732 "drdynvc_virtual_channel_event_attached failed with error %" PRIu32 "",
1733 error);
1734
1735 break;
1736
1737 case CHANNEL_EVENT_DETACHED:
1738 if ((error = drdynvc_virtual_channel_event_detached(drdynvc)))
1739 WLog_Print(drdynvc->log, WLOG_ERROR,
1740 "drdynvc_virtual_channel_event_detached failed with error %" PRIu32 "",
1741 error);
1742
1743 break;
1744
1745 default:
1746 break;
1747 }
1748
1749 if (error && drdynvc->rdpcontext)
1750 setChannelError(drdynvc->rdpcontext, error,
1751 "drdynvc_virtual_channel_init_event_ex reported an error");
1752 }
1753
1754 /**
1755 * Channel Client Interface
1756 */
1757
drdynvc_get_version(DrdynvcClientContext * context)1758 static int drdynvc_get_version(DrdynvcClientContext* context)
1759 {
1760 drdynvcPlugin* drdynvc = (drdynvcPlugin*)context->handle;
1761 return drdynvc->version;
1762 }
1763
1764 /* drdynvc is always built-in */
1765 #define VirtualChannelEntryEx drdynvc_VirtualChannelEntryEx
1766
VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS_EX pEntryPoints,PVOID pInitHandle)1767 BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS_EX pEntryPoints, PVOID pInitHandle)
1768 {
1769 UINT rc;
1770 drdynvcPlugin* drdynvc;
1771 DrdynvcClientContext* context = NULL;
1772 CHANNEL_ENTRY_POINTS_FREERDP_EX* pEntryPointsEx;
1773 drdynvc = (drdynvcPlugin*)calloc(1, sizeof(drdynvcPlugin));
1774
1775 if (!drdynvc)
1776 {
1777 WLog_ERR(TAG, "calloc failed!");
1778 return FALSE;
1779 }
1780
1781 drdynvc->channelDef.options =
1782 CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP | CHANNEL_OPTION_COMPRESS_RDP;
1783 sprintf_s(drdynvc->channelDef.name, ARRAYSIZE(drdynvc->channelDef.name), "drdynvc");
1784 drdynvc->state = DRDYNVC_STATE_INITIAL;
1785 pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP_EX*)pEntryPoints;
1786
1787 if ((pEntryPointsEx->cbSize >= sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX)) &&
1788 (pEntryPointsEx->MagicNumber == FREERDP_CHANNEL_MAGIC_NUMBER))
1789 {
1790 context = (DrdynvcClientContext*)calloc(1, sizeof(DrdynvcClientContext));
1791
1792 if (!context)
1793 {
1794 WLog_Print(drdynvc->log, WLOG_ERROR, "calloc failed!");
1795 free(drdynvc);
1796 return FALSE;
1797 }
1798
1799 context->handle = (void*)drdynvc;
1800 context->custom = NULL;
1801 drdynvc->context = context;
1802 context->GetVersion = drdynvc_get_version;
1803 drdynvc->rdpcontext = pEntryPointsEx->context;
1804 }
1805
1806 drdynvc->log = WLog_Get(TAG);
1807 WLog_Print(drdynvc->log, WLOG_DEBUG, "VirtualChannelEntryEx");
1808 CopyMemory(&(drdynvc->channelEntryPoints), pEntryPoints,
1809 sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX));
1810 drdynvc->InitHandle = pInitHandle;
1811 rc = drdynvc->channelEntryPoints.pVirtualChannelInitEx(
1812 drdynvc, context, pInitHandle, &drdynvc->channelDef, 1, VIRTUAL_CHANNEL_VERSION_WIN2000,
1813 drdynvc_virtual_channel_init_event_ex);
1814
1815 if (CHANNEL_RC_OK != rc)
1816 {
1817 WLog_Print(drdynvc->log, WLOG_ERROR, "pVirtualChannelInit failed with %s [%08" PRIX32 "]",
1818 WTSErrorToString(rc), rc);
1819 free(drdynvc->context);
1820 free(drdynvc);
1821 return FALSE;
1822 }
1823
1824 drdynvc->channelEntryPoints.pInterface = context;
1825 return TRUE;
1826 }
1827