Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / packet-rdp_drdynvc.c
blob7e9ed0b8a6246967ebdd14e48f402a70d82ebd07
1 /* Packet-rdp_drdynvc.c
2 * Routines for Dynamic Virtual channel RDP packet dissection
3 * Copyright 2021, David Fort
5 * Wireshark - Network traffic analyzer
6 * By Gerald Combs <gerald@wireshark.org>
7 * Copyright 1998 Gerald Combs
9 * SPDX-License-Identifier: GPL-2.0-or-later
12 #include "config.h"
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 "packet-rdp.h"
20 #include "packet-rdpudp.h"
22 void proto_register_rdp_drdynvc(void);
23 void proto_reg_handoff_drdynvc(void);
25 static int proto_rdp_drdynvc;
27 static int hf_rdp_drdynvc_cbId;
28 static int hf_rdp_drdynvc_sp;
29 static int hf_rdp_drdynvc_pri;
30 static int hf_rdp_drdynvc_cmd;
31 static int hf_rdp_drdynvc_capa_version;
32 static int hf_rdp_drdynvc_capa_prio0;
33 static int hf_rdp_drdynvc_capa_prio1;
34 static int hf_rdp_drdynvc_capa_prio2;
35 static int hf_rdp_drdynvc_capa_prio3;
36 static int hf_rdp_drdynvc_channelId;
37 static int hf_rdp_drdynvc_pad;
38 static int hf_rdp_drdynvc_channelName;
39 static int hf_rdp_drdynvc_creationStatus;
40 static int hf_rdp_drdynvc_createresp_channelname;
41 static int hf_rdp_drdynvc_length;
42 static int hf_rdp_drdynvc_softsync_req_length;
43 static int hf_rdp_drdynvc_softsync_req_flags;
44 static int hf_rdp_drdynvc_softsync_req_ntunnels;
45 static int hf_rdp_drdynvc_softsync_req_channel_tunnelType;
46 static int hf_rdp_drdynvc_softsync_req_channel_ndvc;
47 static int hf_rdp_drdynvc_softsync_req_channel_dvcid;
48 static int hf_rdp_drdynvc_softsync_resp_ntunnels;
49 static int hf_rdp_drdynvc_softsync_resp_tunnel;
50 static int hf_rdp_drdynvc_data;
51 static int hf_rdp_drdynvc_data_progress;
54 static int ett_rdp_drdynvc;
55 static int ett_rdp_drdynvc_softsync_channels;
56 static int ett_rdp_drdynvc_softsync_channel;
57 static int ett_rdp_drdynvc_softsync_dvc;
59 dissector_handle_t egfx_handle;
60 dissector_handle_t rail_handle;
61 dissector_handle_t cliprdr_handle;
62 dissector_handle_t snd_handle;
63 dissector_handle_t ear_handle;
65 #define PNAME "RDP Dynamic Channel Protocol"
66 #define PSNAME "DRDYNVC"
67 #define PFNAME "rdp_drdynvc"
69 enum {
70 DRDYNVC_CREATE_REQUEST_PDU = 0x01,
71 DRDYNVC_DATA_FIRST_PDU = 0x02,
72 DRDYNVC_DATA_PDU = 0x03,
73 DRDYNVC_CLOSE_REQUEST_PDU = 0x04,
74 DRDYNVC_CAPABILITY_REQUEST_PDU = 0x05,
75 DRDYNVC_DATA_FIRST_COMPRESSED_PDU = 0x06,
76 DRDYNVC_DATA_COMPRESSED_PDU = 0x07,
77 DRDYNVC_SOFT_SYNC_REQUEST_PDU = 0x08,
78 DRDYNVC_SOFT_SYNC_RESPONSE_PDU = 0x09
81 typedef enum {
82 DRDYNVC_CHANNEL_UNKNOWN,
83 DRDYNVC_CHANNEL_EGFX, /* MS-RDPEGX */
84 DRDYNVC_CHANNEL_TELEMETRY, /* MS-RDPET */
85 DRDYNVC_CHANNEL_AUDIOUT, /* MS-RDPEA */
86 DRDYNVC_CHANNEL_AUDIN, /* MS-RDPEAI */
87 DRDYNVC_CHANNEL_VIDEO_CTL, /*MS-RDPEVOR */
88 DRDYNVC_CHANNEL_VIDEO_DATA, /*MS-RDPEVOR */
89 DRDYNVC_CHANNEL_CAM, /* MS-RDPECAM */
90 DRDYNVC_CHANNEL_DISPLAY, /* MS-RDPEDISP */
91 DRDYNVC_CHANNEL_GEOMETRY,/* MS-RDPEGT */
92 DRDYNVC_CHANNEL_MULTITOUCH, /* MS-RDPEI */
93 DRDYNVC_CHANNEL_AUTH_REDIR, /* MS-RDPEAR */
95 DRDYNVC_CHANNEL_RAIL, /* MS-RDPERP */
96 DRDYNVC_CHANNEL_CLIPRDR, /* MS-RDPECLIP */
97 DRDYNVC_CHANNEL_DR, /* MS-RDPDR */
98 } drdynvc_known_channel_t;
100 enum {
101 DRDYNVC_CHANNEL_PDUS_KEY = 1,
105 typedef struct {
106 bool reassembled;
107 bool decodePayload;
108 uint32_t progressStart;
109 uint32_t progressEnd;
110 uint32_t packetLen;
111 uint32_t startReassemblyFrame;
112 uint32_t endReassemblyFrame;
113 tvbuff_t* tvb;
114 } drdynvc_pdu_info_t;
116 typedef struct {
117 wmem_tree_t *pdus;
118 } drdynvc_pinfo_t;
120 typedef struct {
121 wmem_array_t *currentPacket;
122 uint32_t packetLen;
123 uint32_t pendingLen;
124 uint32_t startFrame;
125 uint32_t endReassemblyFrame;
126 wmem_array_t *chunks;
127 } drdynvc_pending_packet_t;
129 /** @brief context associated with a dynamic channel */
130 typedef struct {
131 drdynvc_known_channel_t type;
132 char *name;
133 uint32_t channelId;
135 drdynvc_pending_packet_t pending_cs;
136 drdynvc_pending_packet_t pending_sc;
137 } drdynvc_channel_def_t;
139 typedef struct _drdynvc_conv_info_t {
140 wmem_multimap_t *channels;
141 } drdynvc_conv_info_t;
144 typedef struct {
145 const char *name;
146 const char *shortName;
147 drdynvc_known_channel_t type;
148 } drdynvc_know_channel_def;
150 static drdynvc_know_channel_def knownChannels[] = {
151 {"AUDIO_INPUT", "audin", DRDYNVC_CHANNEL_AUDIN},
152 {"AUDIO_PLAYBACK_DVC", "audiout", DRDYNVC_CHANNEL_AUDIOUT},
153 {"AUDIO_PLAYBACK_LOSSY_DVC", "audiout lossy", DRDYNVC_CHANNEL_AUDIOUT},
154 {"RDCamera_Device_Enumerator", "cam", DRDYNVC_CHANNEL_CAM},
155 {"Microsoft::Windows::RDS::Video::Control::v08.01", "videoctl", DRDYNVC_CHANNEL_VIDEO_CTL},
156 {"Microsoft::Windows::RDS::Video::Data::v08.01", "videodata", DRDYNVC_CHANNEL_VIDEO_DATA},
157 {"Microsoft::Windows::RDS::AuthRedirection", "authredir", DRDYNVC_CHANNEL_AUTH_REDIR},
158 {"Microsoft::Windows::RDS::Telemetry", "telemetry", DRDYNVC_CHANNEL_TELEMETRY},
159 {"Microsoft::Windows::RDS::Graphics", "egfx", DRDYNVC_CHANNEL_EGFX},
160 {"Microsoft::Windows::RDS::DisplayControl", "display", DRDYNVC_CHANNEL_DISPLAY},
161 {"Microsoft::Windows::RDS::Geometry::v08.01", "geometry", DRDYNVC_CHANNEL_GEOMETRY},
162 {"Microsoft::Windows::RDS::Input", "input", DRDYNVC_CHANNEL_MULTITOUCH},
164 /* static channels that can be reopened on the dynamic channel */
165 {"rail", "rail", DRDYNVC_CHANNEL_RAIL},
166 {"cliprdr", "cliprdr", DRDYNVC_CHANNEL_CLIPRDR},
167 {"rdpdr", "rdpdr", DRDYNVC_CHANNEL_DR},
170 static const value_string drdynvc_tunneltype_vals[] = {
171 { 0x1, "reliable" },
172 { 0x3, "lossy" },
173 { 0x0, NULL},
176 static const value_string rdp_drdynvc_cbId_vals[] = {
177 { 0x0, "1 byte" },
178 { 0x1, "2 bytes" },
179 { 0x2, "4 bytes" },
180 { 0x0, NULL},
183 static const value_string rdp_drdynvc_prio_vals[] = {
184 { 0x0, "PriorityCharge0" },
185 { 0x1, "PriorityCharge1" },
186 { 0x2, "PriorityCharge2" },
187 { 0x3, "PriorityCharge3" },
188 { 0x0, NULL},
191 static const value_string rdp_drdynvc_cmd_vals[] = {
192 { DRDYNVC_CREATE_REQUEST_PDU, "Create PDU" },
193 { DRDYNVC_DATA_FIRST_PDU, "Data first PDU" },
194 { DRDYNVC_DATA_PDU, "Data PDU" },
195 { DRDYNVC_CLOSE_REQUEST_PDU, "Close PDU" },
196 { DRDYNVC_CAPABILITY_REQUEST_PDU, "Capabilities PDU" },
197 { DRDYNVC_DATA_FIRST_COMPRESSED_PDU, "Data first compressed PDU" },
198 { DRDYNVC_DATA_COMPRESSED_PDU, "Data compressed PDU" },
199 { DRDYNVC_SOFT_SYNC_REQUEST_PDU,"Soft-Sync request PDU" },
200 { DRDYNVC_SOFT_SYNC_RESPONSE_PDU,"Soft-Sync response PDU" },
201 { 0x0, NULL},
204 static void
205 drdynvc_pending_packet_init(drdynvc_pending_packet_t *pending, uint32_t startFrame)
207 pending->packetLen = 0;
208 pending->pendingLen = 0;
209 pending->startFrame = startFrame;
210 pending->endReassemblyFrame = 0;
211 pending->currentPacket = NULL;
212 pending->chunks = NULL;
215 static drdynvc_known_channel_t
216 drdynvc_find_channel_type(const char *name)
218 unsigned i;
220 for (i = 0; i < array_length(knownChannels); i++)
222 if (strcmp(knownChannels[i].name, name) == 0)
223 return knownChannels[i].type;
225 return DRDYNVC_CHANNEL_UNKNOWN;
228 static drdynvc_conv_info_t *
229 drdynvc_get_conversation_data(packet_info *pinfo)
231 conversation_t *conversation, *conversation_tcp;
232 drdynvc_conv_info_t *info;
234 conversation = find_or_create_conversation(pinfo);
236 info = (drdynvc_conv_info_t *)conversation_get_proto_data(conversation, proto_rdp_drdynvc);
237 if (!info) {
238 conversation_tcp = rdp_find_tcp_conversation_from_udp(conversation);
239 if (conversation_tcp)
240 info = (drdynvc_conv_info_t *)conversation_get_proto_data(conversation_tcp, proto_rdp_drdynvc);
243 if (info == NULL) {
244 info = wmem_new0(wmem_file_scope(), drdynvc_conv_info_t);
245 info->channels = wmem_multimap_new(wmem_file_scope(), g_direct_hash, g_direct_equal);
246 conversation_add_proto_data(conversation, proto_rdp_drdynvc, info);
249 return info;
253 static int
254 dissect_rdp_vlength(tvbuff_t *tvb, int hf_index, int offset, uint8_t vlen, proto_tree *tree, uint32_t *ret)
256 int len;
257 uint32_t value;
259 switch (vlen) {
260 case 0:
261 value = tvb_get_uint8(tvb, offset);
262 len = 1;
263 break;
264 case 1:
265 value = tvb_get_uint16(tvb, offset, ENC_LITTLE_ENDIAN);
266 len = 2;
267 break;
268 case 2:
269 value = tvb_get_uint32(tvb, offset, ENC_LITTLE_ENDIAN);
270 len = 4;
271 break;
272 default:
273 if (ret)
274 *ret = 0;
275 return 0;
278 proto_tree_add_uint(tree, hf_index, tvb, offset, len, value);
279 if (ret)
280 *ret = value;
281 return len;
284 static const char *
285 find_channel_name_by_id(packet_info *pinfo, drdynvc_conv_info_t *dyninfo, uint32_t dvcId) {
286 drdynvc_channel_def_t *dynChannel = wmem_multimap_lookup32_le(dyninfo->channels, GUINT_TO_POINTER(dvcId), pinfo->num);
287 if (dynChannel)
288 return dynChannel->name;
290 return NULL;
293 static drdynvc_pinfo_t *getDrDynPacketInfo(packet_info *pinfo)
295 drdynvc_pinfo_t *ret = p_get_proto_data(wmem_file_scope(), pinfo, proto_rdp_drdynvc, DRDYNVC_CHANNEL_PDUS_KEY);
296 if (ret)
297 return ret;
299 ret = wmem_alloc(wmem_file_scope(), sizeof(*ret));
300 ret->pdus = wmem_tree_new(wmem_file_scope());
302 p_set_proto_data(wmem_file_scope(), pinfo, proto_rdp_drdynvc, DRDYNVC_CHANNEL_PDUS_KEY, ret);
303 return ret;
306 static int
307 dissect_rdp_drdynvc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void *data _U_)
309 proto_item *item;
310 proto_tree *tree;
311 int offset = 0;
312 uint8_t cbIdSpCmd, cmdId;
313 uint8_t cbId, Len;
314 bool haveChannelId, havePri, haveLen;
315 bool isServerTarget = rdp_isServerAddressTarget(pinfo);
316 uint32_t channelId = 0;
317 uint32_t fullPduLen = 0;
318 drdynvc_conv_info_t *info;
319 drdynvc_channel_def_t *channel = NULL;
321 col_set_str(pinfo->cinfo, COL_PROTOCOL, "DRDYNVC");
322 col_clear(pinfo->cinfo, COL_INFO);
324 parent_tree = proto_tree_get_root(parent_tree);
325 item = proto_tree_add_item(parent_tree, proto_rdp_drdynvc, tvb, 0, -1, ENC_NA);
326 tree = proto_item_add_subtree(item, ett_rdp_drdynvc);
328 cbIdSpCmd = tvb_get_uint8(tvb, offset);
329 cmdId = (cbIdSpCmd >> 4) & 0xf;
330 cbId = (cbIdSpCmd & 0x3);
332 haveChannelId = true;
333 havePri = false;
334 haveLen = false;
335 switch (cmdId) {
336 case DRDYNVC_CREATE_REQUEST_PDU:
337 havePri = true;
338 break;
339 case DRDYNVC_DATA_FIRST_PDU:
340 haveLen = true;
341 break;
342 case DRDYNVC_DATA_FIRST_COMPRESSED_PDU:
343 haveLen = true;
344 break;
345 case DRDYNVC_CAPABILITY_REQUEST_PDU:
346 case DRDYNVC_SOFT_SYNC_REQUEST_PDU:
347 case DRDYNVC_SOFT_SYNC_RESPONSE_PDU:
348 haveChannelId = false;
349 break;
350 default:
351 break;
354 proto_tree_add_item(tree, hf_rdp_drdynvc_cbId, tvb, offset, 1, ENC_NA);
355 if (havePri)
356 proto_tree_add_item(tree, hf_rdp_drdynvc_pri, tvb, offset, 1, ENC_NA);
357 else
358 proto_tree_add_item(tree, hf_rdp_drdynvc_sp, tvb, offset, 1, ENC_NA);
359 proto_tree_add_item(tree, hf_rdp_drdynvc_cmd, tvb, offset, 1, ENC_NA);
361 offset++;
363 info = drdynvc_get_conversation_data(pinfo);
364 if (haveChannelId) {
365 offset += dissect_rdp_vlength(tvb, hf_rdp_drdynvc_channelId, offset, cbId, tree, &channelId);
367 channel = wmem_multimap_lookup32_le(info->channels, GUINT_TO_POINTER(channelId), pinfo->num);
370 if (haveLen) {
371 Len = (cbIdSpCmd >> 2) & 0x3;
372 offset += dissect_rdp_vlength(tvb, hf_rdp_drdynvc_length, offset, Len, tree, &fullPduLen);
375 switch (cmdId) {
376 case DRDYNVC_CREATE_REQUEST_PDU:
377 if (!isServerTarget) {
378 unsigned nameLen = tvb_strsize(tvb, offset);
380 col_set_str(pinfo->cinfo, COL_INFO, "CreateChannel Request");
381 proto_tree_add_item(tree, hf_rdp_drdynvc_channelName, tvb, offset, -1, ENC_ASCII);
383 if (!PINFO_FD_VISITED(pinfo)) {
384 channel = wmem_alloc(wmem_file_scope(), sizeof(*channel));
385 channel->channelId = channelId;
386 channel->name = tvb_get_string_enc(wmem_file_scope(), tvb, offset, nameLen, ENC_ASCII);
387 channel->type = drdynvc_find_channel_type(channel->name);
388 drdynvc_pending_packet_init(&channel->pending_cs, pinfo->num);
389 drdynvc_pending_packet_init(&channel->pending_sc, pinfo->num);
391 wmem_multimap_insert32(info->channels, GUINT_TO_POINTER(channelId), pinfo->num, channel);
394 } else {
395 col_set_str(pinfo->cinfo, COL_INFO, "CreateChannel Response");
397 if (channel) {
398 proto_item_set_generated(
399 proto_tree_add_string_format_value(tree, hf_rdp_drdynvc_createresp_channelname, tvb, offset, 0, NULL, "%s", channel->name)
402 proto_tree_add_item(tree, hf_rdp_drdynvc_creationStatus, tvb, offset, 4, ENC_NA);
405 break;
406 case DRDYNVC_CAPABILITY_REQUEST_PDU: {
407 /* Pad */
408 proto_tree_add_item(tree, hf_rdp_drdynvc_pad, tvb, offset, 1, ENC_NA);
409 offset++;
411 uint32_t version;
412 proto_tree_add_item_ret_uint(tree, hf_rdp_drdynvc_capa_version, tvb, offset, 2, ENC_LITTLE_ENDIAN, &version);
413 offset += 2;
415 if (!isServerTarget) {
416 col_set_str(pinfo->cinfo, COL_INFO, "Capabilities request");
418 if (version > 1) {
419 proto_tree_add_item(tree, hf_rdp_drdynvc_capa_prio0, tvb, offset, 2, ENC_LITTLE_ENDIAN);
420 offset += 2;
421 proto_tree_add_item(tree, hf_rdp_drdynvc_capa_prio1, tvb, offset, 2, ENC_LITTLE_ENDIAN);
422 offset += 2;
423 proto_tree_add_item(tree, hf_rdp_drdynvc_capa_prio2, tvb, offset, 2, ENC_LITTLE_ENDIAN);
424 offset += 2;
425 proto_tree_add_item(tree, hf_rdp_drdynvc_capa_prio3, tvb, offset, 2, ENC_LITTLE_ENDIAN);
426 offset += 2;
428 } else {
429 col_set_str(pinfo->cinfo, COL_INFO, "Capabilities response");
431 break;
433 case DRDYNVC_DATA_FIRST_PDU: {
434 col_set_str(pinfo->cinfo, COL_INFO, "Data first");
436 if (channel) {
437 drdynvc_pdu_info_t *pduInfo = NULL;
438 drdynvc_pending_packet_t *pendingPacket = isServerTarget ? &channel->pending_cs : &channel->pending_sc;
439 int payloadLen = tvb_reported_length_remaining(tvb, offset);
440 bool isSinglePacket = (fullPduLen == (uint32_t)payloadLen);
441 drdynvc_pinfo_t *drdynvcPinfo = getDrDynPacketInfo(pinfo);
442 uint32_t key = crc32_ccitt_tvb_offset(tvb, offset, payloadLen);
444 proto_item_set_generated(
445 proto_tree_add_string_format_value(tree, hf_rdp_drdynvc_createresp_channelname, tvb, offset, 0, NULL, "%s", channel->name)
448 proto_item_set_generated(
449 proto_tree_add_string_format_value(tree, hf_rdp_drdynvc_data_progress, tvb, offset, 0, NULL, "0-%d/%d", payloadLen, fullPduLen)
452 if (!PINFO_FD_VISITED(pinfo)) {
453 if (!isSinglePacket) {
454 if (pendingPacket->chunks)
455 wmem_destroy_array(pendingPacket->chunks);
456 pendingPacket->chunks = wmem_array_new(wmem_file_scope(), sizeof(drdynvc_pdu_info_t*));
458 pduInfo = wmem_alloc(wmem_file_scope(), sizeof(*pduInfo));
459 pduInfo->reassembled = true;
460 pduInfo->startReassemblyFrame = pinfo->num;
461 pduInfo->progressStart = 0;
462 pduInfo->progressEnd = fullPduLen;
463 pduInfo->tvb = NULL;
465 wmem_tree_insert32(drdynvcPinfo->pdus, key, pduInfo);
466 wmem_array_append(pendingPacket->chunks, &pduInfo, 1);
468 pendingPacket->packetLen = fullPduLen;
469 pendingPacket->pendingLen = fullPduLen - payloadLen;
470 pendingPacket->startFrame = pinfo->num;
471 pendingPacket->currentPacket = wmem_array_sized_new(wmem_file_scope(), 1, fullPduLen);
472 wmem_array_append(pendingPacket->currentPacket, tvb_get_ptr(tvb, offset, payloadLen), payloadLen);
473 } else {
474 if (pendingPacket->pendingLen || pendingPacket->chunks)
475 printf("(%d) looks like we have a non completed packet...\n", pinfo->num);
476 if (pendingPacket->chunks)
477 wmem_destroy_array(pendingPacket->chunks);
478 memset(pendingPacket, 0, sizeof(*pendingPacket));
480 } else {
481 pduInfo = (drdynvc_pdu_info_t*)wmem_tree_lookup32(drdynvcPinfo->pdus, key);
484 if (isSinglePacket) {
485 switch (channel->type) {
486 case DRDYNVC_CHANNEL_EGFX:
487 call_dissector(egfx_handle, tvb_new_subset_remaining(tvb, offset), pinfo, tree);
488 break;
489 case DRDYNVC_CHANNEL_RAIL:
490 call_dissector(rail_handle, tvb_new_subset_remaining(tvb, offset), pinfo, tree);
491 break;
492 case DRDYNVC_CHANNEL_CLIPRDR:
493 call_dissector(cliprdr_handle, tvb_new_subset_remaining(tvb, offset), pinfo, tree);
494 break;
495 case DRDYNVC_CHANNEL_AUDIOUT:
496 call_dissector(snd_handle, tvb_new_subset_remaining(tvb, offset), pinfo, tree);
497 break;
498 case DRDYNVC_CHANNEL_AUTH_REDIR:
499 call_dissector(ear_handle, tvb_new_subset_remaining(tvb, offset), pinfo, tree);
500 break;
501 default:
502 proto_tree_add_item(tree, hf_rdp_drdynvc_data, tvb, offset, -1, ENC_NA);
503 break;
506 offset += payloadLen;
507 return offset;
512 proto_tree_add_item(tree, hf_rdp_drdynvc_data, tvb, offset, -1, ENC_NA);
513 break;
515 case DRDYNVC_DATA_PDU: {
516 col_set_str(pinfo->cinfo, COL_INFO, "Data");
518 if (channel) {
519 tvbuff_t *targetTvb = NULL;
521 proto_item_set_generated(
522 proto_tree_add_string_format_value(tree, hf_rdp_drdynvc_createresp_channelname, tvb, offset, 0, NULL, "%s", channel->name)
525 drdynvc_pinfo_t *drdynvcPinfo = getDrDynPacketInfo(pinfo);
526 drdynvc_pdu_info_t *pduInfo = NULL;
527 int payloadLen = tvb_reported_length_remaining(tvb, offset);
528 uint32_t key = crc32_ccitt_tvb_offset(tvb, offset, payloadLen);
530 if (!PINFO_FD_VISITED(pinfo)) {
531 drdynvc_pending_packet_t *pendingPacket = isServerTarget ? &channel->pending_cs : &channel->pending_sc;
533 pduInfo = wmem_alloc(wmem_file_scope(), sizeof(*pduInfo));
534 wmem_tree_insert32(drdynvcPinfo->pdus, key, pduInfo);
536 if (pendingPacket->pendingLen) {
537 /* we have a fragmented packet in progress */
538 if ((uint32_t)payloadLen > pendingPacket->pendingLen) {
539 // TODO: error
540 printf("num=%d error payload too big\n", pinfo->num);
541 return offset;
544 pduInfo->reassembled = true;
545 pduInfo->decodePayload = false;
546 pduInfo->progressStart = pendingPacket->packetLen - pendingPacket->pendingLen;
547 pduInfo->progressEnd = pduInfo->progressStart + payloadLen;
548 pduInfo->packetLen = pendingPacket->packetLen;
550 wmem_array_append(pendingPacket->chunks, &pduInfo, 1);
552 pendingPacket->pendingLen -= payloadLen;
553 wmem_array_append(pendingPacket->currentPacket, tvb_get_ptr(tvb, offset, payloadLen), payloadLen);
555 if (!pendingPacket->pendingLen) {
556 /* last packet of the reassembly */
557 int reassembled_len = wmem_array_get_count(pendingPacket->currentPacket);
558 pduInfo->tvb = tvb_new_real_data(wmem_array_get_raw(pendingPacket->currentPacket), reassembled_len, reassembled_len);
559 pduInfo->decodePayload = true;
560 pendingPacket->currentPacket = NULL;
562 for (unsigned i = 0; i < wmem_array_get_count(pendingPacket->chunks); i++) {
563 drdynvc_pdu_info_t *chunk = *(drdynvc_pdu_info_t **)wmem_array_index(pendingPacket->chunks, i);
564 chunk->endReassemblyFrame = pinfo->num;
566 wmem_destroy_array(pendingPacket->chunks);
567 pendingPacket->chunks = NULL;
569 } else {
570 /* single data packet */
571 pduInfo->reassembled = false;
572 pduInfo->decodePayload = true;
573 pduInfo->progressStart = 0;
574 pduInfo->progressEnd = payloadLen;
575 pduInfo->packetLen = payloadLen;
576 pduInfo->tvb = NULL;
577 pduInfo->startReassemblyFrame = pduInfo->endReassemblyFrame = pinfo->num;
579 } else {
580 pduInfo = (drdynvc_pdu_info_t*)wmem_tree_lookup32(drdynvcPinfo->pdus, key);
583 if (pduInfo) {
584 proto_item_set_generated(
585 proto_tree_add_string_format_value(tree, hf_rdp_drdynvc_data_progress, tvb, offset, 0, NULL, "%d-%d/%d",
586 pduInfo->progressStart, pduInfo->progressEnd, pduInfo->packetLen)
589 if (pduInfo->tvb) {
590 targetTvb = pduInfo->tvb;
591 add_new_data_source(pinfo, targetTvb, "Reassembled DRDYNVC");
592 } else {
593 targetTvb = tvb_new_subset_remaining(tvb, offset);
596 if (pduInfo->endReassemblyFrame && (pduInfo->endReassemblyFrame != pinfo->num)) {
597 // TODO: show a link to the end frame ?
601 if (pduInfo && pduInfo->decodePayload) {
602 switch (channel->type) {
603 case DRDYNVC_CHANNEL_EGFX:
604 call_dissector(egfx_handle, targetTvb, pinfo, tree);
605 break;
606 case DRDYNVC_CHANNEL_RAIL:
607 call_dissector(rail_handle, targetTvb, pinfo, tree);
608 break;
609 case DRDYNVC_CHANNEL_CLIPRDR:
610 call_dissector(cliprdr_handle, targetTvb, pinfo, tree);
611 break;
612 case DRDYNVC_CHANNEL_AUDIOUT:
613 call_dissector(snd_handle, targetTvb, pinfo, tree);
614 break;
615 case DRDYNVC_CHANNEL_AUTH_REDIR:
616 call_dissector(ear_handle, targetTvb, pinfo, tree);
617 break;
618 default:
619 proto_tree_add_item(tree, hf_rdp_drdynvc_data, targetTvb, 0, -1, ENC_NA);
620 break;
622 return tvb_reported_length(tvb);
626 proto_tree_add_item(tree, hf_rdp_drdynvc_data, tvb, offset, -1, ENC_NA);
627 return tvb_reported_length(tvb);
629 case DRDYNVC_DATA_FIRST_COMPRESSED_PDU:
630 col_set_str(pinfo->cinfo, COL_INFO, "Data compressed first");
631 break;
632 case DRDYNVC_DATA_COMPRESSED_PDU:
633 col_set_str(pinfo->cinfo, COL_INFO, "Data compressed");
634 break;
635 case DRDYNVC_SOFT_SYNC_REQUEST_PDU: {
636 uint32_t ntunnels;
637 uint32_t flags;
639 col_set_str(pinfo->cinfo, COL_INFO, "SoftSync Request");
641 /* Pad */
642 proto_tree_add_item(tree, hf_rdp_drdynvc_pad, tvb, offset, 1, ENC_NA);
643 offset++;
645 proto_tree_add_item(tree, hf_rdp_drdynvc_softsync_req_length, tvb, offset, 4, ENC_LITTLE_ENDIAN);
646 offset += 4;
648 proto_tree_add_item_ret_uint(tree, hf_rdp_drdynvc_softsync_req_flags, tvb, offset, 2, ENC_LITTLE_ENDIAN, &flags);
649 offset += 2;
650 // XXX: TODO should decode flags but they are always set to SOFT_SYNC_TCP_FLUSHED|SOFT_SYNC_CHANNEL_LIST_PRESENT
652 proto_tree_add_item_ret_uint(tree, hf_rdp_drdynvc_softsync_req_ntunnels, tvb, offset, 2, ENC_LITTLE_ENDIAN, &ntunnels);
653 offset += 2;
655 if (flags & 0x02) { /* SOFT_SYNC_CHANNEL_LIST_PRESENT */
656 uint16_t i;
657 proto_tree *tunnels_tree = proto_tree_add_subtree(tree, tvb, offset, -1, ett_rdp_drdynvc_softsync_channels, NULL, "Channels");
659 for (i = 0; i < ntunnels; i++) {
660 uint16_t j;
661 uint32_t tunnelType = tvb_get_uint32(tvb, offset, ENC_LITTLE_ENDIAN);
662 uint16_t ndvcs = tvb_get_uint16(tvb, offset + 4, ENC_LITTLE_ENDIAN);
663 int channelSz = 4 + 2 + (ndvcs * 4);
664 proto_tree *channel_tree;
665 const char *label = (tunnelType == 0x1) ? "Reliable channels" : "Lossy channels";
667 channel_tree = proto_tree_add_subtree(tunnels_tree, tvb, offset, channelSz, ett_rdp_drdynvc_softsync_channel, NULL, label);
669 proto_tree_add_item(channel_tree, hf_rdp_drdynvc_softsync_req_channel_tunnelType, tvb, offset, 4, ENC_LITTLE_ENDIAN);
670 offset += 4;
672 proto_tree_add_item(channel_tree, hf_rdp_drdynvc_softsync_req_channel_ndvc, tvb, offset, 2, ENC_LITTLE_ENDIAN);
673 offset += 2;
675 for (j = 0; j < ndvcs; j++, offset += 4) {
676 proto_tree *dvc_tree;
677 uint32_t dvcId;
678 const char *showLabel;
680 dvcId = tvb_get_uint32(tvb, offset, ENC_LITTLE_ENDIAN);
681 showLabel = label = find_channel_name_by_id(pinfo, info, dvcId);
682 if (!label)
683 showLabel = "DVC";
684 dvc_tree = proto_tree_add_subtree(channel_tree, tvb, offset, 4, ett_rdp_drdynvc_softsync_dvc, NULL, showLabel);
685 proto_tree_add_item(dvc_tree, hf_rdp_drdynvc_softsync_req_channel_dvcid, tvb, offset, 4, ENC_LITTLE_ENDIAN);
687 if (label) {
688 proto_item *pi = proto_tree_add_string_format(dvc_tree, hf_rdp_drdynvc_channelName, tvb, offset, 4, label, "%s", label);
689 proto_item_set_generated(pi);
694 break;
696 case DRDYNVC_SOFT_SYNC_RESPONSE_PDU: {
697 uint32_t ntunnels, i;
699 col_set_str(pinfo->cinfo, COL_INFO, "SoftSync Response");
701 /* Pad */
702 proto_tree_add_item(tree, hf_rdp_drdynvc_pad, tvb, offset, 1, ENC_NA);
703 offset++;
705 proto_tree_add_item_ret_uint(tree, hf_rdp_drdynvc_softsync_resp_ntunnels, tvb, offset, 4, ENC_LITTLE_ENDIAN, &ntunnels);
706 offset += 4;
708 if (ntunnels) {
709 proto_tree *tunnels_tree = proto_tree_add_subtree(tree, tvb, offset, 4, ett_rdp_drdynvc_softsync_dvc, NULL, "TunnelsToSwitch");
710 for (i = 0; i < ntunnels; i++, offset += 4) {
711 proto_tree_add_item(tunnels_tree, hf_rdp_drdynvc_softsync_resp_tunnel, tvb, offset, 4, ENC_LITTLE_ENDIAN);
714 break;
716 case DRDYNVC_CLOSE_REQUEST_PDU: {
717 col_set_str(pinfo->cinfo, COL_INFO, "Close request");
718 if (channel) {
719 proto_item_set_generated(
720 proto_tree_add_string_format_value(tree, hf_rdp_drdynvc_channelName, tvb, offset, 0, NULL, "%s", channel->name)
723 break;
725 default:
726 break;
728 return offset;
731 void proto_register_rdp_drdynvc(void) {
733 /* List of fields */
734 static hf_register_info hf[] = {
735 { &hf_rdp_drdynvc_cbId,
736 { "ChannelId length", "rdp_drdynvc.cbid",
737 FT_UINT8, BASE_HEX, VALS(rdp_drdynvc_cbId_vals), 0x3,
738 NULL, HFILL }},
739 { &hf_rdp_drdynvc_sp,
740 { "Sp", "rdp_drdynvc.sp",
741 FT_UINT8, BASE_HEX, NULL, 0xc,
742 NULL, HFILL }},
743 { &hf_rdp_drdynvc_pri,
744 { "Pri", "rdp_drdynvc.pri",
745 FT_UINT8, BASE_HEX, VALS(rdp_drdynvc_prio_vals), 0xc,
746 NULL, HFILL }},
747 { &hf_rdp_drdynvc_cmd,
748 { "PDU type", "rdp_drdynvc.cmd",
749 FT_UINT8, BASE_HEX, VALS(rdp_drdynvc_cmd_vals), 0xf0,
750 NULL, HFILL }},
751 { &hf_rdp_drdynvc_capa_version,
752 { "Version", "rdp_drdynvc.capabilities.version",
753 FT_UINT16, BASE_DEC, NULL, 0,
754 NULL, HFILL }},
755 { &hf_rdp_drdynvc_capa_prio0,
756 { "Priority charge 0", "rdp_drdynvc.capabilities.prioritycharge0",
757 FT_UINT16, BASE_DEC, NULL, 0,
758 NULL, HFILL }},
759 { &hf_rdp_drdynvc_capa_prio1,
760 { "Priority charge 1", "rdp_drdynvc.capabilities.prioritycharge1",
761 FT_UINT16, BASE_DEC, NULL, 0,
762 NULL, HFILL }},
763 { &hf_rdp_drdynvc_capa_prio2,
764 { "Priority charge 2", "rdp_drdynvc.capabilities.prioritycharge2",
765 FT_UINT16, BASE_DEC, NULL, 0,
766 NULL, HFILL }},
767 { &hf_rdp_drdynvc_capa_prio3,
768 { "Priority charge 3", "rdp_drdynvc.capabilities.prioritycharge3",
769 FT_UINT16, BASE_DEC, NULL, 0,
770 NULL, HFILL }},
771 { &hf_rdp_drdynvc_pad,
772 { "Padding", "rdp_drdynvc.pad",
773 FT_UINT8, BASE_HEX, NULL, 0,
774 NULL, HFILL }},
775 { &hf_rdp_drdynvc_channelId,
776 { "Channel Id", "rdp_drdynvc.channelId",
777 FT_UINT32, BASE_HEX, NULL, 0,
778 NULL, HFILL }},
779 { &hf_rdp_drdynvc_length,
780 { "Length", "rdp_drdynvc.length",
781 FT_UINT32, BASE_DEC, NULL, 0,
782 NULL, HFILL }},
783 { &hf_rdp_drdynvc_channelName,
784 { "Channel Name", "rdp_drdynvc.channelName",
785 FT_STRINGZ, BASE_NONE, NULL, 0,
786 NULL, HFILL }},
787 { &hf_rdp_drdynvc_creationStatus,
788 { "Creation status", "rdp_drdynvc.createresponse.status",
789 FT_INT32, BASE_DEC, NULL, 0,
790 NULL, HFILL }},
791 { &hf_rdp_drdynvc_softsync_req_length,
792 { "Length", "rdp_drdynvc.softsyncreq.length",
793 FT_UINT32, BASE_DEC, NULL, 0,
794 NULL, HFILL }},
795 { &hf_rdp_drdynvc_softsync_req_flags,
796 { "Flags", "rdp_drdynvc.softsyncreq.flags",
797 FT_UINT16, BASE_DEC, NULL, 0,
798 NULL, HFILL }},
799 { &hf_rdp_drdynvc_softsync_req_ntunnels,
800 { "NumberOfTunnels", "rdp_drdynvc.softsyncreq.ntunnels",
801 FT_UINT16, BASE_DEC, NULL, 0,
802 NULL, HFILL }},
803 { &hf_rdp_drdynvc_softsync_req_channel_tunnelType,
804 { "Tunnel type", "rdp_drdynvc.softsyncreq.channel.tunnelType",
805 FT_UINT32, BASE_HEX, VALS(drdynvc_tunneltype_vals), 0,
806 NULL, HFILL }},
807 { &hf_rdp_drdynvc_softsync_req_channel_ndvc,
808 { "Number of DVCs", "rdp_drdynvc.softsyncreq.channel.ndvcid",
809 FT_UINT16, BASE_DEC, NULL, 0,
810 NULL, HFILL }},
811 { &hf_rdp_drdynvc_softsync_req_channel_dvcid,
812 { "DVC Id", "rdp_drdynvc.softsyncreq.channel.dvcid",
813 FT_UINT32, BASE_HEX, NULL, 0,
814 NULL, HFILL }},
815 { &hf_rdp_drdynvc_softsync_resp_ntunnels,
816 { "Number of tunnels", "rdp_drdynvc.softsyncresp.ntunnels",
817 FT_UINT32, BASE_DEC, NULL, 0,
818 NULL, HFILL }},
819 { &hf_rdp_drdynvc_softsync_resp_tunnel,
820 { "Number of tunnels", "rdp_drdynvc.softsyncresp.tunnel",
821 FT_UINT32, BASE_DEC, VALS(drdynvc_tunneltype_vals), 0,
822 NULL, HFILL }},
823 { &hf_rdp_drdynvc_createresp_channelname,
824 { "ChannelName", "rdp_drdynvc.createresp",
825 FT_STRINGZ, BASE_NONE, NULL, 0x0,
826 NULL, HFILL }},
827 { &hf_rdp_drdynvc_data_progress,
828 { "DataProgress", "rdp_drdynvc.data_progress",
829 FT_STRINGZ, BASE_NONE, NULL, 0x0,
830 NULL, HFILL }},
831 { &hf_rdp_drdynvc_data,
832 { "Data", "rdp_drdynvc.data",
833 FT_BYTES, BASE_NONE, NULL, 0,
834 NULL, HFILL }},
837 /* List of subtrees */
838 static int *ett[] = {
839 &ett_rdp_drdynvc,
840 &ett_rdp_drdynvc_softsync_channels,
841 &ett_rdp_drdynvc_softsync_channel,
842 &ett_rdp_drdynvc_softsync_dvc
845 proto_rdp_drdynvc = proto_register_protocol(PNAME, PSNAME, PFNAME);
846 /* Register fields and subtrees */
847 proto_register_field_array(proto_rdp_drdynvc, hf, array_length(hf));
848 proto_register_subtree_array(ett, array_length(ett));
850 register_dissector("rdp_drdynvc", dissect_rdp_drdynvc, proto_rdp_drdynvc);
853 void proto_reg_handoff_drdynvc(void) {
854 egfx_handle = find_dissector("rdp_egfx");
855 rail_handle = find_dissector("rdp_rail");
856 cliprdr_handle = find_dissector("rdp_cliprdr");
857 snd_handle = find_dissector("rdp_snd");
858 ear_handle = find_dissector("rdp_ear");
862 * Editor modelines - https://www.wireshark.org/tools/modelines.html
864 * Local Variables:
865 * c-basic-offset: 2
866 * tab-width: 8
867 * indent-tabs-mode: nil
868 * End:
870 * ex: set shiftwidth=2 tabstop=8 expandtab:
871 * :indentSize=2:tabSize=8:noTabs=true: