Coverage Report

Created: 2025-08-26 06:37

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