Coverage Report

Created: 2025-07-01 06:46

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