diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 20:34:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 20:34:10 +0000 |
commit | e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc (patch) | |
tree | 68cb5ef9081156392f1dd62a00c6ccc1451b93df /epan/dissectors/packet-rdp_drdynvc.c | |
parent | Initial commit. (diff) | |
download | wireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.tar.xz wireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.zip |
Adding upstream version 4.2.2.upstream/4.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'epan/dissectors/packet-rdp_drdynvc.c')
-rw-r--r-- | epan/dissectors/packet-rdp_drdynvc.c | 808 |
1 files changed, 808 insertions, 0 deletions
diff --git a/epan/dissectors/packet-rdp_drdynvc.c b/epan/dissectors/packet-rdp_drdynvc.c new file mode 100644 index 00000000..ddd3ec4b --- /dev/null +++ b/epan/dissectors/packet-rdp_drdynvc.c @@ -0,0 +1,808 @@ +/* Packet-rdp_drdynvc.c + * Routines for Dynamic Virtual channel RDP packet dissection + * Copyright 2021, David Fort + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include <epan/packet.h> +#include <epan/prefs.h> +#include <epan/proto_data.h> +#include <epan/conversation.h> +#include <epan/crc32-tvb.h> +#include "packet-rdp.h" +#include "packet-rdpudp.h" + +void proto_register_rdp_drdynvc(void); +void proto_reg_handoff_drdynvc(void); + +static int proto_rdp_drdynvc = -1; + +static int hf_rdp_drdynvc_cbId = -1; +static int hf_rdp_drdynvc_sp = -1; +static int hf_rdp_drdynvc_pri = -1; +static int hf_rdp_drdynvc_cmd = -1; +static int hf_rdp_drdynvc_capa_version = -1; +static int hf_rdp_drdynvc_capa_prio0 = -1; +static int hf_rdp_drdynvc_capa_prio1 = -1; +static int hf_rdp_drdynvc_capa_prio2 = -1; +static int hf_rdp_drdynvc_capa_prio3 = -1; +static int hf_rdp_drdynvc_channelId = -1; +static int hf_rdp_drdynvc_pad = -1; +static int hf_rdp_drdynvc_channelName = -1; +static int hf_rdp_drdynvc_creationStatus = -1; +static int hf_rdp_drdynvc_createresp_channelname = -1; +static int hf_rdp_drdynvc_length = -1; +static int hf_rdp_drdynvc_softsync_req_length = -1; +static int hf_rdp_drdynvc_softsync_req_flags = -1; +static int hf_rdp_drdynvc_softsync_req_ntunnels = -1; +static int hf_rdp_drdynvc_softsync_req_channel_tunnelType = -1; +static int hf_rdp_drdynvc_softsync_req_channel_ndvc = -1; +static int hf_rdp_drdynvc_softsync_req_channel_dvcid = -1; +static int hf_rdp_drdynvc_softsync_resp_ntunnels = -1; +static int hf_rdp_drdynvc_softsync_resp_tunnel = -1; +static int hf_rdp_drdynvc_data = -1; +static int hf_rdp_drdynvc_data_progress = -1; + + +static int ett_rdp_drdynvc = -1; +static int ett_rdp_drdynvc_softsync_channels = -1; +static int ett_rdp_drdynvc_softsync_channel = -1; +static int ett_rdp_drdynvc_softsync_dvc = -1; + +dissector_handle_t egfx_handle; +dissector_handle_t rail_handle; +dissector_handle_t cliprdr_handle; +dissector_handle_t snd_handle; + +#define PNAME "RDP Dynamic Channel Protocol" +#define PSNAME "DRDYNVC" +#define PFNAME "rdp_drdynvc" + +enum { + DRDYNVC_CREATE_REQUEST_PDU = 0x01, + DRDYNVC_DATA_FIRST_PDU = 0x02, + DRDYNVC_DATA_PDU = 0x03, + DRDYNVC_CLOSE_REQUEST_PDU = 0x04, + DRDYNVC_CAPABILITY_REQUEST_PDU = 0x05, + DRDYNVC_DATA_FIRST_COMPRESSED_PDU = 0x06, + DRDYNVC_DATA_COMPRESSED_PDU = 0x07, + DRDYNVC_SOFT_SYNC_REQUEST_PDU = 0x08, + DRDYNVC_SOFT_SYNC_RESPONSE_PDU = 0x09 +}; + +typedef enum { + DRDYNVC_CHANNEL_UNKNOWN, + DRDYNVC_CHANNEL_EGFX, /* MS-RDPEGX */ + DRDYNVC_CHANNEL_TELEMETRY, /* MS-RDPET */ + DRDYNVC_CHANNEL_AUDIOUT, /* MS-RDPEA */ + DRDYNVC_CHANNEL_AUDIN, /* MS-RDPEAI */ + DRDYNVC_CHANNEL_VIDEO_CTL, /*MS-RDPEVOR */ + DRDYNVC_CHANNEL_VIDEO_DATA, /*MS-RDPEVOR */ + DRDYNVC_CHANNEL_CAM, /* MS-RDPECAM */ + DRDYNVC_CHANNEL_DISPLAY, /* MS-RDPEDISP */ + DRDYNVC_CHANNEL_GEOMETRY,/* MS-RDPEGT */ + DRDYNVC_CHANNEL_MULTITOUCH, /* MS-RDPEI */ + DRDYNVC_CHANNEL_AUTH_REDIR, /* MS-RDPEAR */ + + DRDYNVC_CHANNEL_RAIL, /* MS-RDPERP */ + DRDYNVC_CHANNEL_CLIPRDR, /* MS-RDPECLIP */ +} drdynvc_known_channel_t; + +enum { + DRDYNVC_CHANNEL_PDUS_KEY = 1, +}; + +typedef struct { + guint32 length; + guint8* data; +} drdynvc_pdu_t; + +typedef struct { + drdynvc_pdu_t key; + gboolean fragmented; + guint32 packetLen; + guint32 progress; + tvbuff_t* tvb; +} drdynvc_pdu_info_t; + +typedef struct { + wmem_tree_t *pdus; +} drdynvc_pinfo_t; + +typedef struct { + wmem_array_t *packet; + guint32 packetLen; + guint32 pendingLen; + guint32 startFrame; +} drdynvc_pending_packet_t; + +typedef struct { + drdynvc_known_channel_t type; + char *name; + guint32 channelId; + + drdynvc_pending_packet_t pending_cs; + drdynvc_pending_packet_t pending_sc; +} drdynvc_channel_def_t; + +typedef struct _drdynvc_conv_info_t { + wmem_multimap_t *channels; +} drdynvc_conv_info_t; + + +typedef struct { + const char *name; + const char *shortName; + drdynvc_known_channel_t type; +} drdynvc_know_channel_def; + +static drdynvc_know_channel_def knownChannels[] = { + {"AUDIO_INPUT", "audin", DRDYNVC_CHANNEL_AUDIN}, + {"AUDIO_PLAYBACK_DVC", "audiout", DRDYNVC_CHANNEL_AUDIOUT}, + {"AUDIO_PLAYBACK_LOSSY_DVC", "audiout lossy", DRDYNVC_CHANNEL_AUDIOUT}, + {"RDCamera_Device_Enumerator", "cam", DRDYNVC_CHANNEL_CAM}, + {"Microsoft::Windows::RDS::Video::Control::v08.01", "videoctl", DRDYNVC_CHANNEL_VIDEO_CTL}, + {"Microsoft::Windows::RDS::Video::Data::v08.01", "videodata", DRDYNVC_CHANNEL_VIDEO_DATA}, + {"Microsoft::Windows::RDS::AuthRedirection", "authredir", DRDYNVC_CHANNEL_AUTH_REDIR}, + {"Microsoft::Windows::RDS::Telemetry", "telemetry", DRDYNVC_CHANNEL_TELEMETRY}, + {"Microsoft::Windows::RDS::Graphics", "egfx", DRDYNVC_CHANNEL_EGFX}, + {"Microsoft::Windows::RDS::DisplayControl", "display", DRDYNVC_CHANNEL_DISPLAY}, + {"Microsoft::Windows::RDS::Geometry::v08.01", "geometry", DRDYNVC_CHANNEL_GEOMETRY}, + {"Microsoft::Windows::RDS::Input", "input", DRDYNVC_CHANNEL_MULTITOUCH}, + + /* static channels that can be reopened on the dynamic channel */ + {"rail", "rail", DRDYNVC_CHANNEL_RAIL}, + {"cliprdr", "cliprdr", DRDYNVC_CHANNEL_CLIPRDR}, +}; + +static const value_string drdynvc_tunneltype_vals[] = { + { 0x1, "reliable" }, + { 0x3, "lossy" }, + { 0x0, NULL}, +}; + +static const value_string rdp_drdynvc_cbId_vals[] = { + { 0x0, "1 byte" }, + { 0x1, "2 bytes" }, + { 0x2, "4 bytes" }, + { 0x0, NULL}, +}; + +static const value_string rdp_drdynvc_prio_vals[] = { + { 0x0, "PriorityCharge0" }, + { 0x1, "PriorityCharge1" }, + { 0x2, "PriorityCharge2" }, + { 0x3, "PriorityCharge3" }, + { 0x0, NULL}, +}; + +static const value_string rdp_drdynvc_cmd_vals[] = { + { DRDYNVC_CREATE_REQUEST_PDU, "Create PDU" }, + { DRDYNVC_DATA_FIRST_PDU, "Data first PDU" }, + { DRDYNVC_DATA_PDU, "Data PDU" }, + { DRDYNVC_CLOSE_REQUEST_PDU, "Close PDU" }, + { DRDYNVC_CAPABILITY_REQUEST_PDU, "Capabilities PDU" }, + { DRDYNVC_DATA_FIRST_COMPRESSED_PDU, "Data first compressed PDU" }, + { DRDYNVC_DATA_COMPRESSED_PDU, "Data compressed PDU" }, + { DRDYNVC_SOFT_SYNC_REQUEST_PDU,"Soft-Sync request PDU" }, + { DRDYNVC_SOFT_SYNC_RESPONSE_PDU,"Soft-Sync response PDU" }, + { 0x0, NULL}, +}; + +static drdynvc_known_channel_t +drdynvc_find_channel_type(const char *name) +{ + guint i; + + for (i = 0; i < array_length(knownChannels); i++) + { + if (strcmp(knownChannels[i].name, name) == 0) + return knownChannels[i].type; + } + return DRDYNVC_CHANNEL_UNKNOWN; +} + +static drdynvc_conv_info_t * +drdynvc_get_conversation_data(packet_info *pinfo) +{ + conversation_t *conversation, *conversation_tcp; + drdynvc_conv_info_t *info; + + conversation = find_or_create_conversation(pinfo); + + info = (drdynvc_conv_info_t *)conversation_get_proto_data(conversation, proto_rdp_drdynvc); + if (!info) { + conversation_tcp = rdp_find_tcp_conversation_from_udp(conversation); + if (conversation_tcp) + info = (drdynvc_conv_info_t *)conversation_get_proto_data(conversation_tcp, proto_rdp_drdynvc); + } + + if (info == NULL) { + info = wmem_new0(wmem_file_scope(), drdynvc_conv_info_t); + info->channels = wmem_multimap_new(wmem_file_scope(), g_direct_hash, g_direct_equal); + conversation_add_proto_data(conversation, proto_rdp_drdynvc, info); + } + + return info; +} + + +static int +dissect_rdp_vlength(tvbuff_t *tvb, int hf_index, int offset, guint8 vlen, proto_tree *tree, guint32 *ret) +{ + int len; + guint32 value; + + switch (vlen) { + case 0: + value = tvb_get_guint8(tvb, offset); + len = 1; + break; + case 1: + value = tvb_get_guint16(tvb, offset, ENC_LITTLE_ENDIAN); + len = 2; + break; + case 2: + value = tvb_get_guint32(tvb, offset, ENC_LITTLE_ENDIAN); + len = 4; + break; + default: + if (ret) + *ret = 0; + return 0; + } + + proto_tree_add_uint(tree, hf_index, tvb, offset, len, value); + if (ret) + *ret = value; + return len; +} + +static const char * +find_channel_name_by_id(packet_info *pinfo, drdynvc_conv_info_t *dyninfo, guint32 dvcId) { + drdynvc_channel_def_t *dynChannel = wmem_multimap_lookup32_le(dyninfo->channels, GUINT_TO_POINTER(dvcId), pinfo->num); + if (dynChannel) + return dynChannel->name; + + return NULL; +} + +static drdynvc_pinfo_t *getDrDynPacketInfo(packet_info *pinfo) +{ + drdynvc_pinfo_t *ret = p_get_proto_data(wmem_file_scope(), pinfo, proto_rdp_drdynvc, DRDYNVC_CHANNEL_PDUS_KEY); + if (ret) + return ret; + + ret = wmem_alloc(wmem_file_scope(), sizeof(*ret)); + ret->pdus = wmem_tree_new(wmem_file_scope()); + + p_set_proto_data(wmem_file_scope(), pinfo, proto_rdp_drdynvc, DRDYNVC_CHANNEL_PDUS_KEY, ret); + return ret; +} + +static int +dissect_rdp_drdynvc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree, void *data _U_) +{ + proto_item *item; + proto_tree *tree; + gint offset = 0; + guint8 cbIdSpCmd, cmdId; + guint8 cbId, Len; + gboolean haveChannelId, havePri, haveLen; + gboolean isServerTarget = rdp_isServerAddressTarget(pinfo); + guint32 channelId = 0; + guint32 pduLen = 0; + drdynvc_conv_info_t *info; + + col_set_str(pinfo->cinfo, COL_PROTOCOL, "DRDYNVC"); + col_clear(pinfo->cinfo, COL_INFO); + + parent_tree = proto_tree_get_root(parent_tree); + item = proto_tree_add_item(parent_tree, proto_rdp_drdynvc, tvb, 0, -1, ENC_NA); + tree = proto_item_add_subtree(item, ett_rdp_drdynvc); + + cbIdSpCmd = tvb_get_guint8(tvb, offset); + cmdId = (cbIdSpCmd >> 4) & 0xf; + cbId = (cbIdSpCmd & 0x3); + + haveChannelId = true; + havePri = false; + haveLen = false; + switch (cmdId) { + case DRDYNVC_CREATE_REQUEST_PDU: + havePri = true; + break; + case DRDYNVC_DATA_FIRST_PDU: + haveLen = true; + break; + case DRDYNVC_DATA_FIRST_COMPRESSED_PDU: + haveLen = true; + break; + case DRDYNVC_CAPABILITY_REQUEST_PDU: + case DRDYNVC_SOFT_SYNC_REQUEST_PDU: + case DRDYNVC_SOFT_SYNC_RESPONSE_PDU: + haveChannelId = false; + break; + default: + break; + } + + proto_tree_add_item(tree, hf_rdp_drdynvc_cbId, tvb, offset, 1, ENC_NA); + if (havePri) + proto_tree_add_item(tree, hf_rdp_drdynvc_pri, tvb, offset, 1, ENC_NA); + else + proto_tree_add_item(tree, hf_rdp_drdynvc_sp, tvb, offset, 1, ENC_NA); + proto_tree_add_item(tree, hf_rdp_drdynvc_cmd, tvb, offset, 1, ENC_NA); + + offset++; + if (haveChannelId) + offset += dissect_rdp_vlength(tvb, hf_rdp_drdynvc_channelId, offset, cbId, tree, &channelId); + + if (haveLen) { + Len = (cbIdSpCmd >> 2) & 0x3; + offset += dissect_rdp_vlength(tvb, hf_rdp_drdynvc_length, offset, Len, tree, &pduLen); + } + + info = drdynvc_get_conversation_data(pinfo); + switch (cmdId) { + case DRDYNVC_CREATE_REQUEST_PDU: + if (!isServerTarget) { + guint nameLen = tvb_strsize(tvb, offset); + + col_set_str(pinfo->cinfo, COL_INFO, "CreateChannel Request"); + proto_tree_add_item(tree, hf_rdp_drdynvc_channelName, tvb, offset, -1, ENC_ASCII); + + drdynvc_channel_def_t *channel; + if (!PINFO_FD_VISITED(pinfo)) { + channel = wmem_alloc(wmem_file_scope(), sizeof(*channel)); + channel->channelId = channelId; + channel->name = tvb_get_string_enc(wmem_file_scope(), tvb, offset, nameLen, ENC_ASCII); + channel->type = drdynvc_find_channel_type(channel->name); + channel->pending_cs.pendingLen = 0; + channel->pending_cs.packet = NULL; + channel->pending_cs.startFrame = pinfo->num; + channel->pending_sc.pendingLen = 0; + channel->pending_sc.packet = NULL; + channel->pending_sc.startFrame = pinfo->num; + //printf("dynamic %s -> 0x%x\n", channel->name, channel->channelId); + + wmem_multimap_insert32(info->channels, GUINT_TO_POINTER(channelId), pinfo->num, channel); + } + + } else { + drdynvc_channel_def_t *channel = wmem_multimap_lookup32_le(info->channels, GUINT_TO_POINTER(channelId), pinfo->num); + + col_set_str(pinfo->cinfo, COL_INFO, "CreateChannel Response"); + + if (channel) { + proto_item *channelName = proto_tree_add_string_format_value(tree, hf_rdp_drdynvc_createresp_channelname, tvb, offset, 0, NULL, "%s", channel->name); + proto_item_set_generated(channelName); + } + proto_tree_add_item(tree, hf_rdp_drdynvc_creationStatus, tvb, offset, 4, ENC_NA); + + } + break; + case DRDYNVC_CAPABILITY_REQUEST_PDU: { + /* Pad */ + proto_tree_add_item(tree, hf_rdp_drdynvc_pad, tvb, offset, 1, ENC_NA); + offset++; + + guint32 version; + proto_tree_add_item_ret_uint(tree, hf_rdp_drdynvc_capa_version, tvb, offset, 2, ENC_LITTLE_ENDIAN, &version); + offset += 2; + + if (!isServerTarget) { + col_set_str(pinfo->cinfo, COL_INFO, "Capabilities request"); + + if (version > 1) { + proto_tree_add_item(tree, hf_rdp_drdynvc_capa_prio0, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + proto_tree_add_item(tree, hf_rdp_drdynvc_capa_prio1, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + proto_tree_add_item(tree, hf_rdp_drdynvc_capa_prio2, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + proto_tree_add_item(tree, hf_rdp_drdynvc_capa_prio3, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + } + } else { + col_set_str(pinfo->cinfo, COL_INFO, "Capabilities response"); + } + break; + } + case DRDYNVC_DATA_FIRST_PDU: { + col_set_str(pinfo->cinfo, COL_INFO, "Data first"); + + drdynvc_channel_def_t *channel = wmem_multimap_lookup32_le(info->channels, GUINT_TO_POINTER(channelId), pinfo->num); + if (channel) { + drdynvc_pending_packet_t *pendingPacket = isServerTarget ? &channel->pending_cs : &channel->pending_sc; + gint payloadLen = tvb_reported_length_remaining(tvb, offset); + proto_item *channelName = proto_tree_add_string_format_value(tree, hf_rdp_drdynvc_createresp_channelname, tvb, offset, 0, NULL, "%s", channel->name); + proto_item_set_generated(channelName); + + proto_item_set_generated( + proto_tree_add_string_format_value(tree, hf_rdp_drdynvc_data_progress, tvb, offset, 0, NULL, "0-%d/%d", payloadLen, pduLen) + ); + + pendingPacket->packetLen = pduLen; + pendingPacket->pendingLen = pduLen - payloadLen; + pendingPacket->startFrame = pinfo->num; + + if (!pendingPacket->pendingLen) { + switch (channel->type) { + case DRDYNVC_CHANNEL_EGFX: + call_dissector(egfx_handle, tvb_new_subset_remaining(tvb, offset), pinfo, tree); + break; + case DRDYNVC_CHANNEL_RAIL: + call_dissector(rail_handle, tvb_new_subset_remaining(tvb, offset), pinfo, tree); + break; + case DRDYNVC_CHANNEL_CLIPRDR: + call_dissector(cliprdr_handle, tvb_new_subset_remaining(tvb, offset), pinfo, tree); + break; + case DRDYNVC_CHANNEL_AUDIOUT: + call_dissector(snd_handle, tvb_new_subset_remaining(tvb, offset), pinfo, tree); + break; + default: + proto_tree_add_item(tree, hf_rdp_drdynvc_data, tvb, offset, -1, ENC_NA); + break; + } + + offset += payloadLen; + return offset; + } else { + pendingPacket->packet = wmem_array_sized_new(wmem_file_scope(), 1, pduLen); + wmem_array_append(pendingPacket->packet, tvb_get_ptr(tvb, offset, -1), payloadLen); + } + } + + proto_tree_add_item(tree, hf_rdp_drdynvc_data, tvb, offset, -1, ENC_NA); + break; + } + case DRDYNVC_DATA_PDU: { + col_set_str(pinfo->cinfo, COL_INFO, "Data"); + + drdynvc_channel_def_t *channel = wmem_multimap_lookup32_le(info->channels, GUINT_TO_POINTER(channelId), pinfo->num); + if (channel) { + tvbuff_t *targetTvb = NULL; + gboolean fragmented = FALSE; + + proto_item *channelName = proto_tree_add_string_format_value(tree, hf_rdp_drdynvc_createresp_channelname, tvb, offset, 0, NULL, "%s", channel->name); + proto_item_set_generated(channelName); + + drdynvc_pinfo_t *drdynvcPinfo = getDrDynPacketInfo(pinfo); + drdynvc_pdu_info_t *pduInfo; + gint payloadLen = tvb_reported_length_remaining(tvb, offset); + guint32 key = crc32_ccitt_tvb_offset(tvb, offset, payloadLen); + + if (!PINFO_FD_VISITED(pinfo)) { + drdynvc_pending_packet_t *pendingPacket = isServerTarget ? &channel->pending_cs : &channel->pending_sc; + + /*printf("num=%d payloadLen=%d pendingLen=%d/%d\n", pinfo->num, payloadLen, + pendingPacket->pendingLen, pendingPacket->packetLen);*/ + if (pendingPacket->pendingLen) { + if ((guint32)payloadLen > pendingPacket->pendingLen) { + // TODO: error + /*printf("num=%d error\n", pinfo->num);*/ + return offset; + } + + pendingPacket->pendingLen -= payloadLen; + wmem_array_append(pendingPacket->packet, tvb_get_ptr(tvb, offset, -1), payloadLen); + fragmented = TRUE; + } + + if (!pendingPacket->pendingLen) { + pendingPacket->packetLen = 0; + + if (!fragmented) { + targetTvb = tvb_new_subset_remaining(tvb, offset); + } else { + gint reassembled_len = wmem_array_get_count(pendingPacket->packet); + targetTvb = tvb_new_real_data(wmem_array_get_raw(pendingPacket->packet), reassembled_len, reassembled_len); + } + } + + pduInfo = wmem_alloc(wmem_file_scope(), sizeof(*pduInfo)); + pduInfo->fragmented = fragmented; + pduInfo->packetLen = pendingPacket->packetLen ? pendingPacket->packetLen : (guint32)payloadLen; + pduInfo->progress = pendingPacket->packetLen - pendingPacket->pendingLen; + pduInfo->tvb = fragmented ? targetTvb : NULL; + + wmem_tree_insert32(drdynvcPinfo->pdus, key, pduInfo); + } else { + pduInfo = (drdynvc_pdu_info_t*)wmem_tree_lookup32(drdynvcPinfo->pdus, key); + } + + if (pduInfo) { + proto_item_set_generated( + proto_tree_add_string_format_value(tree, hf_rdp_drdynvc_data_progress, tvb, offset, 0, NULL, "%d-%d/%d", + pduInfo->progress, pduInfo->progress + payloadLen, pduInfo->packetLen) + ); + } + + if (!pduInfo->fragmented) + targetTvb = tvb_new_subset_remaining(tvb, offset); + else + targetTvb = pduInfo->tvb; + + if(targetTvb) { + if (pduInfo->fragmented) + add_new_data_source(pinfo, targetTvb, "Reassembled DRDYNVC"); + + switch (channel->type) { + case DRDYNVC_CHANNEL_EGFX: + call_dissector(egfx_handle, targetTvb, pinfo, tree); + break; + case DRDYNVC_CHANNEL_RAIL: + call_dissector(rail_handle, targetTvb, pinfo, tree); + break; + case DRDYNVC_CHANNEL_CLIPRDR: + call_dissector(cliprdr_handle, targetTvb, pinfo, tree); + break; + case DRDYNVC_CHANNEL_AUDIOUT: + call_dissector(snd_handle, targetTvb, pinfo, tree); + break; + default: + proto_tree_add_item(tree, hf_rdp_drdynvc_data, targetTvb, offset, -1, ENC_NA); + break; + } + return tvb_reported_length(tvb); + } + } + + proto_tree_add_item(tree, hf_rdp_drdynvc_data, tvb, offset, -1, ENC_NA); + return tvb_reported_length(tvb); + } + case DRDYNVC_DATA_FIRST_COMPRESSED_PDU: + col_set_str(pinfo->cinfo, COL_INFO, "Data compressed first"); + break; + case DRDYNVC_DATA_COMPRESSED_PDU: + col_set_str(pinfo->cinfo, COL_INFO, "Data compressed"); + break; + case DRDYNVC_SOFT_SYNC_REQUEST_PDU: { + guint32 ntunnels; + guint32 flags; + + col_set_str(pinfo->cinfo, COL_INFO, "SoftSync Request"); + + /* Pad */ + proto_tree_add_item(tree, hf_rdp_drdynvc_pad, tvb, offset, 1, ENC_NA); + offset++; + + proto_tree_add_item(tree, hf_rdp_drdynvc_softsync_req_length, tvb, offset, 4, ENC_LITTLE_ENDIAN); + offset += 4; + + proto_tree_add_item_ret_uint(tree, hf_rdp_drdynvc_softsync_req_flags, tvb, offset, 2, ENC_LITTLE_ENDIAN, &flags); + offset += 2; + // XXX: TODO should decode flags but they are always set to SOFT_SYNC_TCP_FLUSHED|SOFT_SYNC_CHANNEL_LIST_PRESENT + + proto_tree_add_item_ret_uint(tree, hf_rdp_drdynvc_softsync_req_ntunnels, tvb, offset, 2, ENC_LITTLE_ENDIAN, &ntunnels); + offset += 2; + + if (flags & 0x02) { /* SOFT_SYNC_CHANNEL_LIST_PRESENT */ + guint16 i; + proto_tree *tunnels_tree = proto_tree_add_subtree(tree, tvb, offset, -1, ett_rdp_drdynvc_softsync_channels, NULL, "Channels"); + + for (i = 0; i < ntunnels; i++) { + guint16 j; + guint32 tunnelType = tvb_get_guint32(tvb, offset, ENC_LITTLE_ENDIAN); + guint16 ndvcs = tvb_get_guint16(tvb, offset + 4, ENC_LITTLE_ENDIAN); + gint channelSz = 4 + 2 + (ndvcs * 4); + proto_tree *channel_tree; + const char *label = (tunnelType == 0x1) ? "Reliable channels" : "Lossy channels"; + + channel_tree = proto_tree_add_subtree(tunnels_tree, tvb, offset, channelSz, ett_rdp_drdynvc_softsync_channel, NULL, label); + + proto_tree_add_item(channel_tree, hf_rdp_drdynvc_softsync_req_channel_tunnelType, tvb, offset, 4, ENC_LITTLE_ENDIAN); + offset += 4; + + proto_tree_add_item(channel_tree, hf_rdp_drdynvc_softsync_req_channel_ndvc, tvb, offset, 2, ENC_LITTLE_ENDIAN); + offset += 2; + + for (j = 0; j < ndvcs; j++, offset += 4) { + proto_tree *dvc_tree; + guint32 dvcId; + const char *showLabel; + + dvcId = tvb_get_guint32(tvb, offset, ENC_LITTLE_ENDIAN); + showLabel = label = find_channel_name_by_id(pinfo, info, dvcId); + if (!label) + showLabel = "DVC"; + dvc_tree = proto_tree_add_subtree(channel_tree, tvb, offset, 4, ett_rdp_drdynvc_softsync_dvc, NULL, showLabel); + proto_tree_add_item(dvc_tree, hf_rdp_drdynvc_softsync_req_channel_dvcid, tvb, offset, 4, ENC_LITTLE_ENDIAN); + + if (label) { + proto_item *pi = proto_tree_add_string_format(dvc_tree, hf_rdp_drdynvc_channelName, tvb, offset, 4, label, "%s", label); + proto_item_set_generated(pi); + } + } + } + } + break; + } + case DRDYNVC_SOFT_SYNC_RESPONSE_PDU: { + guint32 ntunnels, i; + + col_set_str(pinfo->cinfo, COL_INFO, "SoftSync Response"); + + /* Pad */ + proto_tree_add_item(tree, hf_rdp_drdynvc_pad, tvb, offset, 1, ENC_NA); + offset++; + + proto_tree_add_item_ret_uint(tree, hf_rdp_drdynvc_softsync_resp_ntunnels, tvb, offset, 4, ENC_LITTLE_ENDIAN, &ntunnels); + offset += 4; + + if (ntunnels) { + proto_tree *tunnels_tree = proto_tree_add_subtree(tree, tvb, offset, 4, ett_rdp_drdynvc_softsync_dvc, NULL, "TunnelsToSwitch"); + for (i = 0; i < ntunnels; i++, offset += 4) { + proto_tree_add_item(tunnels_tree, hf_rdp_drdynvc_softsync_resp_tunnel, tvb, offset, 4, ENC_LITTLE_ENDIAN); + } + } + break; + } + case DRDYNVC_CLOSE_REQUEST_PDU: { + drdynvc_channel_def_t *channel = wmem_multimap_lookup32_le(info->channels, GUINT_TO_POINTER(channelId), pinfo->num); + + col_set_str(pinfo->cinfo, COL_INFO, "Close request"); + if (channel) { + proto_item *channelName = proto_tree_add_string_format_value(tree, hf_rdp_drdynvc_channelName, tvb, offset, 0, NULL, "%s", channel->name); + proto_item_set_generated(channelName); + } + break; + } + default: + break; + } + return offset; +} + +void proto_register_rdp_drdynvc(void) { + + /* List of fields */ + static hf_register_info hf[] = { + { &hf_rdp_drdynvc_cbId, + { "ChannelId length", "rdp_drdynvc.cbid", + FT_UINT8, BASE_HEX, VALS(rdp_drdynvc_cbId_vals), 0x3, + NULL, HFILL }}, + { &hf_rdp_drdynvc_sp, + { "Sp", "rdp_drdynvc.sp", + FT_UINT8, BASE_HEX, NULL, 0xc, + NULL, HFILL }}, + { &hf_rdp_drdynvc_pri, + { "Pri", "rdp_drdynvc.pri", + FT_UINT8, BASE_HEX, VALS(rdp_drdynvc_prio_vals), 0xc, + NULL, HFILL }}, + { &hf_rdp_drdynvc_cmd, + { "PDU type", "rdp_drdynvc.cmd", + FT_UINT8, BASE_HEX, VALS(rdp_drdynvc_cmd_vals), 0xf0, + NULL, HFILL }}, + { &hf_rdp_drdynvc_capa_version, + { "Version", "rdp_drdynvc.capabilities.version", + FT_UINT16, BASE_DEC, NULL, 0, + NULL, HFILL }}, + { &hf_rdp_drdynvc_capa_prio0, + { "Priority charge 0", "rdp_drdynvc.capabilities.prioritycharge0", + FT_UINT16, BASE_DEC, NULL, 0, + NULL, HFILL }}, + { &hf_rdp_drdynvc_capa_prio1, + { "Priority charge 1", "rdp_drdynvc.capabilities.prioritycharge1", + FT_UINT16, BASE_DEC, NULL, 0, + NULL, HFILL }}, + { &hf_rdp_drdynvc_capa_prio2, + { "Priority charge 2", "rdp_drdynvc.capabilities.prioritycharge2", + FT_UINT16, BASE_DEC, NULL, 0, + NULL, HFILL }}, + { &hf_rdp_drdynvc_capa_prio3, + { "Priority charge 3", "rdp_drdynvc.capabilities.prioritycharge3", + FT_UINT16, BASE_DEC, NULL, 0, + NULL, HFILL }}, + { &hf_rdp_drdynvc_pad, + { "Padding", "rdp_drdynvc.pad", + FT_UINT8, BASE_HEX, NULL, 0, + NULL, HFILL }}, + { &hf_rdp_drdynvc_channelId, + { "Channel Id", "rdp_drdynvc.channelId", + FT_UINT32, BASE_HEX, NULL, 0, + NULL, HFILL }}, + { &hf_rdp_drdynvc_length, + { "Length", "rdp_drdynvc.length", + FT_UINT32, BASE_DEC, NULL, 0, + NULL, HFILL }}, + { &hf_rdp_drdynvc_channelName, + { "Channel Name", "rdp_drdynvc.channelName", + FT_STRINGZ, BASE_NONE, NULL, 0, + NULL, HFILL }}, + { &hf_rdp_drdynvc_creationStatus, + { "Creation status", "rdp_drdynvc.createresponse.status", + FT_INT32, BASE_DEC, NULL, 0, + NULL, HFILL }}, + { &hf_rdp_drdynvc_softsync_req_length, + { "Length", "rdp_drdynvc.softsyncreq.length", + FT_UINT32, BASE_DEC, NULL, 0, + NULL, HFILL }}, + { &hf_rdp_drdynvc_softsync_req_flags, + { "Flags", "rdp_drdynvc.softsyncreq.flags", + FT_UINT16, BASE_DEC, NULL, 0, + NULL, HFILL }}, + { &hf_rdp_drdynvc_softsync_req_ntunnels, + { "NumberOfTunnels", "rdp_drdynvc.softsyncreq.ntunnels", + FT_UINT16, BASE_DEC, NULL, 0, + NULL, HFILL }}, + { &hf_rdp_drdynvc_softsync_req_channel_tunnelType, + { "Tunnel type", "rdp_drdynvc.softsyncreq.channel.tunnelType", + FT_UINT32, BASE_HEX, VALS(drdynvc_tunneltype_vals), 0, + NULL, HFILL }}, + { &hf_rdp_drdynvc_softsync_req_channel_ndvc, + { "Number of DVCs", "rdp_drdynvc.softsyncreq.channel.ndvcid", + FT_UINT16, BASE_DEC, NULL, 0, + NULL, HFILL }}, + { &hf_rdp_drdynvc_softsync_req_channel_dvcid, + { "DVC Id", "rdp_drdynvc.softsyncreq.channel.dvcid", + FT_UINT32, BASE_HEX, NULL, 0, + NULL, HFILL }}, + { &hf_rdp_drdynvc_softsync_resp_ntunnels, + { "Number of tunnels", "rdp_drdynvc.softsyncresp.ntunnels", + FT_UINT32, BASE_DEC, NULL, 0, + NULL, HFILL }}, + { &hf_rdp_drdynvc_softsync_resp_tunnel, + { "Number of tunnels", "rdp_drdynvc.softsyncresp.tunnel", + FT_UINT32, BASE_DEC, VALS(drdynvc_tunneltype_vals), 0, + NULL, HFILL }}, + { &hf_rdp_drdynvc_createresp_channelname, + { "ChannelName", "rdp_drdynvc.createresp", + FT_STRINGZ, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + { &hf_rdp_drdynvc_data_progress, + { "DataProgress", "rdp_drdynvc.data_progress", + FT_STRINGZ, BASE_NONE, NULL, 0x0, + NULL, HFILL }}, + { &hf_rdp_drdynvc_data, + { "Data", "rdp_drdynvc.data", + FT_BYTES, BASE_NONE, NULL, 0, + NULL, HFILL }}, + }; + + /* List of subtrees */ + static gint *ett[] = { + &ett_rdp_drdynvc, + &ett_rdp_drdynvc_softsync_channels, + &ett_rdp_drdynvc_softsync_channel, + &ett_rdp_drdynvc_softsync_dvc + }; + //module_t *drdynvc_module; + + proto_rdp_drdynvc = proto_register_protocol(PNAME, PSNAME, PFNAME); + /* Register fields and subtrees */ + proto_register_field_array(proto_rdp_drdynvc, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + + /*drdynvc_module = prefs_register_protocol(proto_rdp_drdynvc, NULL);*/ + + register_dissector("rdp_drdynvc", dissect_rdp_drdynvc, proto_rdp_drdynvc); +} + +void proto_reg_handoff_drdynvc(void) { + egfx_handle = find_dissector("rdp_egfx"); + rail_handle = find_dissector("rdp_rail"); + cliprdr_handle = find_dissector("rdp_cliprdr"); + snd_handle = find_dissector("rdp_snd"); +} + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local Variables: + * c-basic-offset: 2 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * ex: set shiftwidth=2 tabstop=8 expandtab: + * :indentSize=2:tabSize=8:noTabs=true: + */ |