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