Coverage Report

Created: 2026-10-15 06:27

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