Coverage Report

Created: 2026-10-28 06:49

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/multitransport.h>
31
#if defined(WITH_RDPEUDP)
32
#include <freerdp/channels/channels.h>
33
#endif
34
#include <freerdp/channels/drdynvc.h>
35
#include <freerdp/utils/drdynvc.h>
36
#include <freerdp/codec/zgfx.h>
37
38
#include "drdynvc_main.h"
39
40
0
#define TAG CHANNELS_TAG("drdynvc.client")
41
42
/* drdynvc message queue ids */
43
typedef enum WINPR_C23_ENUM_TYPE(uint32_t)
44
{
45
  DRDYNVC_MSG_DATA = 0,
46
  DRDYNVC_MSG_TUNNEL_DATA = 1,
47
  DRDYNVC_MSG_TUNNEL_STATE = 2
48
} DRDYNVC_MSG_ID;
49
50
/* Tunnel data held back until the Soft-Sync Request arrives, see drdynvc_receive_tunnel_pdu */
51
0
#define DRDYNVC_MAX_TUNNEL_BACKLOG 4096
52
53
/* Distinct tunnel types a Soft-Sync Response can name */
54
0
#define DRDYNVC_MAX_SOFT_SYNC_TUNNELS 8
55
56
WINPR_ATTR_NODISCARD
57
static const char* channel_state2str(DVC_CHANNEL_STATE state)
58
0
{
59
0
  switch (state)
60
0
  {
61
0
    case DVC_CHANNEL_INIT:
62
0
      return "DVC_CHANNEL_INIT";
63
0
    case DVC_CHANNEL_RUNNING:
64
0
      return "DVC_CHANNEL_RUNNING";
65
0
    case DVC_CHANNEL_CLOSED:
66
0
      return "DVC_CHANNEL_CLOSED";
67
0
    default:
68
0
      return "DVC_CHANNEL_UNKNOWN";
69
0
  }
70
0
}
71
72
static void dvcman_channel_free(DVCMAN_CHANNEL* channel);
73
74
static UINT dvcman_channel_close(DVCMAN_CHANNEL* channel, BOOL perRequest, BOOL fromHashTableFn);
75
static void dvcman_free(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr);
76
77
WINPR_ATTR_NODISCARD
78
static UINT drdynvc_write_data(drdynvcPlugin* drdynvc, UINT32 ChannelId, UINT32 tunnelType,
79
                               const BYTE* data, UINT32 dataSize, BOOL* close,
80
                               DVCMAN_CHANNEL_STATS* stats);
81
82
WINPR_ATTR_NODISCARD
83
static UINT drdynvc_send(drdynvcPlugin* drdynvc, wStream* s, DVCMAN_CHANNEL_STATS* stats);
84
WINPR_ATTR_NODISCARD
85
static UINT drdynvc_send_on(drdynvcPlugin* drdynvc, wStream* s, DVCMAN_CHANNEL_STATS* stats,
86
                            UINT32* tunnelType, BOOL continuation);
87
88
static void dvcman_wtslistener_free(DVCMAN_LISTENER* listener)
89
0
{
90
0
  if (listener)
91
0
    free(listener->channel_name);
92
0
  free(listener);
93
0
}
94
95
/**
96
 * Function description
97
 *
98
 * @return 0 on success, otherwise a Win32 error code
99
 */
100
WINPR_ATTR_NODISCARD
101
static UINT dvcman_get_configuration(IWTSListener* pListener, void** ppPropertyBag)
102
0
{
103
0
  WINPR_ASSERT(ppPropertyBag);
104
0
  WINPR_UNUSED(pListener);
105
0
  *ppPropertyBag = nullptr;
106
0
  return ERROR_INTERNAL_ERROR;
107
0
}
108
109
/**
110
 * Function description
111
 *
112
 * @return 0 on success, otherwise a Win32 error code
113
 */
114
WINPR_ATTR_NODISCARD
115
static UINT dvcman_create_listener(IWTSVirtualChannelManager* pChannelMgr,
116
                                   const char* pszChannelName, ULONG ulFlags,
117
                                   IWTSListenerCallback* pListenerCallback,
118
                                   IWTSListener** ppListener)
119
0
{
120
0
  DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
121
0
  DVCMAN_LISTENER* listener = nullptr;
122
123
0
  WINPR_ASSERT(dvcman);
124
0
  WLog_DBG(TAG, "create_listener: %" PRIuz ".%s.", HashTable_Count(dvcman->listeners) + 1,
125
0
           pszChannelName);
126
0
  listener = (DVCMAN_LISTENER*)calloc(1, sizeof(DVCMAN_LISTENER));
127
128
0
  if (!listener)
129
0
  {
130
0
    WLog_ERR(TAG, "calloc failed!");
131
0
    return CHANNEL_RC_NO_MEMORY;
132
0
  }
133
134
0
  listener->iface.GetConfiguration = dvcman_get_configuration;
135
0
  listener->iface.pInterface = nullptr;
136
0
  listener->dvcman = dvcman;
137
0
  listener->channel_name = _strdup(pszChannelName);
138
139
0
  if (!listener->channel_name)
140
0
  {
141
0
    WLog_ERR(TAG, "_strdup failed!");
142
0
    dvcman_wtslistener_free(listener);
143
0
    return CHANNEL_RC_NO_MEMORY;
144
0
  }
145
146
0
  listener->flags = ulFlags;
147
0
  listener->listener_callback = pListenerCallback;
148
149
0
  if (ppListener)
150
0
    *ppListener = (IWTSListener*)listener;
151
152
0
  if (!HashTable_Insert(dvcman->listeners, listener->channel_name, listener))
153
0
  {
154
0
    dvcman_wtslistener_free(listener);
155
0
    return ERROR_INTERNAL_ERROR;
156
0
  }
157
158
  // NOLINTNEXTLINE(clang-analyzer-unix.Malloc): HashTable_Insert takes ownership of listener
159
0
  return CHANNEL_RC_OK;
160
0
}
161
162
WINPR_ATTR_NODISCARD
163
static UINT dvcman_destroy_listener(IWTSVirtualChannelManager* pChannelMgr, IWTSListener* pListener)
164
0
{
165
0
  DVCMAN_LISTENER* listener = (DVCMAN_LISTENER*)pListener;
166
167
0
  WINPR_UNUSED(pChannelMgr);
168
169
0
  if (listener)
170
0
  {
171
0
    DVCMAN* dvcman = listener->dvcman;
172
0
    if (dvcman)
173
0
      HashTable_Remove(dvcman->listeners, listener->channel_name);
174
0
  }
175
176
0
  return CHANNEL_RC_OK;
177
0
}
178
179
/**
180
 * Function description
181
 *
182
 * @return 0 on success, otherwise a Win32 error code
183
 */
184
WINPR_ATTR_NODISCARD
185
static UINT dvcman_register_plugin(IDRDYNVC_ENTRY_POINTS* pEntryPoints, const char* name,
186
                                   IWTSPlugin* pPlugin)
187
0
{
188
0
  WINPR_ASSERT(pEntryPoints);
189
0
  DVCMAN* dvcman = ((DVCMAN_ENTRY_POINTS*)pEntryPoints)->dvcman;
190
191
0
  WINPR_ASSERT(dvcman);
192
0
  if (!ArrayList_Append(dvcman->plugin_names, name))
193
0
    return ERROR_INTERNAL_ERROR;
194
0
  if (!ArrayList_Append(dvcman->plugins, pPlugin))
195
0
    return ERROR_INTERNAL_ERROR;
196
197
0
  WLog_DBG(TAG, "register_plugin: num_plugins %" PRIuz, ArrayList_Count(dvcman->plugins));
198
0
  return CHANNEL_RC_OK;
199
0
}
200
201
WINPR_ATTR_NODISCARD
202
static IWTSPlugin* dvcman_get_plugin(IDRDYNVC_ENTRY_POINTS* pEntryPoints, const char* name)
203
0
{
204
0
  IWTSPlugin* plugin = nullptr;
205
0
  size_t nc = 0;
206
0
  size_t pc = 0;
207
0
  WINPR_ASSERT(pEntryPoints);
208
0
  DVCMAN* dvcman = ((DVCMAN_ENTRY_POINTS*)pEntryPoints)->dvcman;
209
0
  if (!dvcman || !pEntryPoints || !name)
210
0
    return nullptr;
211
212
0
  nc = ArrayList_Count(dvcman->plugin_names);
213
0
  pc = ArrayList_Count(dvcman->plugins);
214
0
  if (nc != pc)
215
0
    return nullptr;
216
217
0
  ArrayList_Lock(dvcman->plugin_names);
218
0
  ArrayList_Lock(dvcman->plugins);
219
0
  for (size_t i = 0; i < pc; i++)
220
0
  {
221
0
    const char* cur = ArrayList_GetItem(dvcman->plugin_names, i);
222
0
    if (strcmp(cur, name) == 0)
223
0
    {
224
0
      plugin = ArrayList_GetItem(dvcman->plugins, i);
225
0
      break;
226
0
    }
227
0
  }
228
0
  ArrayList_Unlock(dvcman->plugin_names);
229
0
  ArrayList_Unlock(dvcman->plugins);
230
0
  return plugin;
231
0
}
232
233
WINPR_ATTR_NODISCARD
234
static const ADDIN_ARGV* dvcman_get_plugin_data(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
235
0
{
236
0
  WINPR_ASSERT(pEntryPoints);
237
0
  return ((DVCMAN_ENTRY_POINTS*)pEntryPoints)->args;
238
0
}
239
240
WINPR_ATTR_NODISCARD
241
static rdpContext* dvcman_get_rdp_context(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
242
0
{
243
0
  DVCMAN_ENTRY_POINTS* entry = (DVCMAN_ENTRY_POINTS*)pEntryPoints;
244
0
  WINPR_ASSERT(entry);
245
0
  return entry->context;
246
0
}
247
248
WINPR_ATTR_NODISCARD
249
static rdpSettings* dvcman_get_rdp_settings(IDRDYNVC_ENTRY_POINTS* pEntryPoints)
250
0
{
251
0
  rdpContext* context = dvcman_get_rdp_context(pEntryPoints);
252
0
  WINPR_ASSERT(context);
253
254
0
  return context->settings;
255
0
}
256
257
WINPR_ATTR_NODISCARD
258
static UINT32 dvcman_get_channel_id(IWTSVirtualChannel* channel)
259
0
{
260
0
  DVCMAN_CHANNEL* dvc = (DVCMAN_CHANNEL*)channel;
261
0
  WINPR_ASSERT(dvc);
262
0
  return dvc->channel_id;
263
0
}
264
265
WINPR_ATTR_NODISCARD
266
static const char* dvcman_get_channel_name(IWTSVirtualChannel* channel)
267
0
{
268
0
  DVCMAN_CHANNEL* dvc = (DVCMAN_CHANNEL*)channel;
269
0
  WINPR_ASSERT(dvc);
270
0
  return dvc->channel_name;
271
0
}
272
273
WINPR_ATTR_NODISCARD
274
static DVCMAN_CHANNEL* dvcman_get_channel_by_id(IWTSVirtualChannelManager* pChannelMgr,
275
                                                UINT32 ChannelId, BOOL doRef)
276
0
{
277
0
  DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
278
0
  DVCMAN_CHANNEL* dvcChannel = nullptr;
279
280
0
  WINPR_ASSERT(dvcman);
281
0
  HashTable_Lock(dvcman->channelsById);
282
0
  dvcChannel = HashTable_GetItemValue(dvcman->channelsById, &ChannelId);
283
0
  if (dvcChannel)
284
0
  {
285
0
    if (doRef)
286
0
      InterlockedIncrement(&dvcChannel->refCounter);
287
0
  }
288
0
  else
289
0
    HashTable_Unlock(dvcman->channelsById);
290
291
0
  return dvcChannel;
292
0
}
293
294
static void dvcman_return_channel(IWTSVirtualChannelManager* pChannelMgr, DVCMAN_CHANNEL* channel)
295
0
{
296
0
  DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
297
298
0
  if (!channel)
299
0
    return;
300
301
0
  WINPR_ASSERT(dvcman);
302
0
  HashTable_Unlock(dvcman->channelsById);
303
0
}
304
305
WINPR_ATTR_NODISCARD
306
static IWTSVirtualChannel* dvcman_find_channel_by_id(IWTSVirtualChannelManager* pChannelMgr,
307
                                                     UINT32 ChannelId)
308
0
{
309
0
  DVCMAN_CHANNEL* channel = dvcman_get_channel_by_id(pChannelMgr, ChannelId, FALSE);
310
0
  if (!channel)
311
0
    return nullptr;
312
0
  dvcman_return_channel(pChannelMgr, channel);
313
314
0
  return &channel->iface;
315
0
}
316
317
static void dvcman_plugin_terminate(void* plugin)
318
0
{
319
0
  IWTSPlugin* pPlugin = plugin;
320
321
0
  WINPR_ASSERT(pPlugin);
322
0
  UINT error = IFCALLRESULT(CHANNEL_RC_OK, pPlugin->Terminated, pPlugin);
323
0
  if (error != CHANNEL_RC_OK)
324
0
    WLog_ERR(TAG, "Terminated failed with error %" PRIu32 "!", error);
325
0
}
326
327
static void wts_listener_free(void* arg)
328
0
{
329
0
  DVCMAN_LISTENER* listener = (DVCMAN_LISTENER*)arg;
330
0
  dvcman_wtslistener_free(listener);
331
0
}
332
333
WINPR_ATTR_NODISCARD
334
static BOOL channelIdMatch(const void* k1, const void* k2)
335
0
{
336
0
  WINPR_ASSERT(k1);
337
0
  WINPR_ASSERT(k2);
338
0
  return *((const UINT32*)k1) == *((const UINT32*)k2);
339
0
}
340
341
WINPR_ATTR_NODISCARD
342
static UINT32 channelIdHash(const void* id)
343
0
{
344
0
  WINPR_ASSERT(id);
345
0
  return *((const UINT32*)id);
346
0
}
347
348
static void channelByIdCleanerFn(void* value)
349
0
{
350
0
  DVCMAN_CHANNEL* channel = (DVCMAN_CHANNEL*)value;
351
0
  if (channel)
352
0
  {
353
0
    dvcman_channel_close(channel, FALSE, TRUE);
354
0
    dvcman_channel_free(channel);
355
0
  }
356
0
}
357
358
WINPR_ATTR_MALLOC(dvcman_free, 2)
359
static IWTSVirtualChannelManager* dvcman_new(drdynvcPlugin* plugin)
360
0
{
361
0
  wObject* obj = nullptr;
362
0
  DVCMAN* dvcman = (DVCMAN*)calloc(1, sizeof(DVCMAN));
363
364
0
  if (!dvcman)
365
0
    return nullptr;
366
367
0
  dvcman->iface.CreateListener = dvcman_create_listener;
368
0
  dvcman->iface.DestroyListener = dvcman_destroy_listener;
369
0
  dvcman->iface.FindChannelById = dvcman_find_channel_by_id;
370
0
  dvcman->iface.GetChannelId = dvcman_get_channel_id;
371
0
  dvcman->iface.GetChannelName = dvcman_get_channel_name;
372
0
  dvcman->drdynvc = plugin;
373
0
  dvcman->channelsById = HashTable_New(TRUE);
374
375
0
  if (!dvcman->channelsById)
376
0
    goto fail;
377
378
0
  if (!HashTable_SetHashFunction(dvcman->channelsById, channelIdHash))
379
0
    goto fail;
380
381
0
  obj = HashTable_KeyObject(dvcman->channelsById);
382
0
  WINPR_ASSERT(obj);
383
0
  obj->fnObjectEquals = channelIdMatch;
384
385
0
  obj = HashTable_ValueObject(dvcman->channelsById);
386
0
  WINPR_ASSERT(obj);
387
0
  obj->fnObjectFree = channelByIdCleanerFn;
388
389
0
  dvcman->pool = StreamPool_New(TRUE, 10);
390
0
  if (!dvcman->pool)
391
0
    goto fail;
392
393
0
  dvcman->listeners = HashTable_New(TRUE);
394
0
  if (!dvcman->listeners)
395
0
    goto fail;
396
397
0
  if (!HashTable_SetHashFunction(dvcman->listeners, HashTable_StringHash))
398
0
    goto fail;
399
400
0
  obj = HashTable_KeyObject(dvcman->listeners);
401
0
  obj->fnObjectEquals = HashTable_StringCompare;
402
403
0
  obj = HashTable_ValueObject(dvcman->listeners);
404
0
  obj->fnObjectFree = wts_listener_free;
405
406
0
  dvcman->plugin_names = ArrayList_New(TRUE);
407
0
  if (!dvcman->plugin_names)
408
0
    goto fail;
409
0
  obj = ArrayList_Object(dvcman->plugin_names);
410
0
  obj->fnObjectNew = winpr_ObjectStringClone;
411
0
  obj->fnObjectFree = winpr_ObjectStringFree;
412
413
0
  dvcman->plugins = ArrayList_New(TRUE);
414
0
  if (!dvcman->plugins)
415
0
    goto fail;
416
0
  obj = ArrayList_Object(dvcman->plugins);
417
0
  obj->fnObjectFree = dvcman_plugin_terminate;
418
0
  return &dvcman->iface;
419
0
fail:
420
0
  dvcman_free(plugin, &dvcman->iface);
421
0
  return nullptr;
422
0
}
423
424
/**
425
 * Function description
426
 *
427
 * @return 0 on success, otherwise a Win32 error code
428
 */
429
WINPR_ATTR_NODISCARD
430
static UINT dvcman_load_addin(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr,
431
                              const ADDIN_ARGV* args, rdpContext* context)
432
0
{
433
0
  WINPR_ASSERT(drdynvc);
434
0
  WINPR_ASSERT(pChannelMgr);
435
0
  WINPR_ASSERT(args);
436
0
  WINPR_ASSERT(context);
437
438
0
  WLog_Print(drdynvc->log, WLOG_INFO, "Loading Dynamic Virtual Channel %s", args->argv[0]);
439
440
0
  PVIRTUALCHANNELENTRY pvce = freerdp_load_channel_addin_entry(args->argv[0], nullptr, nullptr,
441
0
                                                               FREERDP_ADDIN_CHANNEL_DYNAMIC);
442
0
  PDVC_PLUGIN_ENTRY pDVCPluginEntry = WINPR_FUNC_PTR_CAST(pvce, PDVC_PLUGIN_ENTRY);
443
444
0
  if (pDVCPluginEntry)
445
0
  {
446
0
    DVCMAN_ENTRY_POINTS entryPoints = WINPR_C_ARRAY_INIT;
447
448
0
    entryPoints.iface.RegisterPlugin = dvcman_register_plugin;
449
0
    entryPoints.iface.GetPlugin = dvcman_get_plugin;
450
0
    entryPoints.iface.GetPluginData = dvcman_get_plugin_data;
451
0
    entryPoints.iface.GetRdpSettings = dvcman_get_rdp_settings;
452
0
    entryPoints.iface.GetRdpContext = dvcman_get_rdp_context;
453
0
    entryPoints.dvcman = (DVCMAN*)pChannelMgr;
454
0
    entryPoints.args = args;
455
0
    entryPoints.context = context;
456
0
    return pDVCPluginEntry(&entryPoints.iface);
457
0
  }
458
459
0
  return ERROR_INVALID_FUNCTION;
460
0
}
461
462
static void dvcman_channel_free(DVCMAN_CHANNEL* channel)
463
0
{
464
0
  if (!channel)
465
0
    return;
466
467
0
  if (channel->dvcman)
468
0
  {
469
0
    drdynvcPlugin* plugin = channel->dvcman->drdynvc;
470
0
    if (plugin)
471
0
    {
472
0
      rdpContext* context = plugin->rdpcontext;
473
0
      if (context)
474
0
      {
475
0
        ChannelTerminatedEventArgs e = WINPR_C_ARRAY_INIT;
476
0
        EventArgsInit(&e, "freerdp");
477
0
        e.name = channel->channel_name;
478
0
        e.pInterface = channel->pInterface;
479
480
0
        const int rc = PubSub_OnChannelTerminated(context->pubSub, context, &e);
481
0
        if (rc < 0)
482
0
          WLog_WARN(TAG, "PubSub_OnChannelTerminated(%s) failed", channel->channel_name);
483
0
      }
484
0
    }
485
0
  }
486
487
0
  if (channel->dvc_data)
488
0
    Stream_Release(channel->dvc_data);
489
490
0
  zgfx_context_free(channel->decompressor);
491
0
  DeleteCriticalSection(&(channel->lock));
492
0
  free(channel->channel_name);
493
0
  free(channel);
494
0
}
495
496
static void dvcman_channel_unref(DVCMAN_CHANNEL* channel)
497
0
{
498
0
  WINPR_ASSERT(channel);
499
0
  if (InterlockedDecrement(&channel->refCounter))
500
0
    return;
501
502
0
  DVCMAN* dvcman = channel->dvcman;
503
0
  if (dvcman)
504
0
    HashTable_Remove(dvcman->channelsById, &channel->channel_id);
505
0
}
506
507
WINPR_ATTR_NODISCARD
508
static UINT32 dvcman_channel_get_tunnel(DVCMAN_CHANNEL* channel)
509
0
{
510
0
  WINPR_ASSERT(channel);
511
0
  EnterCriticalSection(&channel->lock);
512
0
  const UINT32 tunnelType = channel->tunnelType;
513
0
  LeaveCriticalSection(&channel->lock);
514
0
  return tunnelType;
515
0
}
516
517
/* Waits for a message the channel is writing to be out, see DVCMAN_CHANNEL::tunnelType. */
518
static void dvcman_channel_set_tunnel(DVCMAN_CHANNEL* channel, UINT32 tunnelType)
519
0
{
520
0
  WINPR_ASSERT(channel);
521
0
  EnterCriticalSection(&channel->lock);
522
0
  channel->tunnelType = tunnelType;
523
0
  LeaveCriticalSection(&channel->lock);
524
0
}
525
526
WINPR_ATTR_NODISCARD
527
static UINT dvcchannel_send_close(DVCMAN_CHANNEL* channel)
528
0
{
529
0
  WINPR_ASSERT(channel);
530
0
  DVCMAN* dvcman = channel->dvcman;
531
0
  drdynvcPlugin* drdynvc = dvcman->drdynvc;
532
0
  wStream* s = StreamPool_Take(dvcman->pool, 5);
533
534
0
  if (!s)
535
0
  {
536
0
    WLog_Print(drdynvc->log, WLOG_ERROR, "StreamPool_Take failed!");
537
0
    return CHANNEL_RC_NO_MEMORY;
538
0
  }
539
540
0
  Stream_Write_UINT8(s, (CLOSE_REQUEST_PDU << 4) | 0x02);
541
0
  Stream_Write_UINT32(s, channel->channel_id);
542
0
  UINT32 tunnelType = dvcman_channel_get_tunnel(channel);
543
0
  return drdynvc_send_on(drdynvc, s, &channel->stats, &tunnelType, FALSE);
544
0
}
545
546
static void check_open_close_receive(DVCMAN_CHANNEL* channel)
547
0
{
548
0
  WINPR_ASSERT(channel);
549
550
0
  IWTSVirtualChannelCallback* cb = channel->channel_callback;
551
0
  const char* name = channel->channel_name;
552
0
  const UINT32 id = channel->channel_id;
553
554
0
  WINPR_ASSERT(cb);
555
0
  if (!cb->OnOpen || !cb->OnClose || !cb->OnDataReceived)
556
0
    WLog_VRB(TAG, "{%s:%" PRIu32 "} OnOpen=%p, OnClose=%p, OnDataReceived=%p", name, id,
557
0
             WINPR_FUNC_PTR_CAST(cb->OnOpen, const void*),
558
0
             WINPR_FUNC_PTR_CAST(cb->OnClose, const void*),
559
0
             WINPR_FUNC_PTR_CAST(cb->OnDataReceived, const void*));
560
0
}
561
562
WINPR_ATTR_NODISCARD
563
static UINT dvcman_call_on_receive(DVCMAN_CHANNEL* channel, wStream* data)
564
0
{
565
0
  WINPR_ASSERT(channel);
566
0
  WINPR_ASSERT(data);
567
568
0
  channel->stats.packetsIn++;
569
570
0
  IWTSVirtualChannelCallback* cb = channel->channel_callback;
571
0
  WINPR_ASSERT(cb);
572
573
0
  check_open_close_receive(channel);
574
0
  WINPR_ASSERT(cb->OnDataReceived);
575
0
  return cb->OnDataReceived(cb, data);
576
0
}
577
578
static UINT dvcman_channel_close(DVCMAN_CHANNEL* channel, BOOL perRequest, BOOL fromHashTableFn)
579
0
{
580
0
  UINT error = CHANNEL_RC_OK;
581
0
  DrdynvcClientContext* context = nullptr;
582
583
0
  WINPR_ASSERT(channel);
584
0
  switch (channel->state)
585
0
  {
586
0
    case DVC_CHANNEL_INIT:
587
0
      break;
588
0
    case DVC_CHANNEL_RUNNING:
589
0
      if (channel->dvcman)
590
0
      {
591
0
        drdynvcPlugin* drdynvc = channel->dvcman->drdynvc;
592
0
        WINPR_ASSERT(drdynvc);
593
0
        context = drdynvc->context;
594
0
        if (perRequest)
595
0
          WLog_Print(drdynvc->log, WLOG_DEBUG, "sending close confirm for '%s'",
596
0
                     channel->channel_name);
597
598
0
        error = dvcchannel_send_close(channel);
599
0
        if (error != CHANNEL_RC_OK)
600
0
        {
601
0
          if (perRequest)
602
0
            WLog_Print(drdynvc->log, WLOG_DEBUG,
603
0
                       "error when sending closeRequest for '%s'",
604
0
                       channel->channel_name);
605
0
          else
606
0
            WLog_Print(drdynvc->log, WLOG_DEBUG,
607
0
                       "error when sending close confirm for '%s'",
608
0
                       channel->channel_name);
609
0
        }
610
0
        WLog_Print(drdynvc->log, WLOG_DEBUG, "listener %s destroyed channel %" PRIu32 "",
611
0
                   channel->channel_name, channel->channel_id);
612
0
      }
613
614
0
      channel->state = DVC_CHANNEL_CLOSED;
615
616
0
      {
617
0
        check_open_close_receive(channel);
618
619
0
        IWTSVirtualChannelCallback* cb = channel->channel_callback;
620
0
        channel->channel_callback = nullptr;
621
0
        if (cb)
622
0
          error = IFCALLRESULT(CHANNEL_RC_OK, cb->OnClose, cb);
623
0
      }
624
625
#if defined(WITH_RDPEUDP)
626
      /* not announced to the application, see dvcman_create_channel */
627
      if (channel->staticBridge)
628
        context = nullptr;
629
#endif
630
0
      if (channel->dvcman && channel->dvcman->drdynvc)
631
0
      {
632
0
        if (context)
633
0
        {
634
0
          IFCALLRET(context->OnChannelDisconnected, error, context, channel->channel_name,
635
0
                    channel->pInterface);
636
0
        }
637
0
      }
638
639
0
      if (!fromHashTableFn)
640
0
        dvcman_channel_unref(channel);
641
0
      break;
642
0
    case DVC_CHANNEL_CLOSED:
643
0
      break;
644
0
    default:
645
0
      break;
646
0
  }
647
648
0
  return error;
649
0
}
650
651
WINPR_ATTR_MALLOC(dvcman_channel_free, 1)
652
static DVCMAN_CHANNEL* dvcman_channel_new(drdynvcPlugin* drdynvc,
653
                                          IWTSVirtualChannelManager* pChannelMgr, UINT32 ChannelId,
654
                                          const char* ChannelName)
655
0
{
656
0
  WINPR_ASSERT(drdynvc);
657
0
  WINPR_ASSERT(pChannelMgr);
658
0
  DVCMAN_CHANNEL* channel = (DVCMAN_CHANNEL*)calloc(1, sizeof(DVCMAN_CHANNEL));
659
660
0
  if (!channel)
661
0
    return nullptr;
662
663
0
  channel->dvcman = (DVCMAN*)pChannelMgr;
664
0
  channel->channel_id = ChannelId;
665
0
  channel->refCounter = 1;
666
0
  channel->state = DVC_CHANNEL_INIT;
667
0
  channel->channel_name = _strdup(ChannelName);
668
0
  if (!channel->channel_name)
669
0
    goto fail;
670
671
0
  channel->decompressor = zgfx_context_new(FALSE);
672
0
  if (!channel->decompressor)
673
0
    goto fail;
674
675
0
  if (!InitializeCriticalSectionEx(&(channel->lock), 0, 0))
676
0
    goto fail;
677
678
0
  if (drdynvc)
679
0
  {
680
0
    rdpContext* context = drdynvc->rdpcontext;
681
0
    if (context)
682
0
    {
683
0
      ChannelInitializedEventArgs e = WINPR_C_ARRAY_INIT;
684
0
      EventArgsInit(&e, "freerdp");
685
0
      e.name = channel->channel_name;
686
0
      e.pInterface = channel->pInterface;
687
688
0
      const int rc = PubSub_OnChannelInitialized(context->pubSub, context, &e);
689
0
      if (rc < 0)
690
0
        WLog_WARN(TAG, "PubSub_OnChannelInitialized(%s) failed", channel->channel_name);
691
0
    }
692
0
  }
693
694
0
  return channel;
695
0
fail:
696
0
  dvcman_channel_free(channel);
697
0
  return nullptr;
698
0
}
699
700
static void dvcman_clear(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr)
701
0
{
702
0
  DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
703
704
0
  WINPR_ASSERT(dvcman);
705
0
  WINPR_UNUSED(drdynvc);
706
707
0
  HashTable_Clear(dvcman->channelsById);
708
0
  ArrayList_Clear(dvcman->plugins);
709
0
  ArrayList_Clear(dvcman->plugin_names);
710
0
  HashTable_Clear(dvcman->listeners);
711
0
}
712
713
static void dvcman_free(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr)
714
0
{
715
0
  DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
716
717
0
  WINPR_ASSERT(dvcman);
718
0
  WINPR_UNUSED(drdynvc);
719
720
0
  HashTable_Free(dvcman->channelsById);
721
0
  ArrayList_Free(dvcman->plugins);
722
0
  ArrayList_Free(dvcman->plugin_names);
723
0
  HashTable_Free(dvcman->listeners);
724
725
0
  StreamPool_Free(dvcman->pool);
726
0
  free(dvcman);
727
0
}
728
729
/**
730
 * Function description
731
 *
732
 * @return 0 on success, otherwise a Win32 error code
733
 */
734
WINPR_ATTR_NODISCARD
735
static UINT dvcman_init(drdynvcPlugin* drdynvc, IWTSVirtualChannelManager* pChannelMgr)
736
0
{
737
0
  DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
738
0
  UINT error = CHANNEL_RC_OK;
739
740
0
  WINPR_ASSERT(dvcman);
741
0
  ArrayList_Lock(dvcman->plugins);
742
0
  for (size_t i = 0; i < ArrayList_Count(dvcman->plugins); i++)
743
0
  {
744
0
    IWTSPlugin* pPlugin = ArrayList_GetItem(dvcman->plugins, i);
745
746
0
    error = IFCALLRESULT(CHANNEL_RC_OK, pPlugin->Initialize, pPlugin, pChannelMgr);
747
0
    if (error != CHANNEL_RC_OK)
748
0
    {
749
0
      WLog_Print(drdynvc->log, WLOG_ERROR, "Initialize failed with error %" PRIu32 "!",
750
0
                 error);
751
0
      goto fail;
752
0
    }
753
0
  }
754
755
0
fail:
756
0
  ArrayList_Unlock(dvcman->plugins);
757
0
  return error;
758
0
}
759
760
/**
761
 * Function description
762
 *
763
 * @return 0 on success, otherwise a Win32 error code
764
 */
765
WINPR_ATTR_NODISCARD
766
static UINT dvcman_write_channel(IWTSVirtualChannel* pChannel, ULONG cbSize, const BYTE* pBuffer,
767
                                 void* pReserved)
768
0
{
769
0
  BOOL close = FALSE;
770
0
  UINT status = 0;
771
0
  DVCMAN_CHANNEL* channel = (DVCMAN_CHANNEL*)pChannel;
772
773
0
  WINPR_UNUSED(pReserved);
774
0
  if (!channel || !channel->dvcman)
775
0
    return CHANNEL_RC_BAD_CHANNEL;
776
777
0
  EnterCriticalSection(&(channel->lock));
778
0
  status = drdynvc_write_data(channel->dvcman->drdynvc, channel->channel_id, channel->tunnelType,
779
0
                              pBuffer, cbSize, &close, &channel->stats);
780
0
  LeaveCriticalSection(&(channel->lock));
781
  /* Close delayed, it removes the channel struct */
782
0
  if (close)
783
0
    dvcman_channel_close(channel, FALSE, FALSE);
784
785
0
  return status;
786
0
}
787
788
/**
789
 * Function description
790
 *
791
 * @return 0 on success, otherwise a Win32 error code
792
 */
793
WINPR_ATTR_NODISCARD
794
static UINT dvcman_close_channel_iface(IWTSVirtualChannel* pChannel)
795
0
{
796
0
  DVCMAN_CHANNEL* channel = (DVCMAN_CHANNEL*)pChannel;
797
798
0
  if (!channel)
799
0
    return CHANNEL_RC_BAD_CHANNEL;
800
801
0
  WLog_DBG(TAG, "close_channel_iface: id=%" PRIu32 "", channel->channel_id);
802
0
  return dvcman_channel_close(channel, FALSE, FALSE);
803
0
}
804
805
struct stats_collector_argument
806
{
807
  DrdynvcClientChannelStat* stats;
808
  size_t count;
809
  size_t used;
810
};
811
812
WINPR_ATTR_NODISCARD
813
static BOOL stats_collector(WINPR_ATTR_UNUSED const void* key, void* value, void* arg)
814
0
{
815
0
  struct stats_collector_argument* args = arg;
816
0
  WINPR_ASSERT(args);
817
818
0
  DVCMAN_CHANNEL* channel = value;
819
0
  WINPR_ASSERT(channel);
820
0
  if (args->used >= args->count)
821
0
    return FALSE;
822
823
0
  DrdynvcClientChannelStat* stat = &args->stats[args->used++];
824
825
0
  if (channel->channel_name)
826
0
  {
827
0
    const size_t len = strnlen(channel->channel_name, ARRAYSIZE(stat->channelName) - 1);
828
0
    strncpy(stat->channelName, channel->channel_name, len);
829
0
  }
830
0
  else
831
0
    memset(stat->channelName, 0, sizeof(stat->channelName));
832
0
  stat->channelId = channel->channel_id;
833
0
  stat->bytesIn = channel->stats.bytesIn;
834
0
  stat->bytesOut = channel->stats.bytesOut;
835
0
  stat->fragmentsIn = channel->stats.fragmentsIn;
836
0
  stat->fragmentsOut = channel->stats.fragmentsOut;
837
0
  stat->packetsIn = channel->stats.packetsIn;
838
0
  stat->packetsOut = channel->stats.packetsOut;
839
0
  return TRUE;
840
0
}
841
842
WINPR_ATTR_MALLOC(free, 1)
843
static DrdynvcClientChannelStat* drdynvc_get_channel_stats(DrdynvcClientContext* context,
844
                                                           size_t* pCount)
845
0
{
846
0
  WINPR_ASSERT(context);
847
0
  WINPR_ASSERT(pCount);
848
849
0
  drdynvcPlugin* drdynvc = (drdynvcPlugin*)context->handle;
850
0
  WINPR_ASSERT(drdynvc);
851
852
0
  DVCMAN* dvcman = (DVCMAN*)drdynvc->channel_mgr;
853
0
  *pCount = 0;
854
0
  if (!dvcman)
855
0
    return nullptr;
856
857
0
  struct stats_collector_argument args = { .stats = nullptr, .count = 0 };
858
859
0
  HashTable_Lock(dvcman->channelsById);
860
0
  const size_t count = HashTable_Count(dvcman->channelsById);
861
0
  if (count > 0)
862
0
  {
863
0
    args.stats = calloc(count, sizeof(DrdynvcClientChannelStat));
864
0
    if (args.stats)
865
0
    {
866
0
      args.count = count;
867
0
      const BOOL rc = HashTable_Foreach(dvcman->channelsById, stats_collector, &args);
868
0
      if (!rc)
869
0
      {
870
0
        HashTable_Unlock(dvcman->channelsById);
871
0
        free(args.stats);
872
0
        return nullptr;
873
0
      }
874
0
    }
875
0
  }
876
0
  HashTable_Unlock(dvcman->channelsById);
877
0
  *pCount = args.used;
878
0
  return args.stats;
879
0
}
880
881
#if defined(WITH_RDPEUDP)
882
/* [MS-RDPEDYC] 3.1.5.4: a server tunnelling static virtual channel traffic opens a dynamic
883
 * channel with the name of a static one for it, and both ends then send and receive that static
884
 * channel's data on the dynamic channel. Servers have been seen doing so also when the client did
885
 * not advertise TRANSPORTTYPE_UDP_PREFERRED, so with UDP multitransport any such channel is
886
 * taken: what arrives on it is handed to the static channel's plugin as if it had come on the
887
 * static channel, and what the plugin writes goes out on it (freerdp_channels_set_static_bridge).
888
 * Windows 11 reads the clipboard's answers only there. Without UDP multitransport such a channel
889
 * is refused, as any channel without a listener. */
890
typedef struct
891
{
892
  IWTSVirtualChannelCallback iface;
893
  freerdp* instance;
894
  UINT16 staticChannelId;
895
  char staticChannelName[CHANNEL_NAME_LEN + 1];
896
897
  /* the custom of the sends it set; the static channel owns it, here it is only compared */
898
  void* target;
899
  /* target until dvcman_static_bridge_publish sets it, owned here until then */
900
  void* pending;
901
} DVCMAN_STATIC_BRIDGE;
902
903
/* What the static channel's sends are passed, see dvcman_static_bridge_send. */
904
typedef struct
905
{
906
  drdynvcPlugin* drdynvc;
907
  UINT32 channelId;
908
} DVCMAN_STATIC_BRIDGE_TARGET;
909
910
/* [MS-RDPEDYC] 3.1.5.4.2: the static channel's data is sent on the dynamic channel that carries
911
 * it. Called for each message the static channel's plugin writes; one for a dynamic channel that
912
 * closed since is discarded. */
913
WINPR_ATTR_NODISCARD
914
static BOOL dvcman_static_bridge_send(void* custom, const BYTE* data, size_t length)
915
{
916
  const DVCMAN_STATIC_BRIDGE_TARGET* target = custom;
917
  WINPR_ASSERT(target);
918
  WINPR_ASSERT(target->drdynvc);
919
920
  if (length > UINT32_MAX)
921
    return FALSE;
922
923
  /* the reference keeps the channel, the table is not held while it writes */
924
  IWTSVirtualChannelManager* mgr = target->drdynvc->channel_mgr;
925
  DVCMAN_CHANNEL* channel = dvcman_get_channel_by_id(mgr, target->channelId, TRUE);
926
  if (!channel)
927
    return TRUE;
928
  dvcman_return_channel(mgr, channel);
929
930
  UINT rc = CHANNEL_RC_OK;
931
  if ((channel->state == DVC_CHANNEL_RUNNING) && channel->staticBridge)
932
    rc = dvcman_write_channel(&channel->iface, (ULONG)length, data, nullptr);
933
  dvcman_channel_unref(channel);
934
  return rc == CHANNEL_RC_OK;
935
}
936
937
/* One chunk for the static channel, with the Channel PDU Header flags and total length it would
938
 * have had there. A plugin that closed its static channel takes no data, which is then
939
 * discarded ([MS-RDPEDYC] 3.1.5.4.5). */
940
static void dvcman_static_bridge_deliver(DVCMAN_STATIC_BRIDGE* bridge, const BYTE* data,
941
                                         size_t length, UINT32 flags, size_t totalLength)
942
{
943
  WINPR_ASSERT(bridge);
944
945
  if ((length > 0) && !freerdp_channels_data(bridge->instance, bridge->staticChannelId, data,
946
                                             length, flags, totalLength))
947
    WLog_DBG(TAG, "static channel %" PRIu16 " took no data, %" PRIuz " bytes discarded",
948
             bridge->staticChannelId, length);
949
}
950
951
/* dvcman_receive_channel_data hands a bridge its chunks without this, see
952
 * dvcman_static_bridge_receive; anything else is one whole message. */
953
WINPR_ATTR_NODISCARD
954
static UINT dvcman_static_bridge_on_data(IWTSVirtualChannelCallback* pChannelCallback,
955
                                         wStream* data)
956
{
957
  WINPR_ASSERT(data);
958
  const size_t length = Stream_GetRemainingLength(data);
959
  dvcman_static_bridge_deliver((DVCMAN_STATIC_BRIDGE*)pChannelCallback, Stream_ConstPointer(data),
960
                               length, CHANNEL_FLAG_FIRST | CHANNEL_FLAG_LAST, length);
961
  return CHANNEL_RC_OK;
962
}
963
964
/* [MS-RDPEDYC] 3.1.5.4.2: the data is not reassembled, each chunk of the data PDUs goes to the
965
 * static channel as it came so that its endpoint reassembles it. 3.1.5.4.4: CHANNEL_FLAG_FIRST
966
 * comes from a DATA_FIRST PDU or a DATA PDU with a whole message, CHANNEL_FLAG_LAST from the DATA
967
 * PDU that completes the message. */
968
WINPR_ATTR_NODISCARD
969
static UINT dvcman_static_bridge_receive(DVCMAN_CHANNEL* channel, wStream* data)
970
{
971
  WINPR_ASSERT(channel);
972
  WINPR_ASSERT(data);
973
974
  const size_t length = Stream_GetRemainingLength(data);
975
  UINT32 flags = CHANNEL_FLAG_FIRST | CHANNEL_FLAG_LAST;
976
  size_t totalLength = length;
977
978
  if (channel->bridgeFragmented)
979
  {
980
    if (length > channel->dvc_data_length - channel->bridgeReceived)
981
    {
982
      WLog_Print(channel->dvcman->drdynvc->log, WLOG_ERROR,
983
                 "data exceeding declared length!");
984
      channel->bridgeFragmented = FALSE;
985
      return ERROR_INVALID_DATA;
986
    }
987
988
    flags = (channel->bridgeReceived == 0) ? CHANNEL_FLAG_FIRST : 0;
989
    channel->bridgeReceived += (UINT32)length;
990
    totalLength = channel->dvc_data_length;
991
    channel->stats.fragmentsIn++;
992
    if (channel->bridgeReceived >= channel->dvc_data_length)
993
    {
994
      flags |= CHANNEL_FLAG_LAST;
995
      channel->bridgeFragmented = FALSE;
996
    }
997
  }
998
  if (flags & CHANNEL_FLAG_LAST)
999
    channel->stats.packetsIn++;
1000
1001
  dvcman_static_bridge_deliver((DVCMAN_STATIC_BRIDGE*)channel->channel_callback,
1002
                               Stream_ConstPointer(data), length, flags, totalLength);
1003
  return CHANNEL_RC_OK;
1004
}
1005
1006
WINPR_ATTR_NODISCARD
1007
static UINT dvcman_static_bridge_on_close(IWTSVirtualChannelCallback* pChannelCallback)
1008
{
1009
  DVCMAN_STATIC_BRIDGE* bridge = (DVCMAN_STATIC_BRIDGE*)pChannelCallback;
1010
  WINPR_ASSERT(bridge);
1011
1012
  /* the static channel sends on itself again (3.1.5.4.5), unless it is gone already or another
1013
   * dynamic channel carries it since */
1014
  if (bridge->target &&
1015
      !freerdp_channels_set_static_bridge(bridge->instance, bridge->staticChannelName, nullptr,
1016
                                          nullptr, bridge->target))
1017
    WLog_DBG(TAG, "static channel %s is gone already", bridge->staticChannelName);
1018
  free(bridge->pending);
1019
  free(bridge);
1020
  return CHANNEL_RC_OK;
1021
}
1022
1023
/* [MS-RDPEDYC] 3.1.5.4.2: the static channel's sends move to the dynamic channel once the
1024
 * Create Response is out, so none reaches the server ahead of it. */
1025
static void dvcman_static_bridge_publish(drdynvcPlugin* drdynvc, DVCMAN_CHANNEL* channel)
1026
{
1027
  WINPR_ASSERT(drdynvc);
1028
  WINPR_ASSERT(channel);
1029
1030
  if (!channel->staticBridge)
1031
    return;
1032
  DVCMAN_STATIC_BRIDGE* bridge = (DVCMAN_STATIC_BRIDGE*)channel->channel_callback;
1033
  WINPR_ASSERT(bridge);
1034
  void* target = bridge->pending;
1035
  bridge->pending = nullptr;
1036
  if (target && freerdp_channels_set_static_bridge(bridge->instance, bridge->staticChannelName,
1037
                                                   dvcman_static_bridge_send, free, target))
1038
    bridge->target = target;
1039
  else
1040
  {
1041
    WLog_Print(drdynvc->log, WLOG_WARN,
1042
               "static channel %s goes on sending on itself, the server may not read it",
1043
               bridge->staticChannelName);
1044
    free(target);
1045
  }
1046
}
1047
1048
/* A bridge for a dynamic channel named like a joined static channel, nullptr for any other. */
1049
WINPR_ATTR_NODISCARD
1050
static DVCMAN_STATIC_BRIDGE* dvcman_static_bridge_new(drdynvcPlugin* drdynvc,
1051
                                                      const char* ChannelName)
1052
{
1053
  WINPR_ASSERT(drdynvc);
1054
  WINPR_ASSERT(ChannelName);
1055
1056
  /* the channel that carries the dynamic ones stays static (3.1.5.4.2) */
1057
  if (_stricmp(ChannelName, DRDYNVC_SVC_CHANNEL_NAME) == 0)
1058
    return nullptr;
1059
  if (!drdynvc->rdpcontext || !drdynvc->rdpcontext->instance)
1060
    return nullptr;
1061
1062
  /* only along with UDP multitransport: Soft-Sync, which the tunnelling goes with (3.1.5.4.1),
1063
   * is negotiated when both ends do it */
1064
  const rdpSettings* settings = drdynvc->rdpcontext->settings;
1065
  if (!settings || !freerdp_settings_get_bool(settings, FreeRDP_SupportMultitransport) ||
1066
      ((freerdp_settings_get_uint32(settings, FreeRDP_MultitransportFlags) &
1067
        SOFTSYNC_TCP_TO_UDP) == 0))
1068
    return nullptr;
1069
1070
  freerdp* instance = drdynvc->rdpcontext->instance;
1071
  const UINT16 id = freerdp_channels_get_id_by_name(instance, ChannelName);
1072
  if (id == 0)
1073
    return nullptr;
1074
1075
  DVCMAN_STATIC_BRIDGE* bridge = calloc(1, sizeof(DVCMAN_STATIC_BRIDGE));
1076
  if (!bridge)
1077
    return nullptr;
1078
  bridge->iface.OnDataReceived = dvcman_static_bridge_on_data;
1079
  bridge->iface.OnClose = dvcman_static_bridge_on_close;
1080
  bridge->instance = instance;
1081
  bridge->staticChannelId = id;
1082
  (void)_snprintf(bridge->staticChannelName, sizeof(bridge->staticChannelName), "%s",
1083
                  ChannelName);
1084
  return bridge;
1085
}
1086
#endif
1087
1088
/**
1089
 * Function description
1090
 *
1091
 * @return 0 on success, otherwise a Win32 error code
1092
 */
1093
WINPR_ATTR_NODISCARD
1094
static DVCMAN_CHANNEL* dvcman_create_channel(drdynvcPlugin* drdynvc,
1095
                                             IWTSVirtualChannelManager* pChannelMgr,
1096
                                             UINT32 ChannelId, const char* ChannelName, UINT* res)
1097
0
{
1098
0
  BOOL bAccept = 0;
1099
0
  DrdynvcClientContext* context = nullptr;
1100
0
  DVCMAN* dvcman = (DVCMAN*)pChannelMgr;
1101
0
  IWTSVirtualChannelCallback* pCallback = nullptr;
1102
1103
0
  WINPR_ASSERT(dvcman);
1104
0
  WINPR_ASSERT(res);
1105
1106
0
  HashTable_Lock(dvcman->listeners);
1107
  /* Refuse channels without a listener before registering anything, so a refused
1108
   * ChannelId leaves no state behind and the server may reuse it. */
1109
0
  DVCMAN_LISTENER* listener =
1110
0
      (DVCMAN_LISTENER*)HashTable_GetItemValue(dvcman->listeners, ChannelName);
1111
#if defined(WITH_RDPEUDP)
1112
  /* or a static channel to carry */
1113
  DVCMAN_STATIC_BRIDGE* bridge =
1114
      listener ? nullptr : dvcman_static_bridge_new(drdynvc, ChannelName);
1115
  if (!listener && !bridge)
1116
#else
1117
0
  if (!listener)
1118
0
#endif
1119
0
  {
1120
0
    HashTable_Unlock(dvcman->listeners);
1121
0
    *res = ERROR_NOT_FOUND;
1122
0
    return nullptr;
1123
0
  }
1124
1125
0
  DVCMAN_CHANNEL* channel = dvcman_get_channel_by_id(pChannelMgr, ChannelId, FALSE);
1126
0
  if (channel)
1127
0
  {
1128
0
    switch (channel->state)
1129
0
    {
1130
0
      case DVC_CHANNEL_RUNNING:
1131
0
        WLog_Print(drdynvc->log, WLOG_ERROR,
1132
0
                   "Protocol error: Duplicated ChannelId %" PRIu32 " (%s)!", ChannelId,
1133
0
                   ChannelName);
1134
0
        *res = CHANNEL_RC_ALREADY_OPEN;
1135
0
        goto out;
1136
1137
0
      case DVC_CHANNEL_CLOSED:
1138
0
      case DVC_CHANNEL_INIT:
1139
0
      default:
1140
0
      {
1141
0
        WLog_Print(drdynvc->log, WLOG_ERROR, "not expecting a createChannel from state %s",
1142
0
                   channel_state2str(channel->state));
1143
0
        *res = CHANNEL_RC_INITIALIZATION_ERROR;
1144
0
        goto out;
1145
0
      }
1146
0
    }
1147
0
  }
1148
0
  else
1149
0
  {
1150
0
    HashTable_Lock(dvcman->channelsById);
1151
0
    channel = dvcman_channel_new(drdynvc, pChannelMgr, ChannelId, ChannelName);
1152
0
    if (!channel)
1153
0
    {
1154
0
      WLog_Print(drdynvc->log, WLOG_ERROR, "dvcman_channel_new failed!");
1155
0
      *res = CHANNEL_RC_NO_MEMORY;
1156
0
      goto out;
1157
0
    }
1158
0
  }
1159
1160
0
  if (!HashTable_Insert(dvcman->channelsById, &channel->channel_id, channel))
1161
0
  {
1162
0
    WLog_Print(drdynvc->log, WLOG_ERROR, "unable to register channel in our channel list");
1163
0
    *res = ERROR_INTERNAL_ERROR;
1164
0
    dvcman_channel_free(channel);
1165
0
    channel = nullptr;
1166
0
    goto out;
1167
0
  }
1168
1169
0
  channel->iface.Write = dvcman_write_channel;
1170
0
  channel->iface.Close = dvcman_close_channel_iface;
1171
0
  bAccept = TRUE;
1172
1173
#if defined(WITH_RDPEUDP)
1174
  if (bridge)
1175
  {
1176
    /* No OnChannelConnected: clients take the pInterface of a channel named like a static
1177
     * one for that channel's interface, and a bridge has none. */
1178
    WLog_Print(drdynvc->log, WLOG_INFO,
1179
               "dynamic channel %" PRIu32 " carries the data of static channel %s",
1180
               channel->channel_id, ChannelName);
1181
    channel->staticBridge = TRUE;
1182
    channel->state = DVC_CHANNEL_RUNNING;
1183
    channel->channel_callback = &bridge->iface;
1184
1185
    /* published once the Create Response is out, see dvcman_static_bridge_publish */
1186
    DVCMAN_STATIC_BRIDGE_TARGET* target = calloc(1, sizeof(DVCMAN_STATIC_BRIDGE_TARGET));
1187
    if (target)
1188
    {
1189
      target->drdynvc = drdynvc;
1190
      target->channelId = channel->channel_id;
1191
    }
1192
    bridge->pending = target;
1193
    bridge = nullptr;
1194
    *res = CHANNEL_RC_OK;
1195
    goto out;
1196
  }
1197
#endif
1198
1199
0
  *res = listener->listener_callback->OnNewChannelConnection(
1200
0
      listener->listener_callback, &channel->iface, nullptr, &bAccept, &pCallback);
1201
1202
0
  if (*res != CHANNEL_RC_OK)
1203
0
  {
1204
0
    WLog_Print(drdynvc->log, WLOG_ERROR,
1205
0
               "OnNewChannelConnection failed with error %" PRIu32 "!", *res);
1206
0
    *res = ERROR_INTERNAL_ERROR;
1207
0
    dvcman_channel_unref(channel);
1208
0
    channel = nullptr;
1209
0
    goto out;
1210
0
  }
1211
1212
0
  if (!bAccept)
1213
0
  {
1214
0
    WLog_Print(drdynvc->log, WLOG_ERROR, "OnNewChannelConnection returned with bAccept FALSE!");
1215
0
    *res = ERROR_INTERNAL_ERROR;
1216
0
    dvcman_channel_unref(channel);
1217
0
    channel = nullptr;
1218
0
    goto out;
1219
0
  }
1220
1221
0
  WLog_Print(drdynvc->log, WLOG_DEBUG, "listener %s created new channel %" PRIu32 "",
1222
0
             listener->channel_name, channel->channel_id);
1223
0
  channel->state = DVC_CHANNEL_RUNNING;
1224
0
  channel->channel_callback = pCallback;
1225
0
  channel->pInterface = listener->iface.pInterface;
1226
0
  context = dvcman->drdynvc->context;
1227
1228
0
  IFCALLRET(context->OnChannelConnected, *res, context, ChannelName, listener->iface.pInterface);
1229
0
  if (*res != CHANNEL_RC_OK)
1230
0
  {
1231
0
    WLog_Print(drdynvc->log, WLOG_ERROR,
1232
0
               "context.OnChannelConnected failed with error %" PRIu32 "", *res);
1233
0
  }
1234
1235
0
out:
1236
0
  HashTable_Unlock(dvcman->listeners);
1237
0
  HashTable_Unlock(dvcman->channelsById);
1238
#if defined(WITH_RDPEUDP)
1239
  free(bridge);
1240
#endif
1241
1242
0
  return channel;
1243
0
}
1244
1245
/**
1246
 * Function description
1247
 *
1248
 * @return 0 on success, otherwise a Win32 error code
1249
 */
1250
WINPR_ATTR_NODISCARD
1251
static UINT dvcman_open_channel(drdynvcPlugin* drdynvc, DVCMAN_CHANNEL* channel)
1252
0
{
1253
0
  UINT error = CHANNEL_RC_OK;
1254
1255
0
  WINPR_ASSERT(drdynvc);
1256
0
  WINPR_ASSERT(channel);
1257
0
  if (channel->state == DVC_CHANNEL_RUNNING)
1258
0
  {
1259
0
    IWTSVirtualChannelCallback* pCallback = channel->channel_callback;
1260
1261
0
    if (pCallback->OnOpen)
1262
0
    {
1263
0
      check_open_close_receive(channel);
1264
0
      error = pCallback->OnOpen(pCallback);
1265
0
      if (error)
1266
0
      {
1267
0
        WLog_Print(drdynvc->log, WLOG_ERROR, "OnOpen failed with error %" PRIu32 "!",
1268
0
                   error);
1269
0
        goto out;
1270
0
      }
1271
0
    }
1272
1273
0
    WLog_Print(drdynvc->log, WLOG_DEBUG, "open_channel: ChannelId %" PRIu32 "",
1274
0
               channel->channel_id);
1275
0
  }
1276
1277
0
out:
1278
0
  return error;
1279
0
}
1280
1281
/**
1282
 * Function description
1283
 *
1284
 * @return 0 on success, otherwise a Win32 error code
1285
 */
1286
WINPR_ATTR_NODISCARD
1287
static UINT dvcman_receive_channel_data_first(DVCMAN_CHANNEL* channel, UINT32 length)
1288
0
{
1289
0
  WINPR_ASSERT(channel);
1290
0
  WINPR_ASSERT(channel->dvcman);
1291
#if defined(WITH_RDPEUDP)
1292
  if (channel->staticBridge)
1293
  {
1294
    /* not reassembled here, see dvcman_static_bridge_receive */
1295
    channel->dvc_data_length = length;
1296
    channel->bridgeReceived = 0;
1297
    channel->bridgeFragmented = TRUE;
1298
    return CHANNEL_RC_OK;
1299
  }
1300
#endif
1301
0
  if (channel->dvc_data)
1302
0
    Stream_Release(channel->dvc_data);
1303
1304
0
  channel->dvc_data = StreamPool_Take(channel->dvcman->pool, 1600);
1305
1306
0
  if (!channel->dvc_data)
1307
0
  {
1308
0
    drdynvcPlugin* drdynvc = channel->dvcman->drdynvc;
1309
0
    WLog_Print(drdynvc->log, WLOG_ERROR, "StreamPool_Take failed!");
1310
0
    return CHANNEL_RC_NO_MEMORY;
1311
0
  }
1312
1313
0
  channel->dvc_data_length = length;
1314
0
  return CHANNEL_RC_OK;
1315
0
}
1316
1317
/**
1318
 * Function description
1319
 *
1320
 * @return 0 on success, otherwise a Win32 error code
1321
 */
1322
WINPR_ATTR_NODISCARD
1323
static UINT dvcman_receive_channel_data(DVCMAN_CHANNEL* channel, wStream* data,
1324
                                        WINPR_ATTR_UNUSED UINT32 ThreadingFlags)
1325
0
{
1326
0
  UINT status = CHANNEL_RC_OK;
1327
0
  size_t dataSize = Stream_GetRemainingLength(data);
1328
1329
0
  WINPR_ASSERT(channel);
1330
0
  WINPR_ASSERT(channel->dvcman);
1331
1332
0
  channel->stats.bytesIn += Stream_Length(data);
1333
#if defined(WITH_RDPEUDP)
1334
  if (channel->staticBridge)
1335
    return dvcman_static_bridge_receive(channel, data);
1336
#endif
1337
0
  if (channel->dvc_data)
1338
0
  {
1339
0
    drdynvcPlugin* drdynvc = channel->dvcman->drdynvc;
1340
1341
    /* Fragmented data */
1342
0
    if (Stream_GetPosition(channel->dvc_data) + dataSize > channel->dvc_data_length)
1343
0
    {
1344
0
      WLog_Print(drdynvc->log, WLOG_ERROR, "data exceeding declared length!");
1345
0
      Stream_Release(channel->dvc_data);
1346
0
      channel->dvc_data = nullptr;
1347
0
      status = ERROR_INVALID_DATA;
1348
0
      goto out;
1349
0
    }
1350
0
    if (!Stream_EnsureRemainingCapacity(channel->dvc_data, dataSize))
1351
0
    {
1352
0
      Stream_Release(channel->dvc_data);
1353
0
      channel->dvc_data = nullptr;
1354
0
      status = ERROR_INVALID_DATA;
1355
0
      goto out;
1356
0
    }
1357
1358
0
    Stream_Copy(data, channel->dvc_data, dataSize);
1359
0
    channel->stats.fragmentsIn++;
1360
1361
0
    if (Stream_GetPosition(channel->dvc_data) >= channel->dvc_data_length)
1362
0
    {
1363
0
      Stream_SealLength(channel->dvc_data);
1364
0
      Stream_ResetPosition(channel->dvc_data);
1365
1366
0
      status = dvcman_call_on_receive(channel, channel->dvc_data);
1367
0
      Stream_Release(channel->dvc_data);
1368
0
      channel->dvc_data = nullptr;
1369
0
    }
1370
0
  }
1371
0
  else
1372
0
    status = dvcman_call_on_receive(channel, data);
1373
1374
0
out:
1375
0
  return status;
1376
0
}
1377
1378
WINPR_ATTR_NODISCARD
1379
static UINT8 drdynvc_write_variable_uint(wStream* s, UINT32 val)
1380
0
{
1381
0
  UINT8 cb = 0;
1382
1383
0
  if (val <= 0xFF)
1384
0
  {
1385
0
    cb = 0;
1386
0
    Stream_Write_UINT8(s, (UINT8)val);
1387
0
  }
1388
0
  else if (val <= 0xFFFF)
1389
0
  {
1390
0
    cb = 1;
1391
0
    Stream_Write_UINT16(s, (UINT16)val);
1392
0
  }
1393
0
  else
1394
0
  {
1395
0
    cb = 2;
1396
0
    Stream_Write_UINT32(s, val);
1397
0
  }
1398
1399
0
  return cb;
1400
0
}
1401
1402
/**
1403
 * Function description
1404
 *
1405
 * @return 0 on success, otherwise a Win32 error code
1406
 */
1407
WINPR_ATTR_NODISCARD
1408
static UINT drdynvc_send(drdynvcPlugin* drdynvc, wStream* s, DVCMAN_CHANNEL_STATS* stats)
1409
0
{
1410
0
  UINT status = 0;
1411
1412
0
  if (!drdynvc)
1413
0
    status = CHANNEL_RC_BAD_CHANNEL_HANDLE;
1414
0
  else
1415
0
  {
1416
0
    const size_t len = Stream_GetPosition(s);
1417
1418
0
    if (stats)
1419
0
      stats->bytesOut += len;
1420
1421
0
    WINPR_ASSERT(drdynvc->channelEntryPoints.pVirtualChannelWriteEx);
1422
0
    status = drdynvc->channelEntryPoints.pVirtualChannelWriteEx(
1423
0
        drdynvc->InitHandle, drdynvc->OpenHandle, Stream_Buffer(s), (UINT32)len, s);
1424
0
  }
1425
1426
0
  switch (status)
1427
0
  {
1428
0
    case CHANNEL_RC_OK:
1429
0
      return CHANNEL_RC_OK;
1430
1431
0
    case CHANNEL_RC_NOT_CONNECTED:
1432
0
      Stream_Release(s);
1433
0
      return CHANNEL_RC_OK;
1434
1435
0
    case CHANNEL_RC_BAD_CHANNEL_HANDLE:
1436
0
      Stream_Release(s);
1437
0
      WLog_ERR(TAG, "VirtualChannelWriteEx failed with CHANNEL_RC_BAD_CHANNEL_HANDLE");
1438
0
      return status;
1439
1440
0
    default:
1441
0
      Stream_Release(s);
1442
0
      WLog_Print(drdynvc->log, WLOG_ERROR,
1443
0
                 "VirtualChannelWriteEx failed with %s [%08" PRIX32 "]",
1444
0
                 WTSErrorToString(status), status);
1445
0
      return status;
1446
0
  }
1447
0
}
1448
1449
/**
1450
 * Ends the session over data of a tunnelled channel that can no longer be sent in order. The
1451
 * client then reconnects, which is how the main connection handles a broken transport too.
1452
 */
1453
static void drdynvc_tunnel_abort(drdynvcPlugin* drdynvc, const char* why)
1454
0
{
1455
0
  WINPR_ASSERT(drdynvc);
1456
0
  WINPR_ASSERT(why);
1457
1458
0
  WLog_Print(drdynvc->log, WLOG_ERROR, "%s, ending the session", why);
1459
0
  if (!drdynvc->rdpcontext)
1460
0
    return;
1461
0
  freerdp_set_last_error_if_not(drdynvc->rdpcontext, FREERDP_ERROR_CONNECT_TRANSPORT_FAILED);
1462
0
  (void)freerdp_abort_connect_context(drdynvc->rdpcontext);
1463
0
}
1464
1465
/**
1466
 * Sends a PDU of a channel the way that channel's data travels: on its multitransport tunnel,
1467
 * or on the drdynvc static channel.
1468
 *
1469
 * A tunnel that is gone falls back to the static channel, and tunnelType is set to 0 so the
1470
 * rest of the message follows. A continuation, a later fragment of a message whose start went
1471
 * over the tunnel, cannot: on TCP it could overtake that start or be read without it.
1472
 *
1473
 * @return 0 on success, otherwise a Win32 error code
1474
 */
1475
WINPR_ATTR_NODISCARD
1476
static UINT drdynvc_send_on(drdynvcPlugin* drdynvc, wStream* s, DVCMAN_CHANNEL_STATS* stats,
1477
                            UINT32* tunnelType, BOOL continuation)
1478
0
{
1479
0
  WINPR_ASSERT(s);
1480
0
  WINPR_ASSERT(tunnelType);
1481
0
  if (drdynvc && (*tunnelType != 0))
1482
0
  {
1483
0
    const size_t len = Stream_GetPosition(s);
1484
0
    if (freerdp_multitransport_send_dvc(drdynvc->rdpcontext, *tunnelType, Stream_Buffer(s),
1485
0
                                        len))
1486
0
    {
1487
0
      if (stats)
1488
0
        stats->bytesOut += len;
1489
0
      Stream_Release(s);
1490
0
      return CHANNEL_RC_OK;
1491
0
    }
1492
0
    if (continuation)
1493
0
    {
1494
0
      Stream_Release(s);
1495
0
      drdynvc_tunnel_abort(drdynvc, "the tunnel failed in the middle of a message");
1496
0
      return ERROR_BROKEN_PIPE;
1497
0
    }
1498
0
    *tunnelType = 0;
1499
0
  }
1500
0
  return drdynvc_send(drdynvc, s, stats);
1501
0
}
1502
1503
/**
1504
 * Function description
1505
 *
1506
 * @return 0 on success, otherwise a Win32 error code
1507
 */
1508
WINPR_ATTR_NODISCARD
1509
static UINT drdynvc_write_data(drdynvcPlugin* drdynvc, UINT32 ChannelId, UINT32 tunnelType,
1510
                               const BYTE* data, UINT32 dataSize, BOOL* close,
1511
                               DVCMAN_CHANNEL_STATS* stats)
1512
0
{
1513
0
  size_t pos = 0;
1514
0
  UINT8 cbChId = 0;
1515
0
  UINT8 cbLen = 0;
1516
0
  UINT status = CHANNEL_RC_BAD_INIT_HANDLE;
1517
0
  DVCMAN* dvcman = nullptr;
1518
1519
0
  if (!drdynvc)
1520
0
    return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1521
1522
0
  dvcman = (DVCMAN*)drdynvc->channel_mgr;
1523
0
  WINPR_ASSERT(dvcman);
1524
1525
  /* All fragments of one message take the same way, so a message never starts on one
1526
   * transport and continues on the other. */
1527
0
  if ((tunnelType != 0) &&
1528
0
      (freerdp_multitransport_get_tunnel_state(drdynvc->rdpcontext, tunnelType) !=
1529
0
       FREERDP_MULTITRANSPORT_TUNNEL_READY))
1530
0
    tunnelType = 0;
1531
1532
0
  WLog_Print(drdynvc->log, WLOG_TRACE, "write_data: ChannelId=%" PRIu32 " size=%" PRIu32 "",
1533
0
             ChannelId, dataSize);
1534
0
  wStream* data_out = StreamPool_Take(dvcman->pool, CHANNEL_CHUNK_LENGTH);
1535
1536
0
  if (!data_out)
1537
0
  {
1538
0
    WLog_Print(drdynvc->log, WLOG_ERROR, "StreamPool_Take failed!");
1539
0
    return CHANNEL_RC_NO_MEMORY;
1540
0
  }
1541
1542
0
  if (!Stream_SetPosition(data_out, 1))
1543
0
  {
1544
0
    Stream_Release(data_out);
1545
0
    return ERROR_INVALID_DATA;
1546
0
  }
1547
0
  cbChId = drdynvc_write_variable_uint(data_out, ChannelId);
1548
0
  pos = Stream_GetPosition(data_out);
1549
1550
0
  if (dataSize == 0)
1551
0
  {
1552
    /* TODO: shall treat that case with write(0) that do a close */
1553
0
    *close = TRUE;
1554
0
    Stream_Release(data_out);
1555
0
  }
1556
0
  else if (dataSize <= CHANNEL_CHUNK_LENGTH - pos)
1557
0
  {
1558
0
    Stream_ResetPosition(data_out);
1559
0
    Stream_Write_UINT8(data_out, (DATA_PDU << 4) | cbChId);
1560
0
    if (!Stream_SetPosition(data_out, pos))
1561
0
    {
1562
0
      Stream_Release(data_out);
1563
0
      return ERROR_INVALID_DATA;
1564
0
    }
1565
0
    Stream_Write(data_out, data, dataSize);
1566
0
    stats->packetsOut++;
1567
0
    status = drdynvc_send_on(drdynvc, data_out, stats, &tunnelType, FALSE);
1568
0
  }
1569
0
  else
1570
0
  {
1571
    /* Fragment the data */
1572
0
    cbLen = drdynvc_write_variable_uint(data_out, dataSize);
1573
0
    pos = Stream_GetPosition(data_out);
1574
0
    Stream_ResetPosition(data_out);
1575
1576
0
    const INT32 pdu = (DATA_FIRST_PDU << 4) | cbChId | (cbLen << 2);
1577
0
    Stream_Write_UINT8(data_out, WINPR_ASSERTING_INT_CAST(UINT8, pdu));
1578
0
    if (!Stream_SetPosition(data_out, pos))
1579
0
    {
1580
0
      Stream_Release(data_out);
1581
0
      return ERROR_INVALID_DATA;
1582
0
    }
1583
1584
0
    {
1585
0
      WINPR_ASSERT(pos <= CHANNEL_CHUNK_LENGTH);
1586
0
      const uint32_t chunkLength =
1587
0
          CHANNEL_CHUNK_LENGTH - WINPR_ASSERTING_INT_CAST(uint32_t, pos);
1588
0
      Stream_Write(data_out, data, chunkLength);
1589
1590
0
      data += chunkLength;
1591
0
      dataSize -= chunkLength;
1592
0
    }
1593
0
    if (dataSize > 0)
1594
0
      stats->fragmentsOut++;
1595
1596
0
    status = drdynvc_send_on(drdynvc, data_out, stats, &tunnelType, FALSE);
1597
1598
0
    while (status == CHANNEL_RC_OK && dataSize > 0)
1599
0
    {
1600
0
      data_out = StreamPool_Take(dvcman->pool, CHANNEL_CHUNK_LENGTH);
1601
1602
0
      if (!data_out)
1603
0
      {
1604
0
        WLog_Print(drdynvc->log, WLOG_ERROR, "StreamPool_Take failed!");
1605
0
        return CHANNEL_RC_NO_MEMORY;
1606
0
      }
1607
1608
0
      if (!Stream_SetPosition(data_out, 1))
1609
0
      {
1610
0
        Stream_Release(data_out);
1611
0
        return ERROR_INVALID_DATA;
1612
0
      }
1613
1614
0
      cbChId = drdynvc_write_variable_uint(data_out, ChannelId);
1615
0
      pos = Stream_GetPosition(data_out);
1616
0
      Stream_ResetPosition(data_out);
1617
0
      Stream_Write_UINT8(data_out, (DATA_PDU << 4) | cbChId);
1618
0
      if (!Stream_SetPosition(data_out, pos))
1619
0
      {
1620
0
        Stream_Release(data_out);
1621
0
        return ERROR_INVALID_DATA;
1622
0
      }
1623
1624
0
      uint32_t chunkLength = dataSize;
1625
1626
0
      WINPR_ASSERT(pos <= CHANNEL_CHUNK_LENGTH);
1627
0
      const uint32_t clen = CHANNEL_CHUNK_LENGTH - WINPR_ASSERTING_INT_CAST(uint32_t, pos);
1628
0
      if (chunkLength > clen)
1629
0
      {
1630
0
        stats->fragmentsOut++;
1631
0
        chunkLength = clen;
1632
0
      }
1633
0
      else
1634
0
        stats->packetsOut++;
1635
1636
0
      Stream_Write(data_out, data, chunkLength);
1637
0
      data += chunkLength;
1638
0
      dataSize -= chunkLength;
1639
1640
0
      status = drdynvc_send_on(drdynvc, data_out, stats, &tunnelType, TRUE);
1641
0
    }
1642
0
  }
1643
1644
0
  if (status != CHANNEL_RC_OK)
1645
0
  {
1646
0
    WLog_Print(drdynvc->log, WLOG_ERROR, "VirtualChannelWriteEx failed with %s [%08" PRIX32 "]",
1647
0
               WTSErrorToString(status), status);
1648
0
    return status;
1649
0
  }
1650
1651
0
  return CHANNEL_RC_OK;
1652
0
}
1653
1654
/**
1655
 * Function description
1656
 *
1657
 * @return 0 on success, otherwise a Win32 error code
1658
 */
1659
WINPR_ATTR_NODISCARD
1660
static UINT drdynvc_send_capability_response(drdynvcPlugin* drdynvc)
1661
0
{
1662
0
  UINT status = 0;
1663
0
  wStream* s = nullptr;
1664
0
  DVCMAN* dvcman = nullptr;
1665
1666
0
  if (!drdynvc)
1667
0
    return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1668
1669
0
  dvcman = (DVCMAN*)drdynvc->channel_mgr;
1670
0
  WINPR_ASSERT(dvcman);
1671
1672
0
  WLog_Print(drdynvc->log, WLOG_TRACE, "capability_response");
1673
0
  s = StreamPool_Take(dvcman->pool, 4);
1674
1675
0
  if (!s)
1676
0
  {
1677
0
    WLog_Print(drdynvc->log, WLOG_ERROR, "Stream_New failed!");
1678
0
    return CHANNEL_RC_NO_MEMORY;
1679
0
  }
1680
1681
0
  Stream_Write_UINT16(s, 0x0050); /* Cmd+Sp+cbChId+Pad. Note: MSTSC sends 0x005c */
1682
0
  Stream_Write_UINT16(s, drdynvc->version);
1683
0
  status = drdynvc_send(drdynvc, s, nullptr);
1684
1685
0
  if (status != CHANNEL_RC_OK)
1686
0
  {
1687
0
    WLog_Print(drdynvc->log, WLOG_ERROR, "VirtualChannelWriteEx failed with %s [%08" PRIX32 "]",
1688
0
               WTSErrorToString(status), status);
1689
0
  }
1690
1691
0
  return status;
1692
0
}
1693
1694
/**
1695
 * Function description
1696
 *
1697
 * @return 0 on success, otherwise a Win32 error code
1698
 */
1699
WINPR_ATTR_NODISCARD
1700
static UINT drdynvc_process_capability_request(drdynvcPlugin* drdynvc, int Sp, int cbChId,
1701
                                               wStream* s)
1702
0
{
1703
0
  UINT status = 0;
1704
1705
0
  if (!drdynvc)
1706
0
    return CHANNEL_RC_BAD_INIT_HANDLE;
1707
1708
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 3))
1709
0
    return ERROR_INVALID_DATA;
1710
1711
0
  WLog_Print(drdynvc->log, WLOG_TRACE, "capability_request Sp=%d cbChId=%d", Sp, cbChId);
1712
0
  Stream_Seek(s, 1); /* pad */
1713
0
  Stream_Read_UINT16(s, drdynvc->version);
1714
1715
  /* RDP8 servers offer version 3, though Microsoft forgot to document it
1716
   * in their early documents.  It behaves the same as version 2.
1717
   */
1718
0
  if ((drdynvc->version == 2) || (drdynvc->version == 3))
1719
0
  {
1720
0
    if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
1721
0
      return ERROR_INVALID_DATA;
1722
1723
0
    Stream_Read_UINT16(s, drdynvc->PriorityCharge0);
1724
0
    Stream_Read_UINT16(s, drdynvc->PriorityCharge1);
1725
0
    Stream_Read_UINT16(s, drdynvc->PriorityCharge2);
1726
0
    Stream_Read_UINT16(s, drdynvc->PriorityCharge3);
1727
0
  }
1728
1729
0
  status = drdynvc_send_capability_response(drdynvc);
1730
0
  drdynvc->state = DRDYNVC_STATE_READY;
1731
0
  return status;
1732
0
}
1733
1734
WINPR_ATTR_NODISCARD
1735
static UINT32 drdynvc_cblen_to_bytes(int cbLen)
1736
0
{
1737
0
  switch (cbLen)
1738
0
  {
1739
0
    case 0:
1740
0
      return 1;
1741
1742
0
    case 1:
1743
0
      return 2;
1744
1745
0
    default:
1746
0
      return 4;
1747
0
  }
1748
0
}
1749
1750
WINPR_ATTR_NODISCARD
1751
static UINT32 drdynvc_read_variable_uint(wStream* s, int cbLen)
1752
0
{
1753
0
  UINT32 val = 0;
1754
1755
0
  switch (cbLen)
1756
0
  {
1757
0
    case 0:
1758
0
      Stream_Read_UINT8(s, val);
1759
0
      break;
1760
1761
0
    case 1:
1762
0
      Stream_Read_UINT16(s, val);
1763
0
      break;
1764
1765
0
    default:
1766
0
      Stream_Read_UINT32(s, val);
1767
0
      break;
1768
0
  }
1769
1770
0
  return val;
1771
0
}
1772
1773
/**
1774
 * Function description
1775
 *
1776
 * @return 0 on success, otherwise a Win32 error code
1777
 */
1778
WINPR_ATTR_NODISCARD
1779
static UINT drdynvc_process_create_request(drdynvcPlugin* drdynvc, UINT8 Sp, UINT8 cbChId,
1780
                                           wStream* s, UINT32 tunnelType)
1781
0
{
1782
0
  UINT status = 0;
1783
0
  wStream* data_out = nullptr;
1784
0
  UINT channel_status = 0;
1785
0
  DVCMAN* dvcman = nullptr;
1786
0
  DVCMAN_CHANNEL* channel = nullptr;
1787
0
  INT32 retStatus = 0;
1788
1789
0
  WINPR_UNUSED(Sp);
1790
0
  if (!drdynvc)
1791
0
    return CHANNEL_RC_BAD_CHANNEL_HANDLE;
1792
1793
0
  dvcman = (DVCMAN*)drdynvc->channel_mgr;
1794
0
  WINPR_ASSERT(dvcman);
1795
1796
0
  if (drdynvc->state == DRDYNVC_STATE_CAPABILITIES)
1797
0
  {
1798
    /**
1799
     * For some reason the server does not always send the
1800
     * capabilities pdu as it should. When this happens,
1801
     * send a capabilities response.
1802
     */
1803
0
    drdynvc->version = 3;
1804
1805
0
    if ((status = drdynvc_send_capability_response(drdynvc)))
1806
0
    {
1807
0
      WLog_Print(drdynvc->log, WLOG_ERROR, "drdynvc_send_capability_response failed!");
1808
0
      return status;
1809
0
    }
1810
1811
0
    drdynvc->state = DRDYNVC_STATE_READY;
1812
0
  }
1813
1814
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, drdynvc_cblen_to_bytes(cbChId)))
1815
0
    return ERROR_INVALID_DATA;
1816
1817
0
  const UINT32 ChannelId = drdynvc_read_variable_uint(s, cbChId);
1818
0
  const size_t pos = Stream_GetPosition(s);
1819
0
  const char* name = Stream_ConstPointer(s);
1820
0
  const size_t length = Stream_GetRemainingLength(s);
1821
1822
0
  if (strnlen(name, length) >= length)
1823
0
    return ERROR_INVALID_DATA;
1824
1825
0
  WLog_Print(drdynvc->log, WLOG_DEBUG,
1826
0
             "process_create_request: ChannelId=%" PRIu32 " ChannelName=%s", ChannelId, name);
1827
1828
0
  data_out = StreamPool_Take(dvcman->pool, pos + 4);
1829
0
  if (!data_out)
1830
0
  {
1831
0
    WLog_Print(drdynvc->log, WLOG_ERROR, "StreamPool_Take failed!");
1832
0
    return CHANNEL_RC_NO_MEMORY;
1833
0
  }
1834
1835
0
  Stream_Write_UINT8(data_out, (CREATE_REQUEST_PDU << 4) | cbChId);
1836
0
  if (!Stream_SetPosition(s, 1))
1837
0
    return ERROR_INVALID_DATA;
1838
0
  Stream_Copy(s, data_out, pos - 1);
1839
1840
0
  channel =
1841
0
      dvcman_create_channel(drdynvc, drdynvc->channel_mgr, ChannelId, name, &channel_status);
1842
0
  switch (channel_status)
1843
0
  {
1844
0
    case CHANNEL_RC_OK:
1845
0
      WLog_Print(drdynvc->log, WLOG_DEBUG, "channel created");
1846
0
      retStatus = 0;
1847
0
      break;
1848
0
    case CHANNEL_RC_NO_MEMORY:
1849
0
      WLog_Print(drdynvc->log, WLOG_DEBUG, "not enough memory for channel creation");
1850
0
      retStatus = STATUS_NO_MEMORY;
1851
0
      break;
1852
0
    case ERROR_NOT_FOUND:
1853
0
      WLog_Print(drdynvc->log, WLOG_DEBUG, "no listener for '%s'", name);
1854
0
      retStatus = STATUS_NOT_FOUND; /* same code used by mstsc, STATUS_UNSUCCESSFUL */
1855
0
      break;
1856
0
    default:
1857
0
      WLog_Print(drdynvc->log, WLOG_DEBUG, "channel creation error");
1858
0
      retStatus = STATUS_UNSUCCESSFUL; /* same code used by mstsc, STATUS_UNSUCCESSFUL */
1859
0
      break;
1860
0
  }
1861
0
  Stream_Write_INT32(data_out, retStatus);
1862
1863
  /* A channel created on a tunnel is read there by the server, so it is answered there. */
1864
0
  if (channel && (channel_status == CHANNEL_RC_OK))
1865
0
    dvcman_channel_set_tunnel(channel, tunnelType);
1866
0
  UINT32 responseTunnel = tunnelType;
1867
0
  status = drdynvc_send_on(drdynvc, data_out, nullptr, &responseTunnel, FALSE);
1868
0
  if (status != CHANNEL_RC_OK)
1869
0
  {
1870
0
    WLog_Print(drdynvc->log, WLOG_ERROR, "VirtualChannelWriteEx failed with %s [%08" PRIX32 "]",
1871
0
               WTSErrorToString(status), status);
1872
0
    if (channel_status == CHANNEL_RC_OK)
1873
0
      dvcman_channel_unref(channel);
1874
0
    return status;
1875
0
  }
1876
1877
0
  if (channel_status == CHANNEL_RC_OK)
1878
0
  {
1879
#if defined(WITH_RDPEUDP)
1880
    dvcman_static_bridge_publish(drdynvc, channel);
1881
#endif
1882
0
    if ((status = dvcman_open_channel(drdynvc, channel)))
1883
0
    {
1884
0
      WLog_Print(drdynvc->log, WLOG_ERROR,
1885
0
                 "dvcman_open_channel failed with error %" PRIu32 "!", status);
1886
0
      return status;
1887
0
    }
1888
0
  }
1889
1890
0
  return status;
1891
0
}
1892
1893
/**
1894
 * Function description
1895
 *
1896
 * @return 0 on success, otherwise a Win32 error code
1897
 */
1898
WINPR_ATTR_NODISCARD
1899
static UINT drdynvc_process_data_first(drdynvcPlugin* drdynvc, int Sp, int cbChId, wStream* s,
1900
                                       BOOL compressed, UINT32 ThreadingFlags, UINT32 tunnelType)
1901
0
{
1902
0
  WINPR_ASSERT(drdynvc);
1903
0
  if (!Stream_CheckAndLogRequiredLength(
1904
0
          TAG, s, drdynvc_cblen_to_bytes(cbChId) + drdynvc_cblen_to_bytes(Sp)))
1905
0
    return ERROR_INVALID_DATA;
1906
1907
0
  UINT32 ChannelId = drdynvc_read_variable_uint(s, cbChId);
1908
0
  UINT32 Length = drdynvc_read_variable_uint(s, Sp);
1909
0
  WLog_Print(drdynvc->log, WLOG_TRACE,
1910
0
             "process_data_first: Sp=%d cbChId=%d, ChannelId=%" PRIu32 " Length=%" PRIu32 "", Sp,
1911
0
             cbChId, ChannelId, Length);
1912
1913
0
  DVCMAN_CHANNEL* channel = dvcman_get_channel_by_id(drdynvc->channel_mgr, ChannelId, TRUE);
1914
0
  if (!channel)
1915
0
  {
1916
    /**
1917
     * Windows Server 2012 R2 can send some messages over
1918
     * Microsoft::Windows::RDS::Geometry::v08.01 even if the dynamic virtual channel wasn't
1919
     * registered on our side. Ignoring it works.
1920
     */
1921
0
    WLog_Print(drdynvc->log, WLOG_ERROR, "ChannelId %" PRIu32 " not found!", ChannelId);
1922
0
    return CHANNEL_RC_OK;
1923
0
  }
1924
1925
0
  UINT status = CHANNEL_RC_OK;
1926
0
  BOOL shouldFree = FALSE;
1927
0
  if (tunnelType != 0)
1928
0
    dvcman_channel_set_tunnel(channel, tunnelType);
1929
0
  if (channel->state != DVC_CHANNEL_RUNNING)
1930
0
    goto out;
1931
1932
0
  if (compressed)
1933
0
  {
1934
0
    BYTE* data = nullptr;
1935
0
    UINT32 dataSize = 0;
1936
0
    if (zgfx_decompress(channel->decompressor, Stream_Pointer(s),
1937
0
                        WINPR_ASSERTING_INT_CAST(UINT32, Stream_GetRemainingLength(s)), &data,
1938
0
                        &dataSize, 0) < 0)
1939
0
    {
1940
0
      status = ERROR_INVALID_DATA;
1941
0
      WLog_Print(drdynvc->log, WLOG_ERROR, "error de-compressing first packet");
1942
0
      goto out;
1943
0
    }
1944
1945
0
    if (dataSize == 0)
1946
0
    {
1947
0
      status = ERROR_INVALID_DATA;
1948
0
      WLog_Print(drdynvc->log, WLOG_ERROR, "error de-compressing first packet");
1949
0
      free(data);
1950
0
      goto out;
1951
0
    }
1952
1953
0
    s = Stream_New(data, dataSize);
1954
0
    if (!s)
1955
0
    {
1956
0
      status = CHANNEL_RC_NO_MEMORY;
1957
0
      WLog_Print(drdynvc->log, WLOG_ERROR, "error allocating new Stream(len=%" PRIu32 ")",
1958
0
                 dataSize);
1959
0
      free(data);
1960
0
      goto out;
1961
0
    }
1962
0
    shouldFree = TRUE;
1963
0
  }
1964
1965
0
  status = dvcman_receive_channel_data_first(channel, Length);
1966
1967
0
  if (status == CHANNEL_RC_OK)
1968
0
    status = dvcman_receive_channel_data(channel, s, ThreadingFlags);
1969
1970
0
  if (status != CHANNEL_RC_OK)
1971
0
    status = dvcman_channel_close(channel, FALSE, FALSE);
1972
1973
0
out:
1974
0
  if (shouldFree)
1975
0
    Stream_Free(s, TRUE);
1976
0
  dvcman_channel_unref(channel);
1977
0
  dvcman_return_channel(drdynvc->channel_mgr, channel);
1978
0
  return status;
1979
0
}
1980
1981
/**
1982
 * Function description
1983
 *
1984
 * @return 0 on success, otherwise a Win32 error code
1985
 */
1986
WINPR_ATTR_NODISCARD
1987
static UINT drdynvc_process_data(drdynvcPlugin* drdynvc, int Sp, int cbChId, wStream* s,
1988
                                 BOOL compressed, UINT32 ThreadingFlags, UINT32 tunnelType)
1989
0
{
1990
0
  WINPR_ASSERT(drdynvc);
1991
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, drdynvc_cblen_to_bytes(cbChId)))
1992
0
    return ERROR_INVALID_DATA;
1993
1994
0
  UINT32 ChannelId = drdynvc_read_variable_uint(s, cbChId);
1995
0
  WLog_Print(drdynvc->log, WLOG_TRACE, "process_data: Sp=%d cbChId=%d, ChannelId=%" PRIu32 "", Sp,
1996
0
             cbChId, ChannelId);
1997
1998
0
  DVCMAN_CHANNEL* channel = dvcman_get_channel_by_id(drdynvc->channel_mgr, ChannelId, TRUE);
1999
0
  if (!channel)
2000
0
  {
2001
    /**
2002
     * Windows Server 2012 R2 can send some messages over
2003
     * Microsoft::Windows::RDS::Geometry::v08.01 even if the dynamic virtual channel wasn't
2004
     * registered on our side. Ignoring it works.
2005
     */
2006
0
    WLog_Print(drdynvc->log, WLOG_ERROR, "ChannelId %" PRIu32 " not found!", ChannelId);
2007
0
    return CHANNEL_RC_OK;
2008
0
  }
2009
2010
0
  BOOL shouldFree = FALSE;
2011
0
  UINT status = CHANNEL_RC_OK;
2012
0
  if (tunnelType != 0)
2013
0
    dvcman_channel_set_tunnel(channel, tunnelType);
2014
0
  if (channel->state != DVC_CHANNEL_RUNNING)
2015
0
    goto out;
2016
2017
0
  if (compressed)
2018
0
  {
2019
0
    BYTE* data = nullptr;
2020
0
    UINT32 dataSize = 0;
2021
2022
0
    if (zgfx_decompress(channel->decompressor, Stream_Pointer(s),
2023
0
                        WINPR_ASSERTING_INT_CAST(UINT32, Stream_GetRemainingLength(s)), &data,
2024
0
                        &dataSize, 0) < 0)
2025
0
    {
2026
0
      status = ERROR_INVALID_DATA;
2027
0
      WLog_Print(drdynvc->log, WLOG_ERROR, "error de-compressing data packet");
2028
0
      free(data);
2029
0
      goto out;
2030
0
    }
2031
2032
0
    if (dataSize == 0)
2033
0
    {
2034
0
      status = ERROR_INVALID_DATA;
2035
0
      WLog_Print(drdynvc->log, WLOG_ERROR, "error de-compressing data packet");
2036
0
      free(data);
2037
0
      goto out;
2038
0
    }
2039
2040
0
    s = Stream_New(data, dataSize);
2041
0
    if (!s)
2042
0
    {
2043
0
      status = CHANNEL_RC_NO_MEMORY;
2044
0
      WLog_Print(drdynvc->log, WLOG_ERROR, "error allocating new Stream(len=%" PRIu32 ")",
2045
0
                 dataSize);
2046
0
      free(data);
2047
0
      goto out;
2048
0
    }
2049
0
    shouldFree = TRUE;
2050
0
  }
2051
2052
0
  status = dvcman_receive_channel_data(channel, s, ThreadingFlags);
2053
0
  if (status != CHANNEL_RC_OK)
2054
0
    status = dvcman_channel_close(channel, FALSE, FALSE);
2055
2056
0
out:
2057
0
  if (shouldFree)
2058
0
    Stream_Free(s, TRUE);
2059
0
  dvcman_channel_unref(channel);
2060
0
  dvcman_return_channel(drdynvc->channel_mgr, channel);
2061
0
  return status;
2062
0
}
2063
2064
/**
2065
 * Function description
2066
 *
2067
 * @return 0 on success, otherwise a Win32 error code
2068
 */
2069
WINPR_ATTR_NODISCARD
2070
static UINT drdynvc_process_close_request(drdynvcPlugin* drdynvc, int Sp, int cbChId, wStream* s,
2071
                                          UINT32 tunnelType)
2072
0
{
2073
0
  UINT32 ChannelId = 0;
2074
0
  DVCMAN_CHANNEL* channel = nullptr;
2075
2076
0
  WINPR_ASSERT(drdynvc);
2077
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, drdynvc_cblen_to_bytes(cbChId)))
2078
0
    return ERROR_INVALID_DATA;
2079
2080
0
  ChannelId = drdynvc_read_variable_uint(s, cbChId);
2081
0
  WLog_Print(drdynvc->log, WLOG_DEBUG,
2082
0
             "process_close_request: Sp=%d cbChId=%d, ChannelId=%" PRIu32 "", Sp, cbChId,
2083
0
             ChannelId);
2084
2085
0
  channel = dvcman_get_channel_by_id(drdynvc->channel_mgr, ChannelId, TRUE);
2086
0
  if (!channel)
2087
0
  {
2088
0
    WLog_Print(drdynvc->log, WLOG_ERROR, "dvcman_close_request channel %" PRIu32 " not present",
2089
0
               ChannelId);
2090
0
    return CHANNEL_RC_OK;
2091
0
  }
2092
2093
0
  if (tunnelType != 0)
2094
0
    dvcman_channel_set_tunnel(channel, tunnelType);
2095
0
  dvcman_channel_close(channel, TRUE, FALSE);
2096
0
  dvcman_channel_unref(channel);
2097
0
  dvcman_return_channel(drdynvc->channel_mgr, channel);
2098
0
  return CHANNEL_RC_OK;
2099
0
}
2100
2101
WINPR_ATTR_NODISCARD
2102
static BOOL drdynvc_soft_sync_negotiated(drdynvcPlugin* drdynvc)
2103
0
{
2104
0
  WINPR_ASSERT(drdynvc);
2105
0
  if (!drdynvc->rdpcontext)
2106
0
    return FALSE;
2107
0
  const UINT32 flags =
2108
0
      freerdp_settings_get_uint32(drdynvc->rdpcontext->settings, FreeRDP_MultitransportFlags);
2109
0
  return (flags & SOFTSYNC_TCP_TO_UDP) != 0;
2110
0
}
2111
2112
WINPR_ATTR_NODISCARD
2113
static UINT drdynvc_order_recv(drdynvcPlugin* drdynvc, wStream* s, UINT32 ThreadingFlags,
2114
                               UINT32 tunnelType);
2115
2116
static void drdynvc_tunnel_backlog_clear(drdynvcPlugin* drdynvc)
2117
0
{
2118
0
  WINPR_ASSERT(drdynvc);
2119
0
  for (size_t x = 0; x < drdynvc->tunnelBacklogCount; x++)
2120
0
    Stream_Release(drdynvc->tunnelBacklog[x]);
2121
0
  drdynvc->tunnelBacklogCount = 0;
2122
0
}
2123
2124
/* Hands up tunnel data that arrived before the Soft-Sync Request, in arrival order. The backlog
2125
 * is taken out first, so a PDU handed up that leads back here finds it empty instead of entries
2126
 * this loop has handed up already. */
2127
WINPR_ATTR_NODISCARD
2128
static UINT drdynvc_tunnel_backlog_flush(drdynvcPlugin* drdynvc)
2129
0
{
2130
0
  WINPR_ASSERT(drdynvc);
2131
2132
0
  wStream** backlog = drdynvc->tunnelBacklog;
2133
0
  UINT32* types = drdynvc->tunnelBacklogTypes;
2134
0
  const size_t count = drdynvc->tunnelBacklogCount;
2135
0
  drdynvc->tunnelBacklog = nullptr;
2136
0
  drdynvc->tunnelBacklogTypes = nullptr;
2137
0
  drdynvc->tunnelBacklogCount = 0;
2138
0
  drdynvc->tunnelBacklogCapacity = 0;
2139
2140
0
  UINT error = CHANNEL_RC_OK;
2141
0
  for (size_t x = 0; x < count; x++)
2142
0
  {
2143
0
    const UINT rc = drdynvc_order_recv(drdynvc, backlog[x], TRUE, types[x]);
2144
0
    Stream_Release(backlog[x]);
2145
0
    if (rc != CHANNEL_RC_OK)
2146
0
      error = rc;
2147
0
  }
2148
0
  free((void*)backlog);
2149
0
  free(types);
2150
0
  return error;
2151
0
}
2152
2153
WINPR_ATTR_NODISCARD
2154
static BOOL drdynvc_tunnel_backlog_add(drdynvcPlugin* drdynvc, wStream* s, UINT32 tunnelType)
2155
0
{
2156
0
  WINPR_ASSERT(drdynvc);
2157
0
  WINPR_ASSERT(s);
2158
2159
0
  if (drdynvc->tunnelBacklogCount == drdynvc->tunnelBacklogCapacity)
2160
0
  {
2161
    /* bounded by DRDYNVC_MAX_TUNNEL_BACKLOG, checked anyway */
2162
0
    if (drdynvc->tunnelBacklogCapacity > SIZE_MAX / 2 / sizeof(wStream*))
2163
0
      return FALSE;
2164
0
    const size_t capacity =
2165
0
        (drdynvc->tunnelBacklogCapacity == 0) ? 32 : drdynvc->tunnelBacklogCapacity * 2;
2166
0
    wStream** streams =
2167
0
        (wStream**)realloc((void*)drdynvc->tunnelBacklog, capacity * sizeof(wStream*));
2168
0
    if (!streams)
2169
0
      return FALSE;
2170
0
    drdynvc->tunnelBacklog = streams;
2171
0
    UINT32* types = realloc(drdynvc->tunnelBacklogTypes, capacity * sizeof(UINT32));
2172
0
    if (!types)
2173
0
      return FALSE;
2174
0
    drdynvc->tunnelBacklogTypes = types;
2175
0
    drdynvc->tunnelBacklogCapacity = capacity;
2176
0
  }
2177
0
  drdynvc->tunnelBacklog[drdynvc->tunnelBacklogCount] = s;
2178
0
  drdynvc->tunnelBacklogTypes[drdynvc->tunnelBacklogCount] = tunnelType;
2179
0
  drdynvc->tunnelBacklogCount++;
2180
0
  return TRUE;
2181
0
}
2182
2183
/**
2184
 * A PDU that arrived on a multitransport tunnel. Takes ownership of the stream.
2185
 *
2186
 * [MS-RDPEDYC] 3.2.5.3.1: the client must not read dynamic channel data from a tunnel before it
2187
 * has the Soft-Sync Request, which tells it the server has flushed those channels on TCP. Data
2188
 * that overtakes the request on the faster path waits for it.
2189
 */
2190
WINPR_ATTR_NODISCARD
2191
static UINT drdynvc_receive_tunnel_pdu(drdynvcPlugin* drdynvc, wStream* s, UINT32 tunnelType)
2192
0
{
2193
0
  WINPR_ASSERT(drdynvc);
2194
0
  WINPR_ASSERT(s);
2195
2196
0
  if (drdynvc_soft_sync_negotiated(drdynvc) && !drdynvc->softSyncDone)
2197
0
  {
2198
    /* Reading on without the request would interleave tunnel and TCP data in no defined
2199
     * order, so a server that never sends it ends the session. */
2200
0
    if (drdynvc->tunnelBacklogCount >= DRDYNVC_MAX_TUNNEL_BACKLOG)
2201
0
    {
2202
0
      Stream_Release(s);
2203
0
      drdynvc_tunnel_backlog_clear(drdynvc);
2204
0
      drdynvc_tunnel_abort(drdynvc,
2205
0
                           "no Soft-Sync Request after the tunnel backlog filled up");
2206
0
      return ERROR_INVALID_DATA;
2207
0
    }
2208
0
    if (!drdynvc_tunnel_backlog_add(drdynvc, s, tunnelType))
2209
0
    {
2210
      /* acknowledged to the server already, dropping it leaves a hole in the stream */
2211
0
      Stream_Release(s);
2212
0
      drdynvc_tunnel_abort(drdynvc, "no memory to hold tunnel data until the Soft-Sync");
2213
0
      return CHANNEL_RC_NO_MEMORY;
2214
0
    }
2215
0
    return CHANNEL_RC_OK;
2216
0
  }
2217
2218
0
  const UINT error = drdynvc_order_recv(drdynvc, s, TRUE, tunnelType);
2219
0
  Stream_Release(s);
2220
0
  return error;
2221
0
}
2222
2223
/* Moves a channel's data to a tunnel, if the channel exists. The lookup and the move happen under
2224
 * the channel table's lock, so the channel cannot go away in between. */
2225
static void dvcman_move_channel_to_tunnel(drdynvcPlugin* drdynvc, UINT32 channelId,
2226
                                          UINT32 tunnelType)
2227
0
{
2228
0
  WINPR_ASSERT(drdynvc);
2229
0
  DVCMAN* dvcman = (DVCMAN*)drdynvc->channel_mgr;
2230
0
  WINPR_ASSERT(dvcman);
2231
2232
0
  HashTable_Lock(dvcman->channelsById);
2233
0
  DVCMAN_CHANNEL* channel = HashTable_GetItemValue(dvcman->channelsById, &channelId);
2234
0
  if (channel)
2235
0
  {
2236
0
    dvcman_channel_set_tunnel(channel, tunnelType);
2237
0
    WLog_Print(drdynvc->log, WLOG_DEBUG,
2238
0
               "channel %" PRIu32 " (%s) moves to tunnel 0x%08" PRIx32, channelId,
2239
0
               channel->channel_name, tunnelType);
2240
0
  }
2241
0
  HashTable_Unlock(dvcman->channelsById);
2242
0
}
2243
2244
WINPR_ATTR_NODISCARD
2245
static UINT drdynvc_send_soft_sync_response(drdynvcPlugin* drdynvc, const UINT32* tunnels,
2246
                                            UINT32 count)
2247
0
{
2248
0
  WINPR_ASSERT(drdynvc);
2249
0
  WINPR_ASSERT(tunnels || (count == 0));
2250
0
  DVCMAN* dvcman = (DVCMAN*)drdynvc->channel_mgr;
2251
0
  WINPR_ASSERT(dvcman);
2252
2253
  /* count is the number of tunnels we accept, never a value from the wire */
2254
0
  if (count > DRDYNVC_MAX_SOFT_SYNC_TUNNELS)
2255
0
    return ERROR_INVALID_PARAMETER;
2256
0
  wStream* s = StreamPool_Take(dvcman->pool, 6 + sizeof(UINT32) * count);
2257
0
  if (!s)
2258
0
    return CHANNEL_RC_NO_MEMORY;
2259
2260
  /* [MS-RDPEDYC] 2.2.5.2 DYNVC_SOFT_SYNC_RESPONSE */
2261
0
  Stream_Write_UINT8(s, SOFT_SYNC_RESPONSE_PDU << 4);
2262
0
  Stream_Write_UINT8(s, 0); /* Pad */
2263
0
  Stream_Write_UINT32(s, count);
2264
0
  for (UINT32 x = 0; x < count; x++)
2265
0
    Stream_Write_UINT32(s, tunnels[x]);
2266
2267
  /* always on the drdynvc static channel */
2268
0
  return drdynvc_send(drdynvc, s, nullptr);
2269
0
}
2270
2271
/**
2272
 * [MS-RDPEDYC] 2.2.5.1 DYNVC_SOFT_SYNC_REQUEST, positioned after the header byte.
2273
 */
2274
WINPR_ATTR_NODISCARD
2275
static UINT drdynvc_process_soft_sync_request(drdynvcPlugin* drdynvc, wStream* s)
2276
0
{
2277
0
  WINPR_ASSERT(drdynvc);
2278
0
  WINPR_ASSERT(s);
2279
2280
  /* the header byte was read already */
2281
0
  const size_t position = Stream_GetPosition(s);
2282
0
  if (position < 1)
2283
0
    return ERROR_INVALID_DATA;
2284
0
  const size_t start = position - 1;
2285
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 9))
2286
0
    return ERROR_INVALID_DATA;
2287
2288
0
  Stream_Seek_UINT8(s); /* Pad */
2289
0
  const UINT32 length = Stream_Get_UINT32(s);
2290
0
  const UINT16 flags = Stream_Get_UINT16(s);
2291
0
  const UINT16 numberOfTunnels = Stream_Get_UINT16(s);
2292
2293
  /* Windows counts Length from the Length field on, the header byte and Pad are not in it. The
2294
   * channel lists must fit in it, nothing after it belongs to them. */
2295
0
  const size_t total = Stream_Length(s);
2296
0
  if ((total < start) || (total - start < 2))
2297
0
    return ERROR_INVALID_DATA;
2298
0
  const size_t available = total - start - 2;
2299
0
  if ((length < 8) || (length > available))
2300
0
  {
2301
0
    WLog_Print(drdynvc->log, WLOG_ERROR,
2302
0
               "Soft-Sync Request Length %" PRIu32 " with %" PRIuz " bytes present", length,
2303
0
               available);
2304
0
    return ERROR_INVALID_DATA;
2305
0
  }
2306
  /* length <= available, so the request ends inside the stream */
2307
0
  if (!Stream_SetLength(s, total - (available - length)))
2308
0
    return ERROR_INVALID_DATA;
2309
2310
0
  WLog_Print(drdynvc->log, WLOG_DEBUG,
2311
0
             "Soft-Sync Request: flags 0x%04" PRIx16 ", %" PRIu16 " tunnel(s)", flags,
2312
0
             numberOfTunnels);
2313
2314
  /* [MS-RDPEDYC] 2.2.5.1: SOFT_SYNC_TCP_FLUSHED must be set. Without it the server has not
2315
   * said its TCP data for these channels is out, so none of them moves: an empty response
2316
   * keeps them all on TCP. */
2317
0
  if (!(flags & SOFT_SYNC_TCP_FLUSHED))
2318
0
  {
2319
0
    WLog_Print(drdynvc->log, WLOG_WARN,
2320
0
               "Soft-Sync Request without SOFT_SYNC_TCP_FLUSHED, channels stay on TCP");
2321
0
    const UINT error = drdynvc_send_soft_sync_response(drdynvc, nullptr, 0);
2322
0
    if (error != CHANNEL_RC_OK)
2323
0
      return error;
2324
0
    drdynvc->softSyncDone = TRUE;
2325
0
    return drdynvc_tunnel_backlog_flush(drdynvc);
2326
0
  }
2327
2328
0
  const size_t lists = Stream_GetPosition(s);
2329
2330
  /* A tunnel that is still being set up gets its answer once it is up, or has failed. The
2331
   * response must not name a tunnel before the Initiate Multitransport Response for it went
2332
   * out ([MS-RDPEDYC] 3.2.5.3.2). */
2333
0
  if (flags & SOFT_SYNC_CHANNEL_LIST_PRESENT)
2334
0
  {
2335
0
    for (UINT16 x = 0; x < numberOfTunnels; x++)
2336
0
    {
2337
0
      if (!Stream_CheckAndLogRequiredLength(TAG, s, 6))
2338
0
        return ERROR_INVALID_DATA;
2339
0
      const UINT32 tunnelType = Stream_Get_UINT32(s);
2340
0
      const UINT16 count = Stream_Get_UINT16(s);
2341
0
      if (!Stream_CheckAndLogRequiredLengthOfSize(TAG, s, count, sizeof(UINT32)))
2342
0
        return ERROR_INVALID_DATA;
2343
0
      Stream_Seek(s, 4ull * count);
2344
2345
0
      if (freerdp_multitransport_get_tunnel_state(drdynvc->rdpcontext, tunnelType) ==
2346
0
          FREERDP_MULTITRANSPORT_TUNNEL_PENDING)
2347
0
      {
2348
0
        WLog_Print(drdynvc->log, WLOG_DEBUG,
2349
0
                   "tunnel 0x%08" PRIx32 " is not up yet, answering Soft-Sync later",
2350
0
                   tunnelType);
2351
0
        const size_t size = Stream_Length(s) - start;
2352
0
        Stream_Free(drdynvc->pendingSoftSync, TRUE);
2353
0
        drdynvc->pendingSoftSync = Stream_New(nullptr, size);
2354
0
        if (!drdynvc->pendingSoftSync)
2355
0
          return CHANNEL_RC_NO_MEMORY;
2356
0
        Stream_Write(drdynvc->pendingSoftSync, Stream_Buffer(s) + start, size);
2357
0
        Stream_SealLength(drdynvc->pendingSoftSync);
2358
0
        return CHANNEL_RC_OK;
2359
0
      }
2360
0
    }
2361
0
  }
2362
2363
0
  UINT32 accepted[DRDYNVC_MAX_SOFT_SYNC_TUNNELS] = WINPR_C_ARRAY_INIT;
2364
0
  UINT32 acceptedCount = 0;
2365
0
  if (!Stream_SetPosition(s, lists))
2366
0
    return ERROR_INVALID_DATA;
2367
2368
0
  if (flags & SOFT_SYNC_CHANNEL_LIST_PRESENT)
2369
0
  {
2370
0
    for (UINT16 x = 0; x < numberOfTunnels; x++)
2371
0
    {
2372
0
      const UINT32 tunnelType = Stream_Get_UINT32(s);
2373
0
      const UINT16 count = Stream_Get_UINT16(s);
2374
0
      const BOOL ready =
2375
0
          freerdp_multitransport_get_tunnel_state(drdynvc->rdpcontext, tunnelType) ==
2376
0
          FREERDP_MULTITRANSPORT_TUNNEL_READY;
2377
2378
0
      for (UINT16 y = 0; y < count; y++)
2379
0
      {
2380
0
        const UINT32 channelId = Stream_Get_UINT32(s);
2381
0
        if (ready)
2382
0
          dvcman_move_channel_to_tunnel(drdynvc, channelId, tunnelType);
2383
0
      }
2384
2385
0
      if (ready && (acceptedCount < ARRAYSIZE(accepted)))
2386
0
      {
2387
0
        BOOL known = FALSE;
2388
0
        for (UINT32 z = 0; z < acceptedCount; z++)
2389
0
          known |= (accepted[z] == tunnelType);
2390
0
        if (!known)
2391
0
          accepted[acceptedCount++] = tunnelType;
2392
0
      }
2393
0
    }
2394
0
  }
2395
0
  else if (freerdp_multitransport_get_tunnel_state(drdynvc->rdpcontext, TUNNELTYPE_UDPFECR) ==
2396
0
           FREERDP_MULTITRANSPORT_TUNNEL_READY)
2397
0
  {
2398
0
    accepted[acceptedCount++] = TUNNELTYPE_UDPFECR;
2399
0
  }
2400
2401
0
  WLog_Print(drdynvc->log, WLOG_INFO,
2402
0
             "Soft-Sync: %" PRIu32 " tunnel(s) will carry dynamic channel data", acceptedCount);
2403
2404
0
  const UINT error = drdynvc_send_soft_sync_response(drdynvc, accepted, acceptedCount);
2405
0
  if (error != CHANNEL_RC_OK)
2406
0
    return error;
2407
2408
0
  drdynvc->softSyncDone = TRUE;
2409
0
  return drdynvc_tunnel_backlog_flush(drdynvc);
2410
0
}
2411
2412
/* A tunnel came up or went away: a deferred Soft-Sync Request can be answered now. */
2413
WINPR_ATTR_NODISCARD
2414
static UINT drdynvc_tunnel_state_changed(drdynvcPlugin* drdynvc)
2415
0
{
2416
0
  WINPR_ASSERT(drdynvc);
2417
0
  if (drdynvc->pendingSoftSync)
2418
0
  {
2419
0
    wStream* s = drdynvc->pendingSoftSync;
2420
0
    drdynvc->pendingSoftSync = nullptr;
2421
0
    Stream_ResetPosition(s);
2422
0
    const UINT error = drdynvc_order_recv(drdynvc, s, TRUE, 0);
2423
0
    Stream_Free(s, TRUE);
2424
0
    if (error != CHANNEL_RC_OK)
2425
0
      return error;
2426
0
  }
2427
2428
  /* without a tunnel there is nothing to wait for */
2429
0
  if (!drdynvc->softSyncDone &&
2430
0
      (freerdp_multitransport_get_tunnel_state(drdynvc->rdpcontext, TUNNELTYPE_UDPFECR) ==
2431
0
       FREERDP_MULTITRANSPORT_TUNNEL_NONE))
2432
0
    return drdynvc_tunnel_backlog_flush(drdynvc);
2433
0
  return CHANNEL_RC_OK;
2434
0
}
2435
2436
/**
2437
 * Function description
2438
 *
2439
 * @return 0 on success, otherwise a Win32 error code
2440
 */
2441
WINPR_ATTR_NODISCARD
2442
static UINT drdynvc_order_recv(drdynvcPlugin* drdynvc, wStream* s, UINT32 ThreadingFlags,
2443
                               UINT32 tunnelType)
2444
0
{
2445
0
  WINPR_ASSERT(drdynvc);
2446
0
  if (!Stream_CheckAndLogRequiredLength(TAG, s, 1))
2447
0
    return ERROR_INVALID_DATA;
2448
2449
0
  UINT8 value = Stream_Get_UINT8(s);
2450
0
  const UINT8 Cmd = (value & 0xf0) >> 4;
2451
0
  const UINT8 Sp = (value & 0x0c) >> 2;
2452
0
  const UINT8 cbChId = (value & 0x03) >> 0;
2453
0
  WLog_Print(drdynvc->log, WLOG_TRACE, "order_recv: Cmd=%s, Sp=%" PRIu8 " cbChId=%" PRIu8,
2454
0
             drdynvc_get_packet_type(Cmd), Sp, cbChId);
2455
2456
  /* Capabilities and Soft-Sync travel on the drdynvc static channel only, a tunnel carries the
2457
   * channels themselves. Accepting a Soft-Sync Request from the tunnel would also let it run
2458
   * while the tunnel backlog is being handed up. */
2459
0
  if ((tunnelType != 0) && ((Cmd == CAPABILITY_REQUEST_PDU) || (Cmd == SOFT_SYNC_REQUEST_PDU)))
2460
0
  {
2461
0
    WLog_Print(drdynvc->log, WLOG_ERROR, "%s on tunnel 0x%08" PRIx32 ", rejected",
2462
0
               drdynvc_get_packet_type(Cmd), tunnelType);
2463
0
    return ERROR_INVALID_DATA;
2464
0
  }
2465
2466
0
  switch (Cmd)
2467
0
  {
2468
0
    case CAPABILITY_REQUEST_PDU:
2469
0
      return drdynvc_process_capability_request(drdynvc, Sp, cbChId, s);
2470
2471
0
    case CREATE_REQUEST_PDU:
2472
0
      return drdynvc_process_create_request(drdynvc, Sp, cbChId, s, tunnelType);
2473
2474
0
    case DATA_FIRST_PDU:
2475
0
    case DATA_FIRST_COMPRESSED_PDU:
2476
0
      return drdynvc_process_data_first(drdynvc, Sp, cbChId, s,
2477
0
                                        (Cmd == DATA_FIRST_COMPRESSED_PDU), ThreadingFlags,
2478
0
                                        tunnelType);
2479
2480
0
    case DATA_PDU:
2481
0
    case DATA_COMPRESSED_PDU:
2482
0
      return drdynvc_process_data(drdynvc, Sp, cbChId, s, (Cmd == DATA_COMPRESSED_PDU),
2483
0
                                  ThreadingFlags, tunnelType);
2484
2485
0
    case CLOSE_REQUEST_PDU:
2486
0
      return drdynvc_process_close_request(drdynvc, Sp, cbChId, s, tunnelType);
2487
2488
0
    case SOFT_SYNC_REQUEST_PDU:
2489
0
      return drdynvc_process_soft_sync_request(drdynvc, s);
2490
2491
0
    case SOFT_SYNC_RESPONSE_PDU:
2492
0
      WLog_Print(drdynvc->log, WLOG_ERROR,
2493
0
                 "not expecting a SOFT_SYNC_RESPONSE_PDU as a client");
2494
0
      return ERROR_INTERNAL_ERROR;
2495
2496
0
    default:
2497
0
      WLog_Print(drdynvc->log, WLOG_ERROR, "unknown drdynvc cmd 0x%x", Cmd);
2498
0
      return ERROR_INTERNAL_ERROR;
2499
0
  }
2500
0
}
2501
2502
/**
2503
 * Function description
2504
 *
2505
 * @return 0 on success, otherwise a Win32 error code
2506
 */
2507
WINPR_ATTR_NODISCARD
2508
static UINT drdynvc_virtual_channel_event_data_received(drdynvcPlugin* drdynvc, void* pData,
2509
                                                        UINT32 dataLength, UINT32 totalLength,
2510
                                                        UINT32 dataFlags)
2511
0
{
2512
0
  wStream* data_in = nullptr;
2513
2514
0
  WINPR_ASSERT(drdynvc);
2515
0
  if ((dataFlags & CHANNEL_FLAG_SUSPEND) || (dataFlags & CHANNEL_FLAG_RESUME))
2516
0
  {
2517
0
    return CHANNEL_RC_OK;
2518
0
  }
2519
2520
0
  if (dataFlags & CHANNEL_FLAG_FIRST)
2521
0
  {
2522
0
    if (drdynvc->firstFlagReceived)
2523
0
      return ERROR_INVALID_DATA;
2524
0
    drdynvc->firstFlagReceived = TRUE;
2525
2526
0
    DVCMAN* mgr = (DVCMAN*)drdynvc->channel_mgr;
2527
0
    if (drdynvc->data_in)
2528
0
      Stream_Release(drdynvc->data_in);
2529
2530
0
    drdynvc->data_in = StreamPool_Take(mgr->pool, dataLength);
2531
0
    drdynvc->totalLength = totalLength;
2532
0
  }
2533
2534
0
  if (!(data_in = drdynvc->data_in))
2535
0
  {
2536
0
    WLog_Print(drdynvc->log, WLOG_ERROR, "StreamPool_Take failed!");
2537
0
    return CHANNEL_RC_NO_MEMORY;
2538
0
  }
2539
2540
0
  if (!Stream_EnsureRemainingCapacity(data_in, dataLength))
2541
0
  {
2542
0
    WLog_Print(drdynvc->log, WLOG_ERROR, "Stream_EnsureRemainingCapacity failed!");
2543
0
    Stream_Release(drdynvc->data_in);
2544
0
    drdynvc->data_in = nullptr;
2545
0
    return ERROR_INTERNAL_ERROR;
2546
0
  }
2547
2548
0
  Stream_Write(data_in, pData, dataLength);
2549
2550
0
  if ((Stream_GetPosition(data_in) > totalLength) || (drdynvc->totalLength != totalLength))
2551
0
  {
2552
0
    Stream_Release(drdynvc->data_in);
2553
0
    drdynvc->data_in = nullptr;
2554
0
    return ERROR_INVALID_DATA;
2555
0
  }
2556
2557
0
  if (dataFlags & CHANNEL_FLAG_LAST)
2558
0
  {
2559
0
    if (!drdynvc->firstFlagReceived)
2560
0
      return ERROR_INVALID_DATA;
2561
0
    drdynvc->firstFlagReceived = FALSE;
2562
2563
0
    const size_t pos = Stream_GetPosition(data_in);
2564
0
    if (drdynvc->totalLength != pos)
2565
0
    {
2566
0
      WLog_Print(drdynvc->log, WLOG_ERROR, "drdynvc_plugin_process_received: read error");
2567
0
      return ERROR_INVALID_DATA;
2568
0
    }
2569
2570
0
    drdynvc->totalLength = 0;
2571
0
    drdynvc->data_in = nullptr;
2572
0
    Stream_SealLength(data_in);
2573
0
    Stream_ResetPosition(data_in);
2574
2575
0
    if (drdynvc->async)
2576
0
    {
2577
0
      if (!MessageQueue_Post(drdynvc->queue, nullptr, DRDYNVC_MSG_DATA, (void*)data_in,
2578
0
                             nullptr))
2579
0
      {
2580
0
        WLog_Print(drdynvc->log, WLOG_ERROR, "MessageQueue_Post failed!");
2581
0
        return ERROR_INTERNAL_ERROR;
2582
0
      }
2583
0
    }
2584
0
    else
2585
0
    {
2586
0
      UINT error = drdynvc_order_recv(drdynvc, data_in, TRUE, 0);
2587
0
      Stream_Release(data_in);
2588
2589
0
      if (error)
2590
0
      {
2591
0
        WLog_Print(drdynvc->log, WLOG_WARN,
2592
0
                   "drdynvc_order_recv failed with error %" PRIu32 "!", error);
2593
0
        return error;
2594
0
      }
2595
0
    }
2596
0
  }
2597
2598
0
  return CHANNEL_RC_OK;
2599
0
}
2600
2601
static void VCAPITYPE drdynvc_virtual_channel_open_event_ex(LPVOID lpUserParam, DWORD openHandle,
2602
                                                            UINT event, LPVOID pData,
2603
                                                            UINT32 dataLength, UINT32 totalLength,
2604
                                                            UINT32 dataFlags)
2605
0
{
2606
0
  UINT error = CHANNEL_RC_OK;
2607
0
  drdynvcPlugin* drdynvc = (drdynvcPlugin*)lpUserParam;
2608
2609
0
  WINPR_ASSERT(drdynvc);
2610
0
  switch (event)
2611
0
  {
2612
0
    case CHANNEL_EVENT_DATA_RECEIVED:
2613
0
      if (!drdynvc || (drdynvc->OpenHandle != openHandle))
2614
0
      {
2615
0
        WLog_ERR(TAG, "drdynvc_virtual_channel_open_event: error no match");
2616
0
        return;
2617
0
      }
2618
0
      if ((error = drdynvc_virtual_channel_event_data_received(drdynvc, pData, dataLength,
2619
0
                                                               totalLength, dataFlags)))
2620
0
        WLog_Print(drdynvc->log, WLOG_ERROR,
2621
0
                   "drdynvc_virtual_channel_event_data_received failed with error %" PRIu32
2622
0
                   "",
2623
0
                   error);
2624
2625
0
      break;
2626
2627
0
    case CHANNEL_EVENT_WRITE_CANCELLED:
2628
0
    case CHANNEL_EVENT_WRITE_COMPLETE:
2629
0
    {
2630
0
      wStream* s = (wStream*)pData;
2631
0
      Stream_Release(s);
2632
0
    }
2633
0
    break;
2634
2635
0
    case CHANNEL_EVENT_USER:
2636
0
      break;
2637
0
    default:
2638
0
      break;
2639
0
  }
2640
2641
0
  if (error && drdynvc && drdynvc->rdpcontext)
2642
0
    setChannelError(drdynvc->rdpcontext, error,
2643
0
                    "drdynvc_virtual_channel_open_event reported an error");
2644
0
}
2645
2646
WINPR_ATTR_NODISCARD
2647
static DWORD WINAPI drdynvc_virtual_channel_client_thread(LPVOID arg)
2648
0
{
2649
  /* TODO: rewrite this */
2650
0
  wStream* data = nullptr;
2651
0
  wMessage message = WINPR_C_ARRAY_INIT;
2652
0
  UINT error = CHANNEL_RC_OK;
2653
0
  drdynvcPlugin* drdynvc = (drdynvcPlugin*)arg;
2654
2655
0
  if (!drdynvc)
2656
0
  {
2657
0
    ExitThread((DWORD)CHANNEL_RC_BAD_CHANNEL_HANDLE);
2658
0
    return CHANNEL_RC_BAD_CHANNEL_HANDLE;
2659
0
  }
2660
2661
0
  while (1)
2662
0
  {
2663
0
    if (!MessageQueue_Wait(drdynvc->queue))
2664
0
    {
2665
0
      WLog_Print(drdynvc->log, WLOG_ERROR, "MessageQueue_Wait failed!");
2666
0
      error = ERROR_INTERNAL_ERROR;
2667
0
      break;
2668
0
    }
2669
2670
0
    if (!MessageQueue_Peek(drdynvc->queue, &message, TRUE))
2671
0
    {
2672
0
      WLog_Print(drdynvc->log, WLOG_ERROR, "MessageQueue_Peek failed!");
2673
0
      error = ERROR_INTERNAL_ERROR;
2674
0
      break;
2675
0
    }
2676
2677
0
    if (message.id == WMQ_QUIT)
2678
0
      break;
2679
2680
0
    if (message.id == DRDYNVC_MSG_DATA)
2681
0
    {
2682
0
      UINT32 ThreadingFlags = TRUE;
2683
0
      data = (wStream*)message.wParam;
2684
2685
0
      if ((error = drdynvc_order_recv(drdynvc, data, ThreadingFlags, 0)))
2686
0
      {
2687
0
        WLog_Print(drdynvc->log, WLOG_WARN,
2688
0
                   "drdynvc_order_recv failed with error %" PRIu32 "!", error);
2689
0
      }
2690
2691
0
      Stream_Release(data);
2692
0
    }
2693
0
    else if (message.id == DRDYNVC_MSG_TUNNEL_DATA)
2694
0
    {
2695
0
      data = (wStream*)message.wParam;
2696
0
      const UINT32 tunnelType = WINPR_ASSERTING_INT_CAST(UINT32, (UINT_PTR)message.lParam);
2697
2698
0
      if ((error = drdynvc_receive_tunnel_pdu(drdynvc, data, tunnelType)))
2699
0
      {
2700
0
        WLog_Print(drdynvc->log, WLOG_WARN,
2701
0
                   "drdynvc_receive_tunnel_pdu failed with error %" PRIu32 "!", error);
2702
0
      }
2703
0
    }
2704
0
    else if (message.id == DRDYNVC_MSG_TUNNEL_STATE)
2705
0
    {
2706
0
      if ((error = drdynvc_tunnel_state_changed(drdynvc)))
2707
0
      {
2708
0
        WLog_Print(drdynvc->log, WLOG_WARN,
2709
0
                   "drdynvc_tunnel_state_changed failed with error %" PRIu32 "!", error);
2710
0
      }
2711
0
    }
2712
0
  }
2713
2714
0
  {
2715
    /* Disconnect remaining dynamic channels that the server did not.
2716
     * This is required to properly shut down channels by calling the appropriate
2717
     * event handlers. */
2718
0
    DVCMAN* drdynvcMgr = (DVCMAN*)drdynvc->channel_mgr;
2719
2720
0
    HashTable_Clear(drdynvcMgr->channelsById);
2721
0
  }
2722
2723
0
  if (error && drdynvc->rdpcontext)
2724
0
    setChannelError(drdynvc->rdpcontext, error,
2725
0
                    "drdynvc_virtual_channel_client_thread reported an error");
2726
2727
0
  ExitThread((DWORD)error);
2728
0
  return error;
2729
0
}
2730
2731
static void drdynvc_queue_object_free(void* obj)
2732
0
{
2733
0
  wStream* s = nullptr;
2734
0
  wMessage* msg = (wMessage*)obj;
2735
2736
0
  if (!msg || ((msg->id != DRDYNVC_MSG_DATA) && (msg->id != DRDYNVC_MSG_TUNNEL_DATA)))
2737
0
    return;
2738
2739
0
  s = (wStream*)msg->wParam;
2740
2741
0
  if (s)
2742
0
    Stream_Release(s);
2743
0
}
2744
2745
/* Multitransport hooks, called on the thread running the main loop, and tunnel data on the
2746
 * tunnel thread when the plugin has its own thread. Like data from the static channel, a failure
2747
 * here ends the connection. */
2748
WINPR_ATTR_NODISCARD
2749
static BOOL drdynvc_on_tunnel_data(void* custom, UINT32 tunnelType, wStream* s)
2750
0
{
2751
0
  drdynvcPlugin* drdynvc = custom;
2752
0
  WINPR_ASSERT(drdynvc);
2753
0
  WINPR_ASSERT(s);
2754
2755
0
  if (!drdynvc->channel_mgr || (Stream_Length(s) == 0))
2756
0
  {
2757
0
    Stream_Release(s);
2758
0
    return TRUE;
2759
0
  }
2760
2761
0
  if (drdynvc->async)
2762
0
  {
2763
0
    if (!MessageQueue_Post(drdynvc->queue, nullptr, DRDYNVC_MSG_TUNNEL_DATA, s,
2764
0
                           (void*)(UINT_PTR)tunnelType))
2765
0
    {
2766
0
      WLog_Print(drdynvc->log, WLOG_ERROR, "MessageQueue_Post failed!");
2767
0
      Stream_Release(s);
2768
0
      return FALSE;
2769
0
    }
2770
0
    return TRUE;
2771
0
  }
2772
2773
0
  const UINT error = drdynvc_receive_tunnel_pdu(drdynvc, s, tunnelType);
2774
0
  if (error)
2775
0
  {
2776
0
    WLog_Print(drdynvc->log, WLOG_WARN,
2777
0
               "drdynvc_receive_tunnel_pdu failed with error %" PRIu32 "!", error);
2778
0
    return FALSE;
2779
0
  }
2780
0
  return TRUE;
2781
0
}
2782
2783
WINPR_ATTR_NODISCARD
2784
static BOOL drdynvc_on_tunnel_state(void* custom, UINT32 tunnelType,
2785
                                    FreeRDP_MultitransportTunnelState state)
2786
0
{
2787
0
  drdynvcPlugin* drdynvc = custom;
2788
0
  WINPR_ASSERT(drdynvc);
2789
2790
0
  WLog_Print(drdynvc->log, WLOG_DEBUG, "tunnel 0x%08" PRIx32 " is %s", tunnelType,
2791
0
             (state == FREERDP_MULTITRANSPORT_TUNNEL_READY) ? "ready" : "gone");
2792
2793
0
  if (drdynvc->async)
2794
0
  {
2795
0
    if (!MessageQueue_Post(drdynvc->queue, nullptr, DRDYNVC_MSG_TUNNEL_STATE, nullptr, nullptr))
2796
0
    {
2797
0
      WLog_Print(drdynvc->log, WLOG_ERROR, "MessageQueue_Post failed!");
2798
0
      return FALSE;
2799
0
    }
2800
0
    return TRUE;
2801
0
  }
2802
2803
0
  const UINT error = drdynvc_tunnel_state_changed(drdynvc);
2804
0
  if (error)
2805
0
  {
2806
0
    WLog_Print(drdynvc->log, WLOG_WARN,
2807
0
               "drdynvc_tunnel_state_changed failed with error %" PRIu32 "!", error);
2808
0
    return FALSE;
2809
0
  }
2810
0
  return TRUE;
2811
0
}
2812
2813
static void drdynvc_soft_sync_reset(drdynvcPlugin* drdynvc)
2814
0
{
2815
0
  WINPR_ASSERT(drdynvc);
2816
0
  drdynvc->softSyncDone = FALSE;
2817
0
  Stream_Free(drdynvc->pendingSoftSync, TRUE);
2818
0
  drdynvc->pendingSoftSync = nullptr;
2819
0
  drdynvc_tunnel_backlog_clear(drdynvc);
2820
0
}
2821
2822
/* After this no tunnel event reaches the plugin. */
2823
static void drdynvc_tunnel_hooks_remove(drdynvcPlugin* drdynvc)
2824
0
{
2825
0
  WINPR_ASSERT(drdynvc);
2826
2827
  /* fails only without a multitransport, then nothing was registered */
2828
0
  if (!freerdp_multitransport_set_dvc_callbacks(drdynvc->rdpcontext, nullptr, nullptr))
2829
0
    WLog_Print(drdynvc->log, WLOG_DEBUG, "no multitransport tunnel hooks to remove");
2830
0
}
2831
2832
WINPR_ATTR_NODISCARD
2833
static UINT drdynvc_virtual_channel_event_initialized(drdynvcPlugin* drdynvc, LPVOID pData,
2834
                                                      UINT32 dataLength)
2835
0
{
2836
0
  wObject* obj = nullptr;
2837
0
  WINPR_UNUSED(pData);
2838
0
  WINPR_UNUSED(dataLength);
2839
2840
0
  if (!drdynvc)
2841
0
    goto error;
2842
2843
0
  drdynvc->queue = MessageQueue_New(nullptr);
2844
2845
0
  if (!drdynvc->queue)
2846
0
  {
2847
0
    WLog_Print(drdynvc->log, WLOG_ERROR, "MessageQueue_New failed!");
2848
0
    goto error;
2849
0
  }
2850
2851
0
  obj = MessageQueue_Object(drdynvc->queue);
2852
0
  obj->fnObjectFree = drdynvc_queue_object_free;
2853
0
  drdynvc->channel_mgr = dvcman_new(drdynvc);
2854
2855
0
  if (!drdynvc->channel_mgr)
2856
0
  {
2857
0
    WLog_Print(drdynvc->log, WLOG_ERROR, "dvcman_new failed!");
2858
0
    goto error;
2859
0
  }
2860
2861
0
  return CHANNEL_RC_OK;
2862
0
error:
2863
0
  return ERROR_INTERNAL_ERROR;
2864
0
}
2865
2866
/**
2867
 * Function description
2868
 *
2869
 * @return 0 on success, otherwise a Win32 error code
2870
 */
2871
WINPR_ATTR_NODISCARD
2872
static UINT drdynvc_virtual_channel_event_connected(drdynvcPlugin* drdynvc, LPVOID pData,
2873
                                                    UINT32 dataLength)
2874
0
{
2875
0
  UINT error = 0;
2876
0
  UINT32 status = 0;
2877
0
  rdpSettings* settings = nullptr;
2878
2879
0
  WINPR_ASSERT(drdynvc);
2880
0
  WINPR_UNUSED(pData);
2881
0
  WINPR_UNUSED(dataLength);
2882
2883
0
  if (!drdynvc)
2884
0
    return CHANNEL_RC_BAD_CHANNEL_HANDLE;
2885
2886
0
  WINPR_ASSERT(drdynvc->channelEntryPoints.pVirtualChannelOpenEx);
2887
0
  status = drdynvc->channelEntryPoints.pVirtualChannelOpenEx(
2888
0
      drdynvc->InitHandle, &drdynvc->OpenHandle, drdynvc->channelDef.name,
2889
0
      drdynvc_virtual_channel_open_event_ex);
2890
2891
0
  if (status != CHANNEL_RC_OK)
2892
0
  {
2893
0
    WLog_Print(drdynvc->log, WLOG_ERROR, "pVirtualChannelOpen failed with %s [%08" PRIX32 "]",
2894
0
               WTSErrorToString(status), status);
2895
0
    return status;
2896
0
  }
2897
2898
0
  WINPR_ASSERT(drdynvc->rdpcontext);
2899
0
  settings = drdynvc->rdpcontext->settings;
2900
0
  WINPR_ASSERT(settings);
2901
2902
0
  for (UINT32 index = 0;
2903
0
       index < freerdp_settings_get_uint32(settings, FreeRDP_DynamicChannelCount); index++)
2904
0
  {
2905
0
    const ADDIN_ARGV* args =
2906
0
        freerdp_settings_get_pointer_array(settings, FreeRDP_DynamicChannelArray, index);
2907
0
    error = dvcman_load_addin(drdynvc, drdynvc->channel_mgr, args, drdynvc->rdpcontext);
2908
2909
0
    if (CHANNEL_RC_OK != error)
2910
0
      goto error;
2911
0
  }
2912
2913
0
  if ((error = dvcman_init(drdynvc, drdynvc->channel_mgr)))
2914
0
  {
2915
0
    WLog_Print(drdynvc->log, WLOG_ERROR, "dvcman_init failed with error %" PRIu32 "!", error);
2916
0
    goto error;
2917
0
  }
2918
2919
0
  drdynvc->state = DRDYNVC_STATE_CAPABILITIES;
2920
2921
0
  if (drdynvc->async)
2922
0
  {
2923
0
    if (!(drdynvc->thread = CreateThread(nullptr, 0, drdynvc_virtual_channel_client_thread,
2924
0
                                         (void*)drdynvc, 0, nullptr)))
2925
0
    {
2926
0
      error = ERROR_INTERNAL_ERROR;
2927
0
      WLog_Print(drdynvc->log, WLOG_ERROR, "CreateThread failed!");
2928
0
      goto error;
2929
0
    }
2930
2931
0
    if (!SetThreadPriority(drdynvc->thread, THREAD_PRIORITY_HIGHEST))
2932
0
      WLog_Print(drdynvc->log, WLOG_WARN, "SetThreadPriority failed, ignoring.");
2933
0
  }
2934
2935
0
  {
2936
0
    drdynvc_soft_sync_reset(drdynvc);
2937
0
    const FreeRDP_MultitransportDvcCallbacks callbacks = {
2938
0
      .TunnelDataReceived = drdynvc_on_tunnel_data,
2939
0
      .TunnelStateChanged = drdynvc_on_tunnel_state,
2940
0
      .DataOnTunnelThread = drdynvc->async
2941
0
    };
2942
0
    if (!freerdp_multitransport_set_dvc_callbacks(drdynvc->rdpcontext, &callbacks, drdynvc))
2943
0
      WLog_Print(drdynvc->log, WLOG_WARN,
2944
0
                 "multitransport tunnels unavailable, using the static channel only");
2945
0
  }
2946
2947
0
error:
2948
0
  return error;
2949
0
}
2950
2951
/**
2952
 * Function description
2953
 *
2954
 * @return 0 on success, otherwise a Win32 error code
2955
 */
2956
WINPR_ATTR_NODISCARD
2957
static UINT drdynvc_virtual_channel_event_disconnected(drdynvcPlugin* drdynvc)
2958
0
{
2959
0
  UINT status = 0;
2960
2961
0
  if (!drdynvc)
2962
0
    return CHANNEL_RC_BAD_CHANNEL_HANDLE;
2963
2964
  /* registered on initialization, whether the channel opened or not */
2965
0
  drdynvc_tunnel_hooks_remove(drdynvc);
2966
2967
0
  if (drdynvc->OpenHandle == 0)
2968
0
    return CHANNEL_RC_OK;
2969
2970
0
  if (drdynvc->queue)
2971
0
  {
2972
0
    if (!MessageQueue_PostQuit(drdynvc->queue, 0))
2973
0
    {
2974
0
      status = GetLastError();
2975
0
      WLog_Print(drdynvc->log, WLOG_ERROR,
2976
0
                 "MessageQueue_PostQuit failed with error %" PRIu32 "", status);
2977
0
      return status;
2978
0
    }
2979
0
  }
2980
2981
0
  if (drdynvc->thread)
2982
0
  {
2983
0
    if (WaitForSingleObject(drdynvc->thread, INFINITE) != WAIT_OBJECT_0)
2984
0
    {
2985
0
      status = GetLastError();
2986
0
      WLog_Print(drdynvc->log, WLOG_ERROR,
2987
0
                 "WaitForSingleObject failed with error %" PRIu32 "", status);
2988
0
      return status;
2989
0
    }
2990
2991
0
    (void)CloseHandle(drdynvc->thread);
2992
0
    drdynvc->thread = nullptr;
2993
0
  }
2994
0
  else
2995
0
  {
2996
0
    {
2997
      /* Disconnect remaining dynamic channels that the server did not.
2998
       * This is required to properly shut down channels by calling the appropriate
2999
       * event handlers. */
3000
0
      DVCMAN* drdynvcMgr = (DVCMAN*)drdynvc->channel_mgr;
3001
3002
0
      HashTable_Clear(drdynvcMgr->channelsById);
3003
0
    }
3004
0
  }
3005
3006
0
  WINPR_ASSERT(drdynvc->channelEntryPoints.pVirtualChannelCloseEx);
3007
0
  status = drdynvc->channelEntryPoints.pVirtualChannelCloseEx(drdynvc->InitHandle,
3008
0
                                                              drdynvc->OpenHandle);
3009
3010
0
  if (status != CHANNEL_RC_OK)
3011
0
  {
3012
0
    WLog_Print(drdynvc->log, WLOG_ERROR, "pVirtualChannelClose failed with %s [%08" PRIX32 "]",
3013
0
               WTSErrorToString(status), status);
3014
0
  }
3015
3016
0
  drdynvc_soft_sync_reset(drdynvc);
3017
0
  dvcman_clear(drdynvc, drdynvc->channel_mgr);
3018
0
  if (drdynvc->queue)
3019
0
    MessageQueue_Clear(drdynvc->queue);
3020
0
  drdynvc->OpenHandle = 0;
3021
3022
0
  if (drdynvc->data_in)
3023
0
  {
3024
0
    Stream_Release(drdynvc->data_in);
3025
0
    drdynvc->data_in = nullptr;
3026
0
  }
3027
3028
0
  return status;
3029
0
}
3030
3031
/**
3032
 * Function description
3033
 *
3034
 * @return 0 on success, otherwise a Win32 error code
3035
 */
3036
WINPR_ATTR_NODISCARD
3037
static UINT drdynvc_virtual_channel_event_terminated(drdynvcPlugin* drdynvc)
3038
0
{
3039
0
  if (!drdynvc)
3040
0
    return CHANNEL_RC_BAD_CHANNEL_HANDLE;
3041
3042
  /* the hooks point at this plugin, which is freed below */
3043
0
  drdynvc_tunnel_hooks_remove(drdynvc);
3044
3045
0
  MessageQueue_Free(drdynvc->queue);
3046
0
  drdynvc->queue = nullptr;
3047
3048
0
  drdynvc_soft_sync_reset(drdynvc);
3049
0
  free((void*)drdynvc->tunnelBacklog);
3050
0
  free(drdynvc->tunnelBacklogTypes);
3051
3052
0
  if (drdynvc->channel_mgr)
3053
0
  {
3054
0
    dvcman_free(drdynvc, drdynvc->channel_mgr);
3055
0
    drdynvc->channel_mgr = nullptr;
3056
0
  }
3057
0
  drdynvc->InitHandle = nullptr;
3058
0
  free(drdynvc->context);
3059
0
  free(drdynvc);
3060
0
  return CHANNEL_RC_OK;
3061
0
}
3062
3063
WINPR_ATTR_NODISCARD
3064
static UINT drdynvc_virtual_channel_event_attached(drdynvcPlugin* drdynvc)
3065
0
{
3066
0
  UINT error = CHANNEL_RC_OK;
3067
0
  DVCMAN* dvcman = nullptr;
3068
3069
0
  if (!drdynvc)
3070
0
    return CHANNEL_RC_BAD_CHANNEL_HANDLE;
3071
3072
0
  dvcman = (DVCMAN*)drdynvc->channel_mgr;
3073
3074
0
  if (!dvcman)
3075
0
    return CHANNEL_RC_BAD_CHANNEL_HANDLE;
3076
3077
0
  ArrayList_Lock(dvcman->plugins);
3078
0
  for (size_t i = 0; i < ArrayList_Count(dvcman->plugins); i++)
3079
0
  {
3080
0
    IWTSPlugin* pPlugin = ArrayList_GetItem(dvcman->plugins, i);
3081
3082
0
    error = IFCALLRESULT(CHANNEL_RC_OK, pPlugin->Attached, pPlugin);
3083
0
    if (error != CHANNEL_RC_OK)
3084
0
    {
3085
0
      WLog_Print(drdynvc->log, WLOG_ERROR, "Attach failed with error %" PRIu32 "!", error);
3086
0
      goto fail;
3087
0
    }
3088
0
  }
3089
3090
0
fail:
3091
0
  ArrayList_Unlock(dvcman->plugins);
3092
0
  return error;
3093
0
}
3094
3095
WINPR_ATTR_NODISCARD
3096
static UINT drdynvc_virtual_channel_event_detached(drdynvcPlugin* drdynvc)
3097
0
{
3098
0
  UINT error = CHANNEL_RC_OK;
3099
0
  DVCMAN* dvcman = nullptr;
3100
3101
0
  if (!drdynvc)
3102
0
    return CHANNEL_RC_BAD_CHANNEL_HANDLE;
3103
3104
0
  dvcman = (DVCMAN*)drdynvc->channel_mgr;
3105
3106
0
  if (!dvcman)
3107
0
    return CHANNEL_RC_BAD_CHANNEL_HANDLE;
3108
3109
0
  ArrayList_Lock(dvcman->plugins);
3110
0
  for (size_t i = 0; i < ArrayList_Count(dvcman->plugins); i++)
3111
0
  {
3112
0
    IWTSPlugin* pPlugin = ArrayList_GetItem(dvcman->plugins, i);
3113
3114
0
    error = IFCALLRESULT(CHANNEL_RC_OK, pPlugin->Detached, pPlugin);
3115
0
    if (error != CHANNEL_RC_OK)
3116
0
    {
3117
0
      WLog_Print(drdynvc->log, WLOG_ERROR, "Detach failed with error %" PRIu32 "!", error);
3118
0
      goto fail;
3119
0
    }
3120
0
  }
3121
3122
0
fail:
3123
0
  ArrayList_Unlock(dvcman->plugins);
3124
3125
0
  return error;
3126
0
}
3127
3128
static VOID VCAPITYPE drdynvc_virtual_channel_init_event_ex(LPVOID lpUserParam, LPVOID pInitHandle,
3129
                                                            UINT event, LPVOID pData,
3130
                                                            UINT dataLength)
3131
0
{
3132
0
  UINT error = CHANNEL_RC_OK;
3133
0
  drdynvcPlugin* drdynvc = (drdynvcPlugin*)lpUserParam;
3134
3135
0
  if (!drdynvc || (drdynvc->InitHandle != pInitHandle))
3136
0
  {
3137
0
    WLog_ERR(TAG, "drdynvc_virtual_channel_init_event: error no match");
3138
0
    return;
3139
0
  }
3140
3141
0
  switch (event)
3142
0
  {
3143
0
    case CHANNEL_EVENT_INITIALIZED:
3144
0
      error = drdynvc_virtual_channel_event_initialized(drdynvc, pData, dataLength);
3145
0
      break;
3146
0
    case CHANNEL_EVENT_CONNECTED:
3147
0
      if ((error = drdynvc_virtual_channel_event_connected(drdynvc, pData, dataLength)))
3148
0
        WLog_Print(drdynvc->log, WLOG_ERROR,
3149
0
                   "drdynvc_virtual_channel_event_connected failed with error %" PRIu32 "",
3150
0
                   error);
3151
3152
0
      break;
3153
3154
0
    case CHANNEL_EVENT_DISCONNECTED:
3155
0
      if ((error = drdynvc_virtual_channel_event_disconnected(drdynvc)))
3156
0
        WLog_Print(drdynvc->log, WLOG_ERROR,
3157
0
                   "drdynvc_virtual_channel_event_disconnected failed with error %" PRIu32
3158
0
                   "",
3159
0
                   error);
3160
3161
0
      break;
3162
3163
0
    case CHANNEL_EVENT_TERMINATED:
3164
0
      if ((error = drdynvc_virtual_channel_event_terminated(drdynvc)))
3165
0
        WLog_Print(drdynvc->log, WLOG_ERROR,
3166
0
                   "drdynvc_virtual_channel_event_terminated failed with error %" PRIu32 "",
3167
0
                   error);
3168
3169
0
      break;
3170
3171
0
    case CHANNEL_EVENT_ATTACHED:
3172
0
      if ((error = drdynvc_virtual_channel_event_attached(drdynvc)))
3173
0
        WLog_Print(drdynvc->log, WLOG_ERROR,
3174
0
                   "drdynvc_virtual_channel_event_attached failed with error %" PRIu32 "",
3175
0
                   error);
3176
3177
0
      break;
3178
3179
0
    case CHANNEL_EVENT_DETACHED:
3180
0
      if ((error = drdynvc_virtual_channel_event_detached(drdynvc)))
3181
0
        WLog_Print(drdynvc->log, WLOG_ERROR,
3182
0
                   "drdynvc_virtual_channel_event_detached failed with error %" PRIu32 "",
3183
0
                   error);
3184
3185
0
      break;
3186
3187
0
    default:
3188
0
      break;
3189
0
  }
3190
3191
0
  if (error && drdynvc->rdpcontext)
3192
0
    setChannelError(drdynvc->rdpcontext, error,
3193
0
                    "drdynvc_virtual_channel_init_event_ex reported an error");
3194
0
}
3195
3196
/**
3197
 * Channel Client Interface
3198
 */
3199
3200
WINPR_ATTR_NODISCARD
3201
static int drdynvc_get_version(DrdynvcClientContext* context)
3202
0
{
3203
0
  WINPR_ASSERT(context);
3204
0
  drdynvcPlugin* drdynvc = (drdynvcPlugin*)context->handle;
3205
0
  WINPR_ASSERT(drdynvc);
3206
0
  return drdynvc->version;
3207
0
}
3208
3209
/* drdynvc is always built-in */
3210
#define VirtualChannelEntryEx drdynvc_VirtualChannelEntryEx
3211
3212
FREERDP_ENTRY_POINT(BOOL VCAPITYPE VirtualChannelEntryEx(PCHANNEL_ENTRY_POINTS_EX pEntryPoints,
3213
                                                         PVOID pInitHandle))
3214
0
{
3215
0
  UINT rc = 0;
3216
0
  drdynvcPlugin* drdynvc = nullptr;
3217
0
  DrdynvcClientContext* context = nullptr;
3218
0
  CHANNEL_ENTRY_POINTS_FREERDP_EX* pEntryPointsEx = nullptr;
3219
0
  drdynvc = (drdynvcPlugin*)calloc(1, sizeof(drdynvcPlugin));
3220
3221
0
  WINPR_ASSERT(pEntryPoints);
3222
0
  if (!drdynvc)
3223
0
  {
3224
0
    WLog_ERR(TAG, "calloc failed!");
3225
0
    return FALSE;
3226
0
  }
3227
3228
0
  drdynvc->channelDef.options =
3229
0
      CHANNEL_OPTION_INITIALIZED | CHANNEL_OPTION_ENCRYPT_RDP | CHANNEL_OPTION_COMPRESS_RDP;
3230
0
  (void)sprintf_s(drdynvc->channelDef.name, ARRAYSIZE(drdynvc->channelDef.name),
3231
0
                  DRDYNVC_SVC_CHANNEL_NAME);
3232
0
  drdynvc->state = DRDYNVC_STATE_INITIAL;
3233
0
  pEntryPointsEx = (CHANNEL_ENTRY_POINTS_FREERDP_EX*)pEntryPoints;
3234
3235
0
  if ((pEntryPointsEx->cbSize >= sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX)) &&
3236
0
      (pEntryPointsEx->MagicNumber == FREERDP_CHANNEL_MAGIC_NUMBER))
3237
0
  {
3238
0
    context = (DrdynvcClientContext*)calloc(1, sizeof(DrdynvcClientContext));
3239
3240
0
    if (!context)
3241
0
    {
3242
0
      WLog_Print(drdynvc->log, WLOG_ERROR, "calloc failed!");
3243
0
      free(drdynvc);
3244
0
      return FALSE;
3245
0
    }
3246
3247
0
    context->handle = (void*)drdynvc;
3248
0
    context->custom = nullptr;
3249
0
    drdynvc->context = context;
3250
0
    context->GetVersion = drdynvc_get_version;
3251
0
    context->GetChannelStats = drdynvc_get_channel_stats;
3252
0
    drdynvc->rdpcontext = pEntryPointsEx->context;
3253
0
    if (!freerdp_settings_get_bool(drdynvc->rdpcontext->settings,
3254
0
                                   FreeRDP_TransportDumpReplay) &&
3255
0
        !freerdp_settings_get_bool(drdynvc->rdpcontext->settings,
3256
0
                                   FreeRDP_SynchronousDynamicChannels))
3257
0
      drdynvc->async = TRUE;
3258
0
  }
3259
3260
0
  drdynvc->log = WLog_Get(TAG);
3261
0
  WLog_Print(drdynvc->log, WLOG_DEBUG, "VirtualChannelEntryEx");
3262
0
  CopyMemory(&(drdynvc->channelEntryPoints), pEntryPoints,
3263
0
             sizeof(CHANNEL_ENTRY_POINTS_FREERDP_EX));
3264
0
  drdynvc->InitHandle = pInitHandle;
3265
3266
0
  WINPR_ASSERT(drdynvc->channelEntryPoints.pVirtualChannelInitEx);
3267
0
  rc = drdynvc->channelEntryPoints.pVirtualChannelInitEx(
3268
0
      drdynvc, context, pInitHandle, &drdynvc->channelDef, 1, VIRTUAL_CHANNEL_VERSION_WIN2000,
3269
0
      drdynvc_virtual_channel_init_event_ex);
3270
3271
0
  if (CHANNEL_RC_OK != rc)
3272
0
  {
3273
0
    WLog_Print(drdynvc->log, WLOG_ERROR, "pVirtualChannelInit failed with %s [%08" PRIX32 "]",
3274
0
               WTSErrorToString(rc), rc);
3275
0
    free(drdynvc->context);
3276
0
    free(drdynvc);
3277
0
    return FALSE;
3278
0
  }
3279
3280
0
  drdynvc->channelEntryPoints.pInterface = context;
3281
0
  return TRUE;
3282
0
}