Coverage Report

Created: 2026-05-14 06:28

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wireshark/epan/dissectors/packet-rdp_drdynvc.c
Line
Count
Source
1
/* packet-rdp_drdynvc.c
2
 * Routines for Dynamic Virtual channel RDP packet dissection
3
 * Copyright 2021, David Fort
4
 *
5
 * Wireshark - Network traffic analyzer
6
 * By Gerald Combs <gerald@wireshark.org>
7
 * Copyright 1998 Gerald Combs
8
 *
9
 * SPDX-License-Identifier: GPL-2.0-or-later
10
 */
11
12
#include "config.h"
13
14
#include <epan/packet.h>
15
#include <epan/prefs.h>
16
#include <epan/proto_data.h>
17
#include <epan/conversation.h>
18
#include <epan/crc32-tvb.h>
19
#include <epan/tvbuff_rdp.h>
20
#include "packet-rdp.h"
21
#include "packet-rdpudp.h"
22
23
void proto_register_rdp_drdynvc(void);
24
void proto_reg_handoff_drdynvc(void);
25
26
static int proto_rdp_drdynvc;
27
28
static int hf_rdp_drdynvc_cbId;
29
static int hf_rdp_drdynvc_sp;
30
static int hf_rdp_drdynvc_pri;
31
static int hf_rdp_drdynvc_cmd;
32
static int hf_rdp_drdynvc_capa_version;
33
static int hf_rdp_drdynvc_capa_prio0;
34
static int hf_rdp_drdynvc_capa_prio1;
35
static int hf_rdp_drdynvc_capa_prio2;
36
static int hf_rdp_drdynvc_capa_prio3;
37
static int hf_rdp_drdynvc_channelId;
38
static int hf_rdp_drdynvc_pad;
39
static int hf_rdp_drdynvc_channelName;
40
static int hf_rdp_drdynvc_creationStatus;
41
static int hf_rdp_drdynvc_createresp_channelname;
42
static int hf_rdp_drdynvc_length;
43
static int hf_rdp_drdynvc_softsync_req_length;
44
static int hf_rdp_drdynvc_softsync_req_flags;
45
static int hf_rdp_drdynvc_softsync_req_ntunnels;
46
static int hf_rdp_drdynvc_softsync_req_channel_tunnelType;
47
static int hf_rdp_drdynvc_softsync_req_channel_ndvc;
48
static int hf_rdp_drdynvc_softsync_req_channel_dvcid;
49
static int hf_rdp_drdynvc_softsync_resp_ntunnels;
50
static int hf_rdp_drdynvc_softsync_resp_tunnel;
51
static int hf_rdp_drdynvc_data;
52
static int hf_rdp_drdynvc_data_progress;
53
static int hf_rdp_drdynvc_createreq_frameid;
54
static int hf_rdp_drdynvc_createresp_frameid;
55
56
57
static int ett_rdp_drdynvc;
58
static int ett_rdp_drdynvc_softsync_channels;
59
static int ett_rdp_drdynvc_softsync_channel;
60
static int ett_rdp_drdynvc_softsync_dvc;
61
62
dissector_handle_t egfx_handle;
63
dissector_handle_t rail_handle;
64
dissector_handle_t cliprdr_handle;
65
dissector_handle_t rdpdr_handle;
66
dissector_handle_t snd_handle;
67
dissector_handle_t ear_handle;
68
dissector_handle_t ecam_handle;
69
70
enum {
71
  DRDYNVC_CREATE_REQUEST_PDU = 0x01,
72
  DRDYNVC_DATA_FIRST_PDU = 0x02,
73
  DRDYNVC_DATA_PDU = 0x03,
74
  DRDYNVC_CLOSE_REQUEST_PDU = 0x04,
75
  DRDYNVC_CAPABILITY_REQUEST_PDU = 0x05,
76
  DRDYNVC_DATA_FIRST_COMPRESSED_PDU = 0x06,
77
  DRDYNVC_DATA_COMPRESSED_PDU = 0x07,
78
  DRDYNVC_SOFT_SYNC_REQUEST_PDU = 0x08,
79
  DRDYNVC_SOFT_SYNC_RESPONSE_PDU = 0x09
80
};
81
82
typedef enum {
83
  DRDYNVC_CHANNEL_UNKNOWN,
84
  DRDYNVC_CHANNEL_EGFX, /* MS-RDPEGX */
85
  DRDYNVC_CHANNEL_TELEMETRY, /* MS-RDPET */
86
  DRDYNVC_CHANNEL_AUDIOUT, /* MS-RDPEA */
87
  DRDYNVC_CHANNEL_AUDIN, /* MS-RDPEAI */
88
  DRDYNVC_CHANNEL_VIDEO_CTL, /*MS-RDPEVOR */
89
  DRDYNVC_CHANNEL_VIDEO_DATA, /*MS-RDPEVOR */
90
  DRDYNVC_CHANNEL_CAM,  /* MS-RDPECAM */
91
  DRDYNVC_CHANNEL_DISPLAY, /* MS-RDPEDISP */
92
  DRDYNVC_CHANNEL_GEOMETRY,/* MS-RDPEGT */
93
  DRDYNVC_CHANNEL_MULTITOUCH, /* MS-RDPEI */
94
  DRDYNVC_CHANNEL_AUTH_REDIR, /* MS-RDPEAR */
95
96
  DRDYNVC_CHANNEL_RAIL, /* MS-RDPERP */
97
  DRDYNVC_CHANNEL_CLIPRDR, /* MS-RDPECLIP */
98
  DRDYNVC_CHANNEL_DR, /* MS-RDPDR */
99
} drdynvc_known_channel_t;
100
101
enum {
102
  DRDYNVC_CHANNEL_PDUS_KEY = 1,
103
};
104
105
106
typedef struct {
107
  bool reassembled;
108
  bool decodePayload;
109
  uint32_t progressStart;
110
  uint32_t progressEnd;
111
  uint32_t packetLen;
112
  uint32_t startReassemblyFrame;
113
  uint32_t endReassemblyFrame;
114
  tvbuff_t* tvb;
115
} drdynvc_pdu_info_t;
116
117
typedef struct {
118
  wmem_tree_t *pdus;
119
} drdynvc_pinfo_t;
120
121
/** @brief context for tracking a list of packet chunks */
122
typedef struct {
123
  wmem_array_t *currentPacket;
124
  uint32_t packetLen;
125
  uint32_t pendingLen;
126
  uint32_t startFrame;
127
  uint32_t endReassemblyFrame;
128
  wmem_array_t *chunks;
129
} drdynvc_pending_packet_t;
130
131
/** @brief context associated with a dynamic channel */
132
typedef struct {
133
  drdynvc_known_channel_t type;
134
  char *name;
135
  uint32_t channelId;
136
  uint32_t createFrameId;
137
  uint32_t createConfirmFrameId;
138
139
  drdynvc_pending_packet_t pending_cs;
140
  drdynvc_pending_packet_t pending_sc;
141
  zgfx_context_t *zgfx_cs;
142
  zgfx_context_t *zgfx_sc;
143
} drdynvc_channel_def_t;
144
145
typedef struct _drdynvc_conv_info_t {
146
  wmem_multimap_t *channels;
147
} drdynvc_conv_info_t;
148
149
150
typedef struct {
151
  const char *name;
152
  const char *shortName;
153
  drdynvc_known_channel_t type;
154
} drdynvc_know_channel_def;
155
156
static drdynvc_know_channel_def knownChannels[] = {
157
  {"AUDIO_INPUT", "audin",            DRDYNVC_CHANNEL_AUDIN},
158
  {"AUDIO_PLAYBACK_DVC", "audiout",       DRDYNVC_CHANNEL_AUDIOUT},
159
  {"AUDIO_PLAYBACK_LOSSY_DVC", "audiout lossy", DRDYNVC_CHANNEL_AUDIOUT},
160
  {"RDCamera_Device_Enumerator", "cam",     DRDYNVC_CHANNEL_CAM},
161
  {"Microsoft::Windows::RDS::Video::Control::v08.01", "videoctl", DRDYNVC_CHANNEL_VIDEO_CTL},
162
  {"Microsoft::Windows::RDS::Video::Data::v08.01", "videodata", DRDYNVC_CHANNEL_VIDEO_DATA},
163
  {"Microsoft::Windows::RDS::AuthRedirection", "authredir", DRDYNVC_CHANNEL_AUTH_REDIR},
164
  {"Microsoft::Windows::RDS::Telemetry", "telemetry", DRDYNVC_CHANNEL_TELEMETRY},
165
  {"Microsoft::Windows::RDS::Graphics", "egfx", DRDYNVC_CHANNEL_EGFX},
166
  {"Microsoft::Windows::RDS::DisplayControl", "display", DRDYNVC_CHANNEL_DISPLAY},
167
  {"Microsoft::Windows::RDS::Geometry::v08.01", "geometry", DRDYNVC_CHANNEL_GEOMETRY},
168
  {"Microsoft::Windows::RDS::Input", "input", DRDYNVC_CHANNEL_MULTITOUCH},
169
  {"Microsoft::Windows::RDS::RAIL", "rail", DRDYNVC_CHANNEL_RAIL},
170
171
  /* static channels that can be reopened on the dynamic channel */
172
  {"rail", "rail", DRDYNVC_CHANNEL_RAIL},
173
  {"cliprdr", "cliprdr", DRDYNVC_CHANNEL_CLIPRDR},
174
  {"rdpdr", "rdpdr", DRDYNVC_CHANNEL_DR},
175
};
176
177
static const value_string drdynvc_tunneltype_vals[] = {
178
  {   0x1,  "reliable" },
179
  {   0x3,  "lossy" },
180
  {   0x0, NULL},
181
};
182
183
static const value_string rdp_drdynvc_cbId_vals[] = {
184
  {   0x0, "1 byte" },
185
  {   0x1, "2 bytes" },
186
  {   0x2, "4 bytes" },
187
  {   0x0, NULL},
188
};
189
190
static const value_string rdp_drdynvc_prio_vals[] = {
191
  {   0x0, "PriorityCharge0" },
192
  {   0x1, "PriorityCharge1" },
193
  {   0x2, "PriorityCharge2" },
194
  {   0x3, "PriorityCharge3" },
195
  {   0x0, NULL},
196
};
197
198
static const value_string rdp_drdynvc_cmd_vals[] = {
199
  {   DRDYNVC_CREATE_REQUEST_PDU,   "Create PDU" },
200
  {   DRDYNVC_DATA_FIRST_PDU,     "Data first PDU" },
201
  {   DRDYNVC_DATA_PDU,       "Data PDU" },
202
  {   DRDYNVC_CLOSE_REQUEST_PDU,  "Close PDU" },
203
  {   DRDYNVC_CAPABILITY_REQUEST_PDU, "Capabilities PDU" },
204
  {   DRDYNVC_DATA_FIRST_COMPRESSED_PDU, "Data first compressed PDU" },
205
  {   DRDYNVC_DATA_COMPRESSED_PDU,  "Data compressed PDU" },
206
  {   DRDYNVC_SOFT_SYNC_REQUEST_PDU,"Soft-Sync request PDU" },
207
  {   DRDYNVC_SOFT_SYNC_RESPONSE_PDU,"Soft-Sync response PDU" },
208
  {   0x0, NULL},
209
};
210
211
0
static unsigned channel_hashFunc(const void *key) {
212
0
  uint32_t *intPtr = (uint32_t *)key;
213
214
0
  return *intPtr;
215
0
}
216
217
0
static gboolean channel_equalFunc(const void *a, const void *b) {
218
0
  uint32_t *aPtr = (uint32_t *)a;
219
0
  uint32_t *bPtr = (uint32_t *)b;
220
221
0
  return (*aPtr == *bPtr);
222
0
}
223
224
225
static void
226
drdynvc_pending_packet_init(drdynvc_pending_packet_t *pending, uint32_t startFrame)
227
0
{
228
0
  pending->packetLen = 0;
229
0
  pending->pendingLen = 0;
230
0
  pending->startFrame = startFrame;
231
0
  pending->endReassemblyFrame = 0;
232
0
  pending->currentPacket = NULL;
233
0
  pending->chunks = NULL;
234
0
}
235
236
static drdynvc_known_channel_t
237
drdynvc_find_channel_type(const char *name)
238
0
{
239
0
  unsigned i;
240
241
0
  for (i = 0; i < array_length(knownChannels); i++)
242
0
  {
243
0
    if (strcmp(knownChannels[i].name, name) == 0)
244
0
      return knownChannels[i].type;
245
0
  }
246
247
  // TODO: replace with proper registration of announced ecam sub channels
248
0
  if (strstr(name, "RDCamera_Device_") == name)
249
0
    return DRDYNVC_CHANNEL_CAM;
250
251
0
  return DRDYNVC_CHANNEL_UNKNOWN;
252
0
}
253
254
static drdynvc_conv_info_t *
255
drdynvc_get_conversation_data(packet_info *pinfo)
256
0
{
257
0
  conversation_t *conversation = rdp_find_main_conversation(pinfo);
258
0
  if (!conversation)
259
0
    return NULL;
260
261
0
  drdynvc_conv_info_t *info = (drdynvc_conv_info_t *)conversation_get_proto_data(conversation, proto_rdp_drdynvc);
262
0
  if (info == NULL) {
263
0
    info = wmem_new0(wmem_file_scope(), drdynvc_conv_info_t);
264
0
    info->channels = wmem_multimap_new(wmem_file_scope(), channel_hashFunc, channel_equalFunc);
265
0
    conversation_add_proto_data(conversation, proto_rdp_drdynvc, info);
266
0
  }
267
268
0
  return info;
269
0
}
270
271
272
static int
273
dissect_rdp_vlength(tvbuff_t *tvb, int hf_index, int offset, uint8_t vlen, proto_tree *tree, uint32_t *ret)
274
0
{
275
0
  int len;
276
0
  uint32_t value = 0;
277
278
0
  switch (vlen) {
279
0
  case 0:
280
0
    value = tvb_get_uint8(tvb, offset);
281
0
    len = 1;
282
0
    break;
283
0
  case 1:
284
0
    value = tvb_get_uint16(tvb, offset, ENC_LITTLE_ENDIAN);
285
0
    len = 2;
286
0
    break;
287
0
  case 2:
288
0
    value = tvb_get_uint32(tvb, offset, ENC_LITTLE_ENDIAN);
289
0
    len = 4;
290
0
    break;
291
0
  default:
292
0
    if (ret)
293
0
      *ret = 0;
294
0
    return 0;
295
0
  }
296
297
0
  proto_tree_add_uint(tree, hf_index, tvb, offset, len, value);
298
0
  if (ret)
299
0
    *ret = value;
300
0
  return len;
301
0
}
302
303
static const char *
304
0
find_channel_name_by_id(packet_info *pinfo, drdynvc_conv_info_t *dyninfo, uint32_t dvcId) {
305
0
  drdynvc_channel_def_t *dynChannel = wmem_multimap_lookup32_le(dyninfo->channels, &dvcId, pinfo->num);
306
0
  if (dynChannel)
307
0
    return dynChannel->name;
308
309
0
  return NULL;
310
0
}
311
312
static drdynvc_pinfo_t *getDrDynPacketInfo(packet_info *pinfo)
313
0
{
314
0
  drdynvc_pinfo_t *ret = p_get_proto_data(wmem_file_scope(), pinfo, proto_rdp_drdynvc, DRDYNVC_CHANNEL_PDUS_KEY);
315
0
  if (ret)
316
0
    return ret;
317
318
0
  ret = wmem_alloc(wmem_file_scope(), sizeof(*ret));
319
0
  ret->pdus = wmem_tree_new(wmem_file_scope());
320
321
0
  p_set_proto_data(wmem_file_scope(), pinfo, proto_rdp_drdynvc, DRDYNVC_CHANNEL_PDUS_KEY, ret);
322
0
  return ret;
323
0
}
324
325
static int
326
dissect_rdp_drdynvc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void *data _U_)
327
0
{
328
0
  proto_item *item;
329
0
  proto_tree *tree;
330
0
  int offset = 0;
331
0
  uint8_t cbIdSpCmd, cmdId;
332
0
  uint8_t cbId, Len;
333
0
  bool haveChannelId, havePri, haveLen;
334
0
  bool isServerTarget = rdp_isServerAddressTarget(pinfo);
335
0
  uint32_t channelId = 0;
336
0
  uint32_t fullPduLen = 0;
337
0
  drdynvc_conv_info_t *info;
338
0
  drdynvc_channel_def_t *channel = NULL;
339
340
0
  col_set_str(pinfo->cinfo, COL_PROTOCOL, "DRDYNVC");
341
0
  col_clear(pinfo->cinfo, COL_INFO);
342
343
0
  parent_tree = proto_tree_get_root(parent_tree);
344
0
  item = proto_tree_add_item(parent_tree, proto_rdp_drdynvc, tvb, 0, -1, ENC_NA);
345
0
  tree = proto_item_add_subtree(item, ett_rdp_drdynvc);
346
347
0
  cbIdSpCmd = tvb_get_uint8(tvb, offset);
348
0
  cmdId = (cbIdSpCmd >> 4) & 0xf;
349
0
  cbId = (cbIdSpCmd & 0x3);
350
351
0
  haveChannelId = true;
352
0
  havePri = false;
353
0
  haveLen = false;
354
0
  switch (cmdId) {
355
0
    case DRDYNVC_CREATE_REQUEST_PDU:
356
0
      havePri = true;
357
0
      break;
358
0
    case DRDYNVC_DATA_FIRST_PDU:
359
0
      haveLen = true;
360
0
      break;
361
0
    case DRDYNVC_DATA_FIRST_COMPRESSED_PDU:
362
0
      haveLen = true;
363
0
      break;
364
0
    case DRDYNVC_CAPABILITY_REQUEST_PDU:
365
0
    case DRDYNVC_SOFT_SYNC_REQUEST_PDU:
366
0
    case DRDYNVC_SOFT_SYNC_RESPONSE_PDU:
367
0
      haveChannelId = false;
368
0
      break;
369
0
    default:
370
0
      break;
371
0
  }
372
373
0
  proto_tree_add_item(tree, hf_rdp_drdynvc_cbId, tvb, offset, 1, ENC_NA);
374
0
  if (havePri)
375
0
            proto_tree_add_item(tree, hf_rdp_drdynvc_pri, tvb, offset, 1, ENC_NA);
376
0
  else
377
0
            proto_tree_add_item(tree, hf_rdp_drdynvc_sp, tvb, offset, 1, ENC_NA);
378
0
  proto_tree_add_item(tree, hf_rdp_drdynvc_cmd, tvb, offset, 1, ENC_NA);
379
380
0
  offset++;
381
382
0
  info = drdynvc_get_conversation_data(pinfo);
383
0
  if (!info)
384
0
    return offset;
385
386
0
  if (haveChannelId) {
387
0
            offset += dissect_rdp_vlength(tvb, hf_rdp_drdynvc_channelId, offset, cbId, tree, &channelId);
388
389
0
            channel = wmem_multimap_lookup32_le(info->channels, &channelId, pinfo->num);
390
#if 0
391
            if (channel)
392
                    printf("%d: channels=%p haveChannelId and channel (0x%x) %s\n", pinfo->num, info->channels, channelId, channel->name);
393
            else
394
                    printf("%d: channels=%p haveChannelId and no channel for 0x%x\n", pinfo->num, info->channels, channelId);
395
#endif
396
0
  }
397
398
0
  if (haveLen) {
399
0
            Len = (cbIdSpCmd >> 2) & 0x3;
400
0
            offset += dissect_rdp_vlength(tvb, hf_rdp_drdynvc_length, offset, Len, tree, &fullPduLen);
401
0
  }
402
403
0
  switch (cmdId) {
404
0
    case DRDYNVC_CREATE_REQUEST_PDU:
405
0
      if (!isServerTarget) {
406
0
        unsigned nameLen = tvb_strsize(tvb, offset);
407
408
0
        char *channelName = NULL;
409
0
        proto_tree_add_item_ret_display_string(tree, hf_rdp_drdynvc_channelName, tvb, offset, -1, ENC_ASCII, pinfo->pool, &channelName);
410
411
0
        col_append_sep_fstr(pinfo->cinfo, COL_INFO, ",", "CreateChannel Request(%s)", channelName);
412
413
0
        if (!PINFO_FD_VISITED(pinfo)) {
414
0
          channel = wmem_alloc(wmem_file_scope(), sizeof(*channel));
415
0
          channel->channelId = channelId;
416
0
          channel->name = (char*)tvb_get_string_enc(wmem_file_scope(), tvb, offset, nameLen, ENC_ASCII);
417
0
          channel->type = drdynvc_find_channel_type(channel->name);
418
0
          channel->createFrameId = pinfo->num;
419
0
          channel->createConfirmFrameId = 0;
420
0
          drdynvc_pending_packet_init(&channel->pending_cs, pinfo->num);
421
0
          drdynvc_pending_packet_init(&channel->pending_sc, pinfo->num);
422
0
          channel->zgfx_cs = zgfx_context_new(wmem_file_scope());
423
0
          channel->zgfx_sc = zgfx_context_new(wmem_file_scope());
424
425
0
          wmem_multimap_insert32(info->channels, &channel->channelId, pinfo->num, channel);
426
#if 0
427
          printf("%d: adding new channel %s 0x%x, channels=%p\n", pinfo->num, channel->name, channelId, info->channels);
428
#endif
429
0
        }
430
431
0
        if (channel->createConfirmFrameId) {
432
0
          proto_item_set_generated(
433
0
              proto_tree_add_uint(tree, hf_rdp_drdynvc_createresp_frameid, tvb, 0, 0, channel->createConfirmFrameId)
434
0
          );
435
0
        }
436
437
0
      } else {
438
0
        char *channelName = "unknown";
439
0
        proto_tree_add_item(tree, hf_rdp_drdynvc_creationStatus, tvb, offset, 4, ENC_LITTLE_ENDIAN);
440
441
0
        if (channel) {
442
0
          proto_item_set_generated(
443
0
            proto_tree_add_string_format_value(tree, hf_rdp_drdynvc_createresp_channelname, tvb, offset, 0, NULL, "%s", channel->name)
444
0
          );
445
446
0
          if (!PINFO_FD_VISITED(pinfo)) {
447
0
            if (!channel->createConfirmFrameId)
448
0
              channel->createConfirmFrameId = pinfo->num;
449
0
          }
450
451
0
          if (channel->createFrameId) {
452
0
            proto_item_set_generated(
453
0
                proto_tree_add_uint(tree, hf_rdp_drdynvc_createreq_frameid, tvb, 0, 0, channel->createFrameId)
454
0
            );
455
0
          }
456
0
          channelName = channel->name;
457
0
        }
458
0
        col_append_sep_fstr(pinfo->cinfo, COL_INFO, ",", "CreateChannel Response(%s)", channelName);
459
460
0
      }
461
0
      break;
462
0
    case DRDYNVC_CAPABILITY_REQUEST_PDU: {
463
      /* Pad */
464
0
      proto_tree_add_item(tree, hf_rdp_drdynvc_pad, tvb, offset, 1, ENC_NA);
465
0
      offset++;
466
467
0
      uint32_t version;
468
0
      proto_tree_add_item_ret_uint(tree, hf_rdp_drdynvc_capa_version, tvb, offset, 2, ENC_LITTLE_ENDIAN, &version);
469
0
      offset += 2;
470
471
0
      if (!isServerTarget) {
472
0
        col_append_sep_str(pinfo->cinfo, COL_INFO, ",", "Capabilities request");
473
474
0
        if (version > 1) {
475
0
          proto_tree_add_item(tree, hf_rdp_drdynvc_capa_prio0, tvb, offset, 2, ENC_LITTLE_ENDIAN);
476
0
          offset += 2;
477
0
          proto_tree_add_item(tree, hf_rdp_drdynvc_capa_prio1, tvb, offset, 2, ENC_LITTLE_ENDIAN);
478
0
          offset += 2;
479
0
          proto_tree_add_item(tree, hf_rdp_drdynvc_capa_prio2, tvb, offset, 2, ENC_LITTLE_ENDIAN);
480
0
          offset += 2;
481
0
          proto_tree_add_item(tree, hf_rdp_drdynvc_capa_prio3, tvb, offset, 2, ENC_LITTLE_ENDIAN);
482
0
          offset += 2;
483
0
        }
484
0
      } else {
485
0
        col_append_sep_str(pinfo->cinfo, COL_INFO, ",", "Capabilities response");
486
0
      }
487
0
      break;
488
0
    }
489
0
    case DRDYNVC_DATA_FIRST_PDU:
490
0
    case DRDYNVC_DATA_FIRST_COMPRESSED_PDU: {
491
0
      col_append_sep_str(pinfo->cinfo, COL_INFO, ",", (cmdId == DRDYNVC_DATA_FIRST_PDU) ? "Data first" : "Data compressed first");
492
493
0
      if (channel) {
494
0
        drdynvc_pdu_info_t *pduInfo = NULL;
495
0
        drdynvc_pending_packet_t *pendingPacket = isServerTarget ? &channel->pending_cs : &channel->pending_sc;
496
0
        int payloadLen = tvb_reported_length_remaining(tvb, offset);
497
0
        bool isSinglePacket = (fullPduLen == (uint32_t)payloadLen);
498
0
        drdynvc_pinfo_t *drdynvcPinfo = getDrDynPacketInfo(pinfo);
499
0
        uint32_t key = crc32_ccitt_tvb_offset(tvb, offset, payloadLen);
500
501
0
        proto_item_set_generated(
502
0
          proto_tree_add_string_format_value(tree, hf_rdp_drdynvc_createresp_channelname, tvb, offset, 0, NULL, "%s", channel->name)
503
0
        );
504
505
0
        proto_item_set_generated(
506
0
          proto_tree_add_string_format_value(tree, hf_rdp_drdynvc_data_progress, tvb, offset, 0, NULL, "0-%d/%d", payloadLen, fullPduLen)
507
0
        );
508
509
0
        if (!PINFO_FD_VISITED(pinfo)) {
510
0
          tvbuff_t *input = tvb;
511
0
          int offset2 = offset;
512
513
0
          if (cmdId == DRDYNVC_DATA_FIRST_COMPRESSED_PDU) {
514
0
            zgfx_context_t *compressor = isServerTarget ? channel->zgfx_cs : channel->zgfx_sc;
515
0
            input = rdp8_decompress(compressor, wmem_file_scope(), tvb, offset);
516
0
            offset2 = 0;
517
0
            add_new_data_source(pinfo, input, "decompressed dynvc");
518
0
          }
519
520
0
          if (!isSinglePacket) {
521
0
            if (pendingPacket->chunks)
522
0
              wmem_destroy_array(pendingPacket->chunks);
523
0
            pendingPacket->chunks = wmem_array_new(wmem_file_scope(), sizeof(drdynvc_pdu_info_t*));
524
525
0
            pduInfo = wmem_alloc(wmem_file_scope(), sizeof(*pduInfo));
526
0
            pduInfo->reassembled = true;
527
0
            pduInfo->startReassemblyFrame = pinfo->num;
528
0
            pduInfo->progressStart = 0;
529
0
            pduInfo->progressEnd = fullPduLen;
530
0
            pduInfo->tvb = (cmdId == DRDYNVC_DATA_FIRST_COMPRESSED_PDU) ? input : NULL;
531
532
0
            wmem_tree_insert32(drdynvcPinfo->pdus, key, pduInfo);
533
0
            wmem_array_append(pendingPacket->chunks, &pduInfo, 1);
534
535
0
            pendingPacket->packetLen = fullPduLen;
536
0
            pendingPacket->pendingLen = fullPduLen - payloadLen;
537
0
            pendingPacket->startFrame = pinfo->num;
538
0
            pendingPacket->currentPacket = wmem_array_sized_new(wmem_file_scope(), 1, fullPduLen);
539
0
            wmem_array_append(pendingPacket->currentPacket, tvb_get_ptr(input, offset2, payloadLen), payloadLen);
540
0
          } else {
541
0
            if (pendingPacket->pendingLen || pendingPacket->chunks)
542
0
              printf("(%d) looks like we have a non completed packet...\n", pinfo->num);
543
0
            if (pendingPacket->chunks)
544
0
              wmem_destroy_array(pendingPacket->chunks);
545
0
            memset(pendingPacket, 0, sizeof(*pendingPacket));
546
0
          }
547
0
        } else {
548
0
          pduInfo = (drdynvc_pdu_info_t*)wmem_tree_lookup32(drdynvcPinfo->pdus, key);
549
0
        }
550
551
0
        if (isSinglePacket) {
552
0
          switch (channel->type) {
553
0
          case DRDYNVC_CHANNEL_EGFX:
554
0
            call_dissector(egfx_handle, tvb_new_subset_remaining(tvb, offset), pinfo, tree);
555
0
            break;
556
0
          case DRDYNVC_CHANNEL_RAIL:
557
0
            call_dissector(rail_handle, tvb_new_subset_remaining(tvb, offset), pinfo, tree);
558
0
            break;
559
0
          case DRDYNVC_CHANNEL_CLIPRDR:
560
0
            call_dissector(cliprdr_handle, tvb_new_subset_remaining(tvb, offset), pinfo, tree);
561
0
            break;
562
0
          case DRDYNVC_CHANNEL_AUDIOUT:
563
0
            call_dissector(snd_handle, tvb_new_subset_remaining(tvb, offset), pinfo, tree);
564
0
            break;
565
0
          case DRDYNVC_CHANNEL_AUTH_REDIR:
566
0
            call_dissector(ear_handle, tvb_new_subset_remaining(tvb, offset), pinfo, tree);
567
0
            break;
568
0
          case DRDYNVC_CHANNEL_CAM:
569
0
            call_dissector(ecam_handle, tvb_new_subset_remaining(tvb, offset), pinfo, tree);
570
0
            break;
571
0
          case DRDYNVC_CHANNEL_DR:
572
0
            call_dissector(rdpdr_handle, tvb_new_subset_remaining(tvb, offset), pinfo, tree);
573
0
            break;
574
0
          default:
575
0
            proto_tree_add_item(tree, hf_rdp_drdynvc_data, tvb, offset, -1, ENC_NA);
576
0
            break;
577
0
          }
578
579
0
          offset += payloadLen;
580
0
          return offset;
581
0
        }
582
583
0
      }
584
585
0
      proto_tree_add_item(tree, hf_rdp_drdynvc_data, tvb, offset, -1, ENC_NA);
586
0
      break;
587
0
    }
588
0
    case DRDYNVC_DATA_PDU:
589
0
    case DRDYNVC_DATA_COMPRESSED_PDU: {
590
0
      col_append_sep_str(pinfo->cinfo, COL_INFO, ",", (cmdId == DRDYNVC_DATA_PDU) ? "Data" : "Data compressed");
591
592
0
      if (channel) {
593
0
        tvbuff_t *targetTvb = NULL;
594
595
0
        proto_item_set_generated(
596
0
          proto_tree_add_string_format_value(tree, hf_rdp_drdynvc_createresp_channelname, tvb, offset, 0, NULL, "%s", channel->name)
597
0
        );
598
599
0
        drdynvc_pinfo_t *drdynvcPinfo = getDrDynPacketInfo(pinfo);
600
0
        drdynvc_pdu_info_t *pduInfo = NULL;
601
0
        int payloadLen = tvb_reported_length_remaining(tvb, offset);
602
0
        uint32_t key = crc32_ccitt_tvb_offset(tvb, offset, payloadLen);
603
604
0
        if (!PINFO_FD_VISITED(pinfo)) {
605
0
          drdynvc_pending_packet_t *pendingPacket = isServerTarget ? &channel->pending_cs : &channel->pending_sc;
606
607
0
          tvbuff_t *input = tvb;
608
0
          int offset2 = offset;
609
610
0
          if (cmdId == DRDYNVC_DATA_COMPRESSED_PDU) {
611
0
            zgfx_context_t *compressor = isServerTarget ? channel->zgfx_cs : channel->zgfx_sc;
612
0
            input = rdp8_decompress(compressor, wmem_file_scope(), tvb, offset);
613
0
            offset2 = 0;
614
0
            add_new_data_source(pinfo, input, "decompressed dynvc");
615
0
          }
616
617
0
          pduInfo = wmem_alloc(wmem_file_scope(), sizeof(*pduInfo));
618
0
          wmem_tree_insert32(drdynvcPinfo->pdus, key, pduInfo);
619
620
0
          if (pendingPacket->pendingLen) {
621
            /* we have a fragmented packet in progress */
622
0
            if ((uint32_t)payloadLen > pendingPacket->pendingLen) {
623
              // TODO: error
624
0
              printf("num=%d error payload too big\n", pinfo->num);
625
0
              return offset;
626
0
            }
627
628
0
            pduInfo->reassembled = true;
629
0
            pduInfo->decodePayload = false;
630
0
            pduInfo->progressStart = pendingPacket->packetLen - pendingPacket->pendingLen;
631
0
            pduInfo->progressEnd = pduInfo->progressStart + payloadLen;
632
0
            pduInfo->packetLen = pendingPacket->packetLen;
633
634
0
            wmem_array_append(pendingPacket->chunks, &pduInfo, 1);
635
636
0
            pendingPacket->pendingLen -= payloadLen;
637
0
            wmem_array_append(pendingPacket->currentPacket, tvb_get_ptr(input, offset2, payloadLen), payloadLen);
638
639
0
            if (!pendingPacket->pendingLen) {
640
              /* last packet of the reassembly */
641
0
              int reassembled_len = wmem_array_get_count(pendingPacket->currentPacket);
642
0
              pduInfo->tvb = tvb_new_real_data(wmem_array_get_raw(pendingPacket->currentPacket), reassembled_len, reassembled_len);
643
0
              pduInfo->decodePayload = true;
644
0
              pendingPacket->currentPacket = NULL;
645
646
0
              for (unsigned i = 0; i < wmem_array_get_count(pendingPacket->chunks); i++) {
647
0
                drdynvc_pdu_info_t *chunk = *(drdynvc_pdu_info_t **)wmem_array_index(pendingPacket->chunks, i);
648
0
                chunk->endReassemblyFrame = pinfo->num;
649
0
              }
650
0
              wmem_destroy_array(pendingPacket->chunks);
651
0
              pendingPacket->chunks = NULL;
652
0
            }
653
0
          } else {
654
            /* single data packet */
655
0
            pduInfo->reassembled = false;
656
0
            pduInfo->decodePayload = true;
657
0
            pduInfo->progressStart = 0;
658
0
            pduInfo->progressEnd = payloadLen;
659
0
            pduInfo->packetLen = payloadLen;
660
0
            pduInfo->tvb = (input == tvb) ? NULL : input;
661
0
            pduInfo->startReassemblyFrame = pduInfo->endReassemblyFrame = pinfo->num;
662
0
          }
663
0
        } else {
664
0
          pduInfo = (drdynvc_pdu_info_t*)wmem_tree_lookup32(drdynvcPinfo->pdus, key);
665
0
        }
666
667
0
        if (pduInfo) {
668
0
          proto_item_set_generated(
669
0
            proto_tree_add_string_format_value(tree, hf_rdp_drdynvc_data_progress, tvb, offset, 0, NULL, "%d-%d/%d",
670
0
              pduInfo->progressStart, pduInfo->progressEnd, pduInfo->packetLen)
671
0
          );
672
673
0
          if (pduInfo->tvb) {
674
0
            targetTvb = pduInfo->tvb;
675
0
            add_new_data_source(pinfo, targetTvb, "Reassembled/decompressed DRDYNVC");
676
0
          } else {
677
0
            targetTvb = tvb_new_subset_remaining(tvb, offset);
678
0
          }
679
680
0
          if (pduInfo->endReassemblyFrame && (pduInfo->endReassemblyFrame != pinfo->num)) {
681
            // TODO: show a link to the end frame ?
682
0
          }
683
0
        }
684
685
0
        if (pduInfo && pduInfo->decodePayload) {
686
0
          switch (channel->type) {
687
0
          case DRDYNVC_CHANNEL_EGFX:
688
0
            call_dissector(egfx_handle, targetTvb, pinfo, tree);
689
0
            break;
690
0
          case DRDYNVC_CHANNEL_RAIL:
691
0
            call_dissector(rail_handle, targetTvb, pinfo, tree);
692
0
            break;
693
0
          case DRDYNVC_CHANNEL_CLIPRDR:
694
0
            call_dissector(cliprdr_handle, targetTvb, pinfo, tree);
695
0
            break;
696
0
          case DRDYNVC_CHANNEL_AUDIOUT:
697
0
            call_dissector(snd_handle, targetTvb, pinfo, tree);
698
0
            break;
699
0
          case DRDYNVC_CHANNEL_AUTH_REDIR:
700
0
            call_dissector(ear_handle, targetTvb, pinfo, tree);
701
0
            break;
702
0
          case DRDYNVC_CHANNEL_CAM:
703
0
            call_dissector(ecam_handle, targetTvb, pinfo, tree);
704
0
            break;
705
0
          case DRDYNVC_CHANNEL_DR:
706
0
            call_dissector(rdpdr_handle, targetTvb, pinfo, tree);
707
0
            break;
708
0
          default:
709
0
            proto_tree_add_item(tree, hf_rdp_drdynvc_data, targetTvb, 0, -1, ENC_NA);
710
0
            break;
711
0
          }
712
0
          return tvb_reported_length(tvb);
713
0
        }
714
0
      }
715
716
0
      proto_tree_add_item(tree, hf_rdp_drdynvc_data, tvb, offset, -1, ENC_NA);
717
0
      return tvb_reported_length(tvb);
718
0
    }
719
0
    case DRDYNVC_SOFT_SYNC_REQUEST_PDU: {
720
0
      uint32_t ntunnels;
721
0
      uint32_t flags;
722
723
0
      col_set_str(pinfo->cinfo, COL_INFO, "SoftSync Request");
724
725
      /* Pad */
726
0
      proto_tree_add_item(tree, hf_rdp_drdynvc_pad, tvb, offset, 1, ENC_NA);
727
0
      offset++;
728
729
0
      proto_tree_add_item(tree, hf_rdp_drdynvc_softsync_req_length, tvb, offset, 4, ENC_LITTLE_ENDIAN);
730
0
      offset += 4;
731
732
0
      proto_tree_add_item_ret_uint(tree, hf_rdp_drdynvc_softsync_req_flags, tvb, offset, 2, ENC_LITTLE_ENDIAN, &flags);
733
0
      offset += 2;
734
      // XXX: TODO should decode flags but they are always set to SOFT_SYNC_TCP_FLUSHED|SOFT_SYNC_CHANNEL_LIST_PRESENT
735
736
0
      proto_tree_add_item_ret_uint(tree, hf_rdp_drdynvc_softsync_req_ntunnels, tvb, offset, 2, ENC_LITTLE_ENDIAN, &ntunnels);
737
0
      offset += 2;
738
739
0
      if (flags & 0x02) { /* SOFT_SYNC_CHANNEL_LIST_PRESENT */
740
0
        proto_tree *tunnels_tree = proto_tree_add_subtree(tree, tvb, offset, -1, ett_rdp_drdynvc_softsync_channels, NULL, "Channels");
741
742
0
        for (unsigned i = 0; i < ntunnels; i++) {
743
0
          uint16_t j;
744
0
          uint32_t tunnelType = tvb_get_uint32(tvb, offset, ENC_LITTLE_ENDIAN);
745
0
          uint16_t ndvcs = tvb_get_uint16(tvb, offset + 4, ENC_LITTLE_ENDIAN);
746
0
          int channelSz = 4 + 2 + (ndvcs * 4);
747
0
          proto_tree *channel_tree;
748
0
          const char *label = (tunnelType == 0x1) ? "Reliable channels" : "Lossy channels";
749
750
0
          channel_tree = proto_tree_add_subtree(tunnels_tree, tvb, offset, channelSz, ett_rdp_drdynvc_softsync_channel, NULL, label);
751
752
0
          proto_tree_add_item(channel_tree, hf_rdp_drdynvc_softsync_req_channel_tunnelType, tvb, offset, 4, ENC_LITTLE_ENDIAN);
753
0
          offset += 4;
754
755
0
          proto_tree_add_item(channel_tree, hf_rdp_drdynvc_softsync_req_channel_ndvc, tvb, offset, 2, ENC_LITTLE_ENDIAN);
756
0
          offset += 2;
757
758
0
          for (j = 0; j < ndvcs; j++, offset += 4) {
759
0
            proto_tree *dvc_tree;
760
0
            uint32_t dvcId;
761
0
            const char *showLabel;
762
763
0
            dvcId = tvb_get_uint32(tvb, offset, ENC_LITTLE_ENDIAN);
764
0
            showLabel = label = find_channel_name_by_id(pinfo, info, dvcId);
765
0
            if (!label)
766
0
              showLabel = "DVC";
767
0
            dvc_tree = proto_tree_add_subtree(channel_tree, tvb, offset, 4, ett_rdp_drdynvc_softsync_dvc, NULL, showLabel);
768
0
            proto_tree_add_item(dvc_tree, hf_rdp_drdynvc_softsync_req_channel_dvcid, tvb, offset, 4, ENC_LITTLE_ENDIAN);
769
770
0
            if (label) {
771
0
              proto_item *pi = proto_tree_add_string_format(dvc_tree, hf_rdp_drdynvc_channelName, tvb, offset, 4, label, "%s", label);
772
0
              proto_item_set_generated(pi);
773
0
            }
774
0
          }
775
0
        }
776
0
      }
777
0
      break;
778
0
    }
779
0
    case DRDYNVC_SOFT_SYNC_RESPONSE_PDU: {
780
0
      uint32_t ntunnels, i;
781
782
0
      col_set_str(pinfo->cinfo, COL_INFO, "SoftSync Response");
783
784
      /* Pad */
785
0
      proto_tree_add_item(tree, hf_rdp_drdynvc_pad, tvb, offset, 1, ENC_NA);
786
0
      offset++;
787
788
0
      proto_tree_add_item_ret_uint(tree, hf_rdp_drdynvc_softsync_resp_ntunnels, tvb, offset, 4, ENC_LITTLE_ENDIAN, &ntunnels);
789
0
      offset += 4;
790
791
0
      if (ntunnels) {
792
0
        proto_tree *tunnels_tree = proto_tree_add_subtree(tree, tvb, offset, 4, ett_rdp_drdynvc_softsync_dvc, NULL, "TunnelsToSwitch");
793
0
        for (i = 0; i < ntunnels; i++, offset += 4) {
794
0
          proto_tree_add_item(tunnels_tree, hf_rdp_drdynvc_softsync_resp_tunnel, tvb, offset, 4, ENC_LITTLE_ENDIAN);
795
0
        }
796
0
      }
797
0
      break;
798
0
    }
799
0
    case DRDYNVC_CLOSE_REQUEST_PDU: {
800
0
      col_set_str(pinfo->cinfo, COL_INFO, "Close request");
801
0
      if (channel) {
802
0
        proto_item_set_generated(
803
0
          proto_tree_add_string_format_value(tree, hf_rdp_drdynvc_channelName, tvb, offset, 0, NULL, "%s", channel->name)
804
0
        );
805
0
      }
806
0
      break;
807
0
    }
808
0
    default:
809
0
      break;
810
0
  }
811
0
  return offset;
812
0
}
813
814
15
void proto_register_rdp_drdynvc(void) {
815
816
  /* List of fields */
817
15
  static hf_register_info hf[] = {
818
15
    { &hf_rdp_drdynvc_cbId,
819
15
      { "ChannelId length", "rdp_drdynvc.cbid",
820
15
        FT_UINT8, BASE_HEX, VALS(rdp_drdynvc_cbId_vals), 0x3,
821
15
      NULL, HFILL }},
822
15
    { &hf_rdp_drdynvc_sp,
823
15
      { "Sp", "rdp_drdynvc.sp",
824
15
      FT_UINT8, BASE_HEX, NULL, 0xc,
825
15
      NULL, HFILL }},
826
15
    { &hf_rdp_drdynvc_pri,
827
15
      { "Pri", "rdp_drdynvc.pri",
828
15
      FT_UINT8, BASE_HEX, VALS(rdp_drdynvc_prio_vals), 0xc,
829
15
      NULL, HFILL }},
830
15
    { &hf_rdp_drdynvc_cmd,
831
15
      { "PDU type", "rdp_drdynvc.cmd",
832
15
      FT_UINT8, BASE_HEX, VALS(rdp_drdynvc_cmd_vals), 0xf0,
833
15
      NULL, HFILL }},
834
15
    { &hf_rdp_drdynvc_capa_version,
835
15
      { "Version", "rdp_drdynvc.capabilities.version",
836
15
      FT_UINT16, BASE_DEC, NULL, 0,
837
15
      NULL, HFILL }},
838
15
    { &hf_rdp_drdynvc_capa_prio0,
839
15
      { "Priority charge 0", "rdp_drdynvc.capabilities.prioritycharge0",
840
15
      FT_UINT16, BASE_DEC, NULL, 0,
841
15
      NULL, HFILL }},
842
15
    { &hf_rdp_drdynvc_capa_prio1,
843
15
      { "Priority charge 1", "rdp_drdynvc.capabilities.prioritycharge1",
844
15
      FT_UINT16, BASE_DEC, NULL, 0,
845
15
      NULL, HFILL }},
846
15
    { &hf_rdp_drdynvc_capa_prio2,
847
15
      { "Priority charge 2", "rdp_drdynvc.capabilities.prioritycharge2",
848
15
      FT_UINT16, BASE_DEC, NULL, 0,
849
15
      NULL, HFILL }},
850
15
    { &hf_rdp_drdynvc_capa_prio3,
851
15
      { "Priority charge 3", "rdp_drdynvc.capabilities.prioritycharge3",
852
15
      FT_UINT16, BASE_DEC, NULL, 0,
853
15
      NULL, HFILL }},
854
15
    { &hf_rdp_drdynvc_pad,
855
15
      { "Padding", "rdp_drdynvc.pad",
856
15
      FT_UINT8, BASE_HEX, NULL, 0,
857
15
      NULL, HFILL }},
858
15
    { &hf_rdp_drdynvc_channelId,
859
15
      { "Channel Id", "rdp_drdynvc.channelId",
860
15
      FT_UINT32, BASE_HEX, NULL, 0,
861
15
      NULL, HFILL }},
862
15
    { &hf_rdp_drdynvc_length,
863
15
      { "Length", "rdp_drdynvc.length",
864
15
      FT_UINT32, BASE_DEC, NULL, 0,
865
15
      NULL, HFILL }},
866
15
    { &hf_rdp_drdynvc_channelName,
867
15
      { "Channel Name", "rdp_drdynvc.channelName",
868
15
      FT_STRINGZ, BASE_NONE, NULL, 0,
869
15
      NULL, HFILL }},
870
15
    { &hf_rdp_drdynvc_creationStatus,
871
15
      { "Creation status", "rdp_drdynvc.createresponse.status",
872
15
      FT_INT32, BASE_DEC, NULL, 0,
873
15
      NULL, HFILL }},
874
15
    { &hf_rdp_drdynvc_softsync_req_length,
875
15
      { "Length", "rdp_drdynvc.softsyncreq.length",
876
15
      FT_UINT32, BASE_DEC, NULL, 0,
877
15
      NULL, HFILL }},
878
15
    { &hf_rdp_drdynvc_softsync_req_flags,
879
15
      { "Flags", "rdp_drdynvc.softsyncreq.flags",
880
15
      FT_UINT16, BASE_DEC, NULL, 0,
881
15
      NULL, HFILL }},
882
15
    { &hf_rdp_drdynvc_softsync_req_ntunnels,
883
15
      { "NumberOfTunnels", "rdp_drdynvc.softsyncreq.ntunnels",
884
15
      FT_UINT16, BASE_DEC, NULL, 0,
885
15
      NULL, HFILL }},
886
15
    { &hf_rdp_drdynvc_softsync_req_channel_tunnelType,
887
15
      { "Tunnel type", "rdp_drdynvc.softsyncreq.channel.tunnelType",
888
15
      FT_UINT32, BASE_HEX, VALS(drdynvc_tunneltype_vals), 0,
889
15
      NULL, HFILL }},
890
15
    { &hf_rdp_drdynvc_softsync_req_channel_ndvc,
891
15
      { "Number of DVCs", "rdp_drdynvc.softsyncreq.channel.ndvcid",
892
15
      FT_UINT16, BASE_DEC, NULL, 0,
893
15
      NULL, HFILL }},
894
15
    { &hf_rdp_drdynvc_softsync_req_channel_dvcid,
895
15
      { "DVC Id", "rdp_drdynvc.softsyncreq.channel.dvcid",
896
15
      FT_UINT32, BASE_HEX, NULL, 0,
897
15
      NULL, HFILL }},
898
15
    { &hf_rdp_drdynvc_softsync_resp_ntunnels,
899
15
      { "Number of tunnels", "rdp_drdynvc.softsyncresp.ntunnels",
900
15
      FT_UINT32, BASE_DEC, NULL, 0,
901
15
      NULL, HFILL }},
902
15
    { &hf_rdp_drdynvc_softsync_resp_tunnel,
903
15
      { "Number of tunnels", "rdp_drdynvc.softsyncresp.tunnel",
904
15
      FT_UINT32, BASE_DEC, VALS(drdynvc_tunneltype_vals), 0,
905
15
      NULL, HFILL }},
906
15
    { &hf_rdp_drdynvc_createreq_frameid,
907
15
      { "Created at framed id", "rdp_drdynvc.createreqframeid",
908
15
      FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST), 0x0,
909
15
      NULL, HFILL }
910
15
    },
911
15
    { &hf_rdp_drdynvc_createresp_frameid,
912
15
      { "Create response at framed id", "rdp_drdynvc.createrespframeid",
913
15
      FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE), 0x0,
914
15
      NULL, HFILL }
915
15
    },
916
15
    { &hf_rdp_drdynvc_createresp_channelname,
917
15
      { "ChannelName", "rdp_drdynvc.createresp",
918
15
      FT_STRINGZ, BASE_NONE, NULL, 0x0,
919
15
      NULL, HFILL }},
920
15
    { &hf_rdp_drdynvc_data_progress,
921
15
      { "DataProgress", "rdp_drdynvc.data_progress",
922
15
      FT_STRINGZ, BASE_NONE, NULL, 0x0,
923
15
      NULL, HFILL }},
924
15
    { &hf_rdp_drdynvc_data,
925
15
      { "Data", "rdp_drdynvc.data",
926
15
      FT_BYTES, BASE_NONE, NULL, 0,
927
15
      NULL, HFILL }},
928
15
  };
929
930
  /* List of subtrees */
931
15
  static int *ett[] = {
932
15
    &ett_rdp_drdynvc,
933
15
    &ett_rdp_drdynvc_softsync_channels,
934
15
    &ett_rdp_drdynvc_softsync_channel,
935
15
    &ett_rdp_drdynvc_softsync_dvc
936
15
  };
937
938
15
  proto_rdp_drdynvc = proto_register_protocol("RDP Dynamic Channel Protocol", "DRDYNVC", "rdp_drdynvc");
939
  /* Register fields and subtrees */
940
15
  proto_register_field_array(proto_rdp_drdynvc, hf, array_length(hf));
941
15
  proto_register_subtree_array(ett, array_length(ett));
942
943
15
  register_dissector("rdp_drdynvc", dissect_rdp_drdynvc, proto_rdp_drdynvc);
944
15
}
945
946
15
void proto_reg_handoff_drdynvc(void) {
947
15
  egfx_handle = find_dissector("rdp_egfx");
948
15
  rail_handle = find_dissector("rdp_rail");
949
15
  rdpdr_handle = find_dissector("rdpdr");
950
15
  cliprdr_handle = find_dissector("rdp_cliprdr");
951
15
  snd_handle = find_dissector("rdp_snd");
952
15
  ear_handle = find_dissector("rdp_ear");
953
15
  ecam_handle = find_dissector("rdp_ecam");
954
15
}
955
956
/*
957
 * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
958
 *
959
 * Local Variables:
960
 * c-basic-offset: 2
961
 * tab-width: 8
962
 * indent-tabs-mode: nil
963
 * End:
964
 *
965
 * ex: set shiftwidth=2 tabstop=8 expandtab:
966
 * :indentSize=2:tabSize=8:noTabs=true:
967
 */