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
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"
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
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
;
101 DRDYNVC_CHANNEL_PDUS_KEY
= 1,
108 uint32_t progressStart
;
109 uint32_t progressEnd
;
111 uint32_t startReassemblyFrame
;
112 uint32_t endReassemblyFrame
;
114 } drdynvc_pdu_info_t
;
121 wmem_array_t
*currentPacket
;
125 uint32_t endReassemblyFrame
;
126 wmem_array_t
*chunks
;
127 } drdynvc_pending_packet_t
;
129 /** @brief context associated with a dynamic channel */
131 drdynvc_known_channel_t type
;
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
;
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
[] = {
176 static const value_string rdp_drdynvc_cbId_vals
[] = {
183 static const value_string rdp_drdynvc_prio_vals
[] = {
184 { 0x0, "PriorityCharge0" },
185 { 0x1, "PriorityCharge1" },
186 { 0x2, "PriorityCharge2" },
187 { 0x3, "PriorityCharge3" },
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" },
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
)
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
);
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
);
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
);
254 dissect_rdp_vlength(tvbuff_t
*tvb
, int hf_index
, int offset
, uint8_t vlen
, proto_tree
*tree
, uint32_t *ret
)
261 value
= tvb_get_uint8(tvb
, offset
);
265 value
= tvb_get_uint16(tvb
, offset
, ENC_LITTLE_ENDIAN
);
269 value
= tvb_get_uint32(tvb
, offset
, ENC_LITTLE_ENDIAN
);
278 proto_tree_add_uint(tree
, hf_index
, tvb
, offset
, len
, value
);
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
);
288 return dynChannel
->name
;
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
);
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
);
307 dissect_rdp_drdynvc(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*parent_tree
, void *data _U_
)
312 uint8_t cbIdSpCmd
, cmdId
;
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;
336 case DRDYNVC_CREATE_REQUEST_PDU
:
339 case DRDYNVC_DATA_FIRST_PDU
:
342 case DRDYNVC_DATA_FIRST_COMPRESSED_PDU
:
345 case DRDYNVC_CAPABILITY_REQUEST_PDU
:
346 case DRDYNVC_SOFT_SYNC_REQUEST_PDU
:
347 case DRDYNVC_SOFT_SYNC_RESPONSE_PDU
:
348 haveChannelId
= false;
354 proto_tree_add_item(tree
, hf_rdp_drdynvc_cbId
, tvb
, offset
, 1, ENC_NA
);
356 proto_tree_add_item(tree
, hf_rdp_drdynvc_pri
, tvb
, offset
, 1, ENC_NA
);
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
);
363 info
= drdynvc_get_conversation_data(pinfo
);
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
);
371 Len
= (cbIdSpCmd
>> 2) & 0x3;
372 offset
+= dissect_rdp_vlength(tvb
, hf_rdp_drdynvc_length
, offset
, Len
, tree
, &fullPduLen
);
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
);
395 col_set_str(pinfo
->cinfo
, COL_INFO
, "CreateChannel Response");
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
);
406 case DRDYNVC_CAPABILITY_REQUEST_PDU
: {
408 proto_tree_add_item(tree
, hf_rdp_drdynvc_pad
, tvb
, offset
, 1, ENC_NA
);
412 proto_tree_add_item_ret_uint(tree
, hf_rdp_drdynvc_capa_version
, tvb
, offset
, 2, ENC_LITTLE_ENDIAN
, &version
);
415 if (!isServerTarget
) {
416 col_set_str(pinfo
->cinfo
, COL_INFO
, "Capabilities request");
419 proto_tree_add_item(tree
, hf_rdp_drdynvc_capa_prio0
, tvb
, offset
, 2, ENC_LITTLE_ENDIAN
);
421 proto_tree_add_item(tree
, hf_rdp_drdynvc_capa_prio1
, tvb
, offset
, 2, ENC_LITTLE_ENDIAN
);
423 proto_tree_add_item(tree
, hf_rdp_drdynvc_capa_prio2
, tvb
, offset
, 2, ENC_LITTLE_ENDIAN
);
425 proto_tree_add_item(tree
, hf_rdp_drdynvc_capa_prio3
, tvb
, offset
, 2, ENC_LITTLE_ENDIAN
);
429 col_set_str(pinfo
->cinfo
, COL_INFO
, "Capabilities response");
433 case DRDYNVC_DATA_FIRST_PDU
: {
434 col_set_str(pinfo
->cinfo
, COL_INFO
, "Data first");
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
;
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
);
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
));
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
);
489 case DRDYNVC_CHANNEL_RAIL
:
490 call_dissector(rail_handle
, tvb_new_subset_remaining(tvb
, offset
), pinfo
, tree
);
492 case DRDYNVC_CHANNEL_CLIPRDR
:
493 call_dissector(cliprdr_handle
, tvb_new_subset_remaining(tvb
, offset
), pinfo
, tree
);
495 case DRDYNVC_CHANNEL_AUDIOUT
:
496 call_dissector(snd_handle
, tvb_new_subset_remaining(tvb
, offset
), pinfo
, tree
);
498 case DRDYNVC_CHANNEL_AUTH_REDIR
:
499 call_dissector(ear_handle
, tvb_new_subset_remaining(tvb
, offset
), pinfo
, tree
);
502 proto_tree_add_item(tree
, hf_rdp_drdynvc_data
, tvb
, offset
, -1, ENC_NA
);
506 offset
+= payloadLen
;
512 proto_tree_add_item(tree
, hf_rdp_drdynvc_data
, tvb
, offset
, -1, ENC_NA
);
515 case DRDYNVC_DATA_PDU
: {
516 col_set_str(pinfo
->cinfo
, COL_INFO
, "Data");
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
) {
540 printf("num=%d error payload too big\n", pinfo
->num
);
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
;
570 /* single data packet */
571 pduInfo
->reassembled
= false;
572 pduInfo
->decodePayload
= true;
573 pduInfo
->progressStart
= 0;
574 pduInfo
->progressEnd
= payloadLen
;
575 pduInfo
->packetLen
= payloadLen
;
577 pduInfo
->startReassemblyFrame
= pduInfo
->endReassemblyFrame
= pinfo
->num
;
580 pduInfo
= (drdynvc_pdu_info_t
*)wmem_tree_lookup32(drdynvcPinfo
->pdus
, key
);
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
)
590 targetTvb
= pduInfo
->tvb
;
591 add_new_data_source(pinfo
, targetTvb
, "Reassembled DRDYNVC");
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
);
606 case DRDYNVC_CHANNEL_RAIL
:
607 call_dissector(rail_handle
, targetTvb
, pinfo
, tree
);
609 case DRDYNVC_CHANNEL_CLIPRDR
:
610 call_dissector(cliprdr_handle
, targetTvb
, pinfo
, tree
);
612 case DRDYNVC_CHANNEL_AUDIOUT
:
613 call_dissector(snd_handle
, targetTvb
, pinfo
, tree
);
615 case DRDYNVC_CHANNEL_AUTH_REDIR
:
616 call_dissector(ear_handle
, targetTvb
, pinfo
, tree
);
619 proto_tree_add_item(tree
, hf_rdp_drdynvc_data
, targetTvb
, 0, -1, ENC_NA
);
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");
632 case DRDYNVC_DATA_COMPRESSED_PDU
:
633 col_set_str(pinfo
->cinfo
, COL_INFO
, "Data compressed");
635 case DRDYNVC_SOFT_SYNC_REQUEST_PDU
: {
639 col_set_str(pinfo
->cinfo
, COL_INFO
, "SoftSync Request");
642 proto_tree_add_item(tree
, hf_rdp_drdynvc_pad
, tvb
, offset
, 1, ENC_NA
);
645 proto_tree_add_item(tree
, hf_rdp_drdynvc_softsync_req_length
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
);
648 proto_tree_add_item_ret_uint(tree
, hf_rdp_drdynvc_softsync_req_flags
, tvb
, offset
, 2, ENC_LITTLE_ENDIAN
, &flags
);
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
);
655 if (flags
& 0x02) { /* SOFT_SYNC_CHANNEL_LIST_PRESENT */
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
++) {
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
);
672 proto_tree_add_item(channel_tree
, hf_rdp_drdynvc_softsync_req_channel_ndvc
, tvb
, offset
, 2, ENC_LITTLE_ENDIAN
);
675 for (j
= 0; j
< ndvcs
; j
++, offset
+= 4) {
676 proto_tree
*dvc_tree
;
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
);
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
);
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
);
696 case DRDYNVC_SOFT_SYNC_RESPONSE_PDU
: {
697 uint32_t ntunnels
, i
;
699 col_set_str(pinfo
->cinfo
, COL_INFO
, "SoftSync Response");
702 proto_tree_add_item(tree
, hf_rdp_drdynvc_pad
, tvb
, offset
, 1, ENC_NA
);
705 proto_tree_add_item_ret_uint(tree
, hf_rdp_drdynvc_softsync_resp_ntunnels
, tvb
, offset
, 4, ENC_LITTLE_ENDIAN
, &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
);
716 case DRDYNVC_CLOSE_REQUEST_PDU
: {
717 col_set_str(pinfo
->cinfo
, COL_INFO
, "Close request");
719 proto_item_set_generated(
720 proto_tree_add_string_format_value(tree
, hf_rdp_drdynvc_channelName
, tvb
, offset
, 0, NULL
, "%s", channel
->name
)
731 void proto_register_rdp_drdynvc(void) {
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,
739 { &hf_rdp_drdynvc_sp
,
740 { "Sp", "rdp_drdynvc.sp",
741 FT_UINT8
, BASE_HEX
, NULL
, 0xc,
743 { &hf_rdp_drdynvc_pri
,
744 { "Pri", "rdp_drdynvc.pri",
745 FT_UINT8
, BASE_HEX
, VALS(rdp_drdynvc_prio_vals
), 0xc,
747 { &hf_rdp_drdynvc_cmd
,
748 { "PDU type", "rdp_drdynvc.cmd",
749 FT_UINT8
, BASE_HEX
, VALS(rdp_drdynvc_cmd_vals
), 0xf0,
751 { &hf_rdp_drdynvc_capa_version
,
752 { "Version", "rdp_drdynvc.capabilities.version",
753 FT_UINT16
, BASE_DEC
, NULL
, 0,
755 { &hf_rdp_drdynvc_capa_prio0
,
756 { "Priority charge 0", "rdp_drdynvc.capabilities.prioritycharge0",
757 FT_UINT16
, BASE_DEC
, NULL
, 0,
759 { &hf_rdp_drdynvc_capa_prio1
,
760 { "Priority charge 1", "rdp_drdynvc.capabilities.prioritycharge1",
761 FT_UINT16
, BASE_DEC
, NULL
, 0,
763 { &hf_rdp_drdynvc_capa_prio2
,
764 { "Priority charge 2", "rdp_drdynvc.capabilities.prioritycharge2",
765 FT_UINT16
, BASE_DEC
, NULL
, 0,
767 { &hf_rdp_drdynvc_capa_prio3
,
768 { "Priority charge 3", "rdp_drdynvc.capabilities.prioritycharge3",
769 FT_UINT16
, BASE_DEC
, NULL
, 0,
771 { &hf_rdp_drdynvc_pad
,
772 { "Padding", "rdp_drdynvc.pad",
773 FT_UINT8
, BASE_HEX
, NULL
, 0,
775 { &hf_rdp_drdynvc_channelId
,
776 { "Channel Id", "rdp_drdynvc.channelId",
777 FT_UINT32
, BASE_HEX
, NULL
, 0,
779 { &hf_rdp_drdynvc_length
,
780 { "Length", "rdp_drdynvc.length",
781 FT_UINT32
, BASE_DEC
, NULL
, 0,
783 { &hf_rdp_drdynvc_channelName
,
784 { "Channel Name", "rdp_drdynvc.channelName",
785 FT_STRINGZ
, BASE_NONE
, NULL
, 0,
787 { &hf_rdp_drdynvc_creationStatus
,
788 { "Creation status", "rdp_drdynvc.createresponse.status",
789 FT_INT32
, BASE_DEC
, NULL
, 0,
791 { &hf_rdp_drdynvc_softsync_req_length
,
792 { "Length", "rdp_drdynvc.softsyncreq.length",
793 FT_UINT32
, BASE_DEC
, NULL
, 0,
795 { &hf_rdp_drdynvc_softsync_req_flags
,
796 { "Flags", "rdp_drdynvc.softsyncreq.flags",
797 FT_UINT16
, BASE_DEC
, NULL
, 0,
799 { &hf_rdp_drdynvc_softsync_req_ntunnels
,
800 { "NumberOfTunnels", "rdp_drdynvc.softsyncreq.ntunnels",
801 FT_UINT16
, BASE_DEC
, NULL
, 0,
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,
807 { &hf_rdp_drdynvc_softsync_req_channel_ndvc
,
808 { "Number of DVCs", "rdp_drdynvc.softsyncreq.channel.ndvcid",
809 FT_UINT16
, BASE_DEC
, NULL
, 0,
811 { &hf_rdp_drdynvc_softsync_req_channel_dvcid
,
812 { "DVC Id", "rdp_drdynvc.softsyncreq.channel.dvcid",
813 FT_UINT32
, BASE_HEX
, NULL
, 0,
815 { &hf_rdp_drdynvc_softsync_resp_ntunnels
,
816 { "Number of tunnels", "rdp_drdynvc.softsyncresp.ntunnels",
817 FT_UINT32
, BASE_DEC
, NULL
, 0,
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,
823 { &hf_rdp_drdynvc_createresp_channelname
,
824 { "ChannelName", "rdp_drdynvc.createresp",
825 FT_STRINGZ
, BASE_NONE
, NULL
, 0x0,
827 { &hf_rdp_drdynvc_data_progress
,
828 { "DataProgress", "rdp_drdynvc.data_progress",
829 FT_STRINGZ
, BASE_NONE
, NULL
, 0x0,
831 { &hf_rdp_drdynvc_data
,
832 { "Data", "rdp_drdynvc.data",
833 FT_BYTES
, BASE_NONE
, NULL
, 0,
837 /* List of subtrees */
838 static int *ett
[] = {
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
867 * indent-tabs-mode: nil
870 * ex: set shiftwidth=2 tabstop=8 expandtab:
871 * :indentSize=2:tabSize=8:noTabs=true: