Coverage Report

Created: 2026-07-16 07:09

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