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-rtp.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-rtp.c')
-rw-r--r-- | epan/dissectors/packet-rtp.c | 3696 |
1 files changed, 3696 insertions, 0 deletions
diff --git a/epan/dissectors/packet-rtp.c b/epan/dissectors/packet-rtp.c new file mode 100644 index 00000000..9f7ecea1 --- /dev/null +++ b/epan/dissectors/packet-rtp.c @@ -0,0 +1,3696 @@ +/* packet-rtp.c + * + * Routines for RTP dissection + * RTP = Real time Transport Protocol + * + * Copyright 2000, Philips Electronics N.V. + * Written by Andreas Sikkema <h323@ramdyne.nl> + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +/* + * This dissector tries to dissect the RTP protocol according to Annex A + * of ITU-T Recommendation H.225.0 (02/98) or RFC 3550 (obsoleting 1889). + * + * RTP traffic is traditionally handled by an even UDP portnumber. This can + * be any port number, but there is a registered port available, port 5004 + * See Annex B of ITU-T Recommendation H.225.0, section B.7 + * + * Note that nowadays RTP and RTCP are often multiplexed onto a single port, + * per RFC 5671. + * + * This doesn't dissect older versions of RTP, such as: + * + * the vat protocol ("version 0") - see + * + * ftp://ftp.ee.lbl.gov/conferencing/vat/alpha-test/vatsrc-4.0b2.tar.gz + * + * and look in "session-vat.cc" if you want to write a dissector + * (have fun - there aren't any nice header files showing the packet + * format); + * + * version 1, as documented in + * + * ftp://gaia.cs.umass.edu/pub/hgschulz/rtp/draft-ietf-avt-rtp-04.txt + * + * It also dissects PacketCable CCC-encapsulated RTP data, as described in + * chapter 5 of the PacketCable Electronic Surveillance Specification: + * + * http://www.packetcable.com/downloads/specs/PKT-SP-ESP1.5-I01-050128.pdf + */ + + +#include "config.h" + +#include <epan/packet.h> +#include <epan/exceptions.h> +#include <epan/show_exception.h> +#include <epan/expert.h> +#include <epan/proto_data.h> +#include <epan/decode_as.h> + +#include "packet-rtp.h" +#include "packet-rtcp.h" +#include "packet-tcp.h" + +#include <epan/rtp_pt.h> +#include <epan/tap.h> +#include <epan/prefs.h> + +/* un-comment the following as well as this line in conversation.c, to enable debug printing */ +/* #define DEBUG_CONVERSATION */ +#include "conversation_debug.h" + +/* uncomment this to enable debugging of fragment reassembly */ +/* #define DEBUG 1 */ +/* #define DEBUG_FRAGMENTS 1 */ + +typedef struct _rfc2198_hdr { + unsigned int pt; + int offset; + int len; + const char* payload_type_str; + int payload_rate; + unsigned payload_channels; + wmem_map_t *payload_fmtp_map; + struct _rfc2198_hdr *next; +} rfc2198_hdr; + +/* we have one of these for each pdu which spans more than one segment + */ +typedef struct _rtp_multisegment_pdu { + /* the seqno of the segment where the pdu starts */ + guint32 startseq; + + /* the seqno of the segment where the pdu ends */ + guint32 endseq; +} rtp_multisegment_pdu; + +typedef struct _rtp_private_conv_info { + /* This tree is indexed by sequence number and keeps track of all + * all pdus spanning multiple segments for this flow. + */ + wmem_tree_t *multisegment_pdus; +} rtp_private_conv_info; + +typedef struct _rtp_number_space { + + uint32_t extended_seqno; + uint64_t extended_timestamp; +} rtp_number_space; + +/** Info to save in RTP conversation */ +struct _rtp_conversation_info +{ + gchar method[MAX_RTP_SETUP_METHOD_SIZE + 1]; + guint32 frame_number; /**> the frame where this conversation is started */ + guint32 media_types; + rtp_dyn_payload_t* rtp_dyn_payload; /**> the dynamic RTP payload info - see comments above */ + + wmem_map_t* ssrc_number_space; /**> maps the SSRCs to the last seen seqno and timestamp + * for that SSRC in the conversation */ + struct _rtp_private_conv_info* rtp_conv_info; /**> conversation info private + * to the rtp dissector + */ + struct srtp_info* srtp_info; /* SRTP context */ + bta2dp_codec_info_t* bta2dp_info; + btvdp_codec_info_t* btvdp_info; + wmem_array_t* rtp_sdp_setup_info_list; /**> List with data from all SDP occurencies for this steram holding a call ID)*/ +}; + +typedef struct { + char *encoding_name; + int sample_rate; + unsigned channels; + wmem_map_t *fmtp_map; +} encoding_name_and_rate_t; + +struct _rtp_dyn_payload_t +{ + GHashTable *table; + size_t ref_count; +}; + +static reassembly_table rtp_reassembly_table; + +static int hf_rtp_fragments = -1; +static int hf_rtp_fragment = -1; +static int hf_rtp_fragment_overlap = -1; +static int hf_rtp_fragment_overlap_conflict = -1; +static int hf_rtp_fragment_multiple_tails = -1; +static int hf_rtp_fragment_too_long_fragment = -1; +static int hf_rtp_fragment_error = -1; +static int hf_rtp_fragment_count = -1; +static int hf_rtp_reassembled_in = -1; +static int hf_rtp_reassembled_length = -1; + +static gint ett_rtp_fragment = -1; +static gint ett_rtp_fragments = -1; + +static const fragment_items rtp_fragment_items = { + &ett_rtp_fragment, + &ett_rtp_fragments, + &hf_rtp_fragments, + &hf_rtp_fragment, + &hf_rtp_fragment_overlap, + &hf_rtp_fragment_overlap_conflict, + &hf_rtp_fragment_multiple_tails, + &hf_rtp_fragment_too_long_fragment, + &hf_rtp_fragment_error, + &hf_rtp_fragment_count, + &hf_rtp_reassembled_in, + &hf_rtp_reassembled_length, + /* Reassembled data field */ + NULL, + "RTP fragments" +}; + +static dissector_handle_t rtp_handle; +static dissector_handle_t rtp_rfc4571_handle; +static dissector_handle_t rtcp_handle; +static dissector_handle_t classicstun_handle; +static dissector_handle_t stun_handle; +static dissector_handle_t classicstun_heur_handle; +static dissector_handle_t stun_heur_handle; +static dissector_handle_t t38_handle; +static dissector_handle_t zrtp_handle; +static dissector_handle_t dtls_handle; +static dissector_handle_t rtp_rfc2198_handle; + +static dissector_handle_t sprt_handle; +static dissector_handle_t v150fw_handle; + +static dissector_handle_t bta2dp_content_protection_header_scms_t; +static dissector_handle_t btvdp_content_protection_header_scms_t; +static dissector_handle_t bta2dp_handle; +static dissector_handle_t btvdp_handle; +static dissector_handle_t sbc_handle; + +static int rtp_tap = -1; + +static dissector_table_t rtp_pt_dissector_table; +static dissector_table_t rtp_dyn_pt_dissector_table; + +static dissector_table_t rtp_hdr_ext_dissector_table; +static dissector_table_t rtp_hdr_ext_rfc5285_dissector_table; + +/* Used for storing data to be retreived by the SDP dissector*/ +static int proto_sdp = -1; + +/* RTP header fields */ +static int proto_rtp = -1; +static int proto_rtp_rfc2198 = -1; +static int hf_rtp_version = -1; +static int hf_rtp_padding = -1; +static int hf_rtp_extension = -1; +static int hf_rtp_csrc_count = -1; +static int hf_rtp_marker = -1; +static int hf_rtp_payload_type = -1; +static int hf_rtp_seq_nr = -1; +static int hf_rtp_ext_seq_nr = -1; +static int hf_rtp_timestamp = -1; +static int hf_rtp_ssrc = -1; +static int hf_rtp_csrc_items = -1; +static int hf_rtp_csrc_item = -1; +static int hf_rtp_data = -1; +static int hf_rtp_padding_data = -1; +static int hf_rtp_padding_count= -1; +static int hf_rtp_rfc2198_follow= -1; +static int hf_rtp_rfc2198_tm_off= -1; +static int hf_rtp_rfc2198_bl_len= -1; + +/* RTP header extension fields */ +static int hf_rtp_prof_define = -1; +static int hf_rtp_length = -1; +static int hf_rtp_hdr_exts = -1; +static int hf_rtp_hdr_ext = -1; + +/* RTP setup fields */ +static int hf_rtp_setup = -1; +static int hf_rtp_setup_frame = -1; +static int hf_rtp_setup_method = -1; + +/* RTP fields defining a sub tree */ +static gint ett_rtp = -1; +static gint ett_csrc_list = -1; +static gint ett_hdr_ext = -1; +static gint ett_hdr_ext_rfc5285 = -1; +static gint ett_rtp_setup = -1; +static gint ett_rtp_rfc2198 = -1; +static gint ett_rtp_rfc2198_hdr = -1; + +/* SRTP fields */ +static int hf_srtp_encrypted_payload = -1; +/* static int hf_srtp_null_encrypted_payload = -1; */ +static int hf_srtp_mki = -1; +static int hf_srtp_auth_tag = -1; + +/* PacketCable CCC header fields */ +static int proto_pkt_ccc = -1; +static int hf_pkt_ccc_id = -1; +static int hf_pkt_ccc_ts = -1; + +/* PacketCable CCC field defining a sub tree */ +static gint ett_pkt_ccc = -1; + +static expert_field ei_rtp_fragment_unfinished = EI_INIT; +static expert_field ei_rtp_padding_missing = EI_INIT; + +/* RFC 5285 Header extensions */ +static int hf_rtp_ext_rfc5285_id = -1; +static int hf_rtp_ext_rfc5285_length = -1; +static int hf_rtp_ext_rfc5285_appbits = -1; +static int hf_rtp_ext_rfc5285_data = -1; + +/* RFC 4571 Header extension */ +static int hf_rfc4571_header_len = -1; + +#define RTP0_INVALID 0 +#define RTP0_STUN 1 +#define RTP0_CLASSICSTUN 2 +#define RTP0_T38 3 +#define RTP0_SPRT 4 +#define RTP0_RFC7983 5 + +static const enum_val_t rtp_version0_types[] = { + { "invalid", "Invalid or ZRTP packets", RTP0_INVALID }, + { "stun", "STUN packets", RTP0_STUN }, + { "classicstun", "CLASSIC-STUN packets", RTP0_CLASSICSTUN }, + { "t38", "T.38 packets", RTP0_T38 }, + { "sprt", "SPRT packets", RTP0_SPRT }, + { "rfc7983", "Multiplexed as in RFC 7983", RTP0_RFC7983 }, + { NULL, NULL, 0 } +}; +static gint global_rtp_version0_type = 5; + +/* Forward declaration we need below */ +void proto_register_rtp(void); +void proto_reg_handoff_rtp(void); +void proto_register_pkt_ccc(void); +void proto_reg_handoff_pkt_ccc(void); + +static gint dissect_rtp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data); +static void show_setup_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree); +static struct _rtp_packet_info *get_rtp_packet_info(packet_info *pinfo, struct _rtp_info *rtp_info); + +/* Preferences bool to control whether or not setup info should be shown */ +static gboolean global_rtp_show_setup_info = TRUE; + +/* desegment RTP streams */ +static gboolean desegment_rtp = TRUE; + +/* RFC2198 Redundant Audio Data */ +#define RFC2198_DEFAULT_PT_RANGE "99" + +static gboolean rfc2198_deencapsulate = TRUE; + + + +/* + * Fields in the first octet of the RTP header. + */ + +/* Version is the first 2 bits of the first octet*/ +#define RTP_VERSION(octet) ((octet) >> 6) + +/* Padding is the third bit; No need to shift, because true is any value + other than 0! */ +#define RTP_PADDING(octet) ((octet) & 0x20) + +/* Extension bit is the fourth bit */ +#define RTP_EXTENSION(octet) ((octet) & 0x10) + +/* ED137 signature */ +#define RTP_ED137_SIG 0x0067 + +/* ED137A signature */ +#define RTP_ED137A_SIG 0x0167 + +/* RFC 5285 one byte header signature */ +#define RTP_RFC5285_ONE_BYTE_SIG 0xBEDE + +/* RFC 5285 two byte header mask and signature */ +#define RTP_RFC5285_TWO_BYTE_MASK 0xFFF0 +#define RTP_RFC5285_TWO_BYTE_SIG 0x1000 + +/* CSRC count is the last four bits */ +#define RTP_CSRC_COUNT(octet) ((octet) & 0xF) + +static const value_string rtp_version_vals[] = +{ + { 2, "RFC 1889 Version" }, /* First for speed */ + { 0, "Old VAT Version" }, + { 1, "First Draft Version" }, + { 0, NULL }, +}; + +static const range_string rtp_ext_profile_rvals[] = +{ + { RTP_ED137_SIG, RTP_ED137_SIG, "ED137" }, + { RTP_ED137A_SIG, RTP_ED137A_SIG, "ED137A" }, + { RTP_RFC5285_TWO_BYTE_SIG, RTP_RFC5285_TWO_BYTE_SIG + 0xF, "RFC 5285 Two-Byte Header Extensions" }, + { RTP_RFC5285_ONE_BYTE_SIG, RTP_RFC5285_ONE_BYTE_SIG, "RFC 5285 One-Byte Header Extensions" }, + { 0, 0, NULL }, +}; + +/* + * Fields in the second octet of the RTP header. + */ + +/* Marker is the first bit of the second octet */ +#define RTP_MARKER(octet) ((octet) & 0x80) + +/* Payload type is the last 7 bits */ +#define RTP_PAYLOAD_TYPE(octet) ((octet) & 0x7F) +/* https://www.iana.org/assignments/rtp-parameters/ */ + +#define FIRST_RTCP_CONFLICT_PAYLOAD_TYPE 64 +#define LAST_RTCP_CONFLICT_PAYLOAD_TYPE 95 + +static const value_string rtp_payload_type_vals[] = +{ +/* 0 */ { PT_PCMU, "ITU-T G.711 PCMU" }, +/* 1 */ { PT_1016, "USA Federal Standard FS-1016" }, +/* 2 */ { PT_G721, "ITU-T G.721" }, +/* 3 */ { PT_GSM, "GSM 06.10" }, +/* 4 */ { PT_G723, "ITU-T G.723" }, +/* 5 */ { PT_DVI4_8000, "DVI4 8000 samples/s" }, +/* 6 */ { PT_DVI4_16000, "DVI4 16000 samples/s" }, +/* 7 */ { PT_LPC, "Experimental linear predictive encoding from Xerox PARC" }, +/* 8 */ { PT_PCMA, "ITU-T G.711 PCMA" }, +/* 9 */ { PT_G722, "ITU-T G.722" }, +/* 10 */ { PT_L16_STEREO, "16-bit uncompressed audio, stereo" }, +/* 11 */ { PT_L16_MONO, "16-bit uncompressed audio, monaural" }, +/* 12 */ { PT_QCELP, "Qualcomm Code Excited Linear Predictive coding" }, +/* 13 */ { PT_CN, "Comfort noise" }, +/* 14 */ { PT_MPA, "MPEG-I/II Audio"}, +/* 15 */ { PT_G728, "ITU-T G.728" }, +/* 16 */ { PT_DVI4_11025, "DVI4 11025 samples/s" }, +/* 17 */ { PT_DVI4_22050, "DVI4 22050 samples/s" }, +/* 18 */ { PT_G729, "ITU-T G.729" }, +/* 19 */ { PT_CN_OLD, "Comfort noise (old)" }, +/* 20 */ { 20, "Unassigned" }, +/* 21 */ { 21, "Unassigned" }, +/* 22 */ { 22, "Unassigned" }, +/* 23 */ { 23, "Unassigned" }, +/* 24 */ { 24, "Unassigned" }, +/* 25 */ { PT_CELB, "Sun CellB video encoding" }, +/* 26 */ { PT_JPEG, "JPEG-compressed video" }, +/* 27 */ { 27, "Unassigned" }, +/* 28 */ { PT_NV, "'nv' program" }, +/* 29 */ { 29, "Unassigned" }, +/* 30 */ { 30, "Unassigned" }, +/* 31 */ { PT_H261, "ITU-T H.261" }, +/* 32 */ { PT_MPV, "MPEG-I/II Video"}, +/* 33 */ { PT_MP2T, "MPEG-II transport streams"}, +/* 34 */ { PT_H263, "ITU-T H.263" }, +/* 35-71 Unassigned */ +/* 35 */ { 35, "Unassigned" }, +/* 36 */ { 36, "Unassigned" }, +/* 37 */ { 37, "Unassigned" }, +/* 38 */ { 38, "Unassigned" }, +/* 39 */ { 39, "Unassigned" }, +/* 40 */ { 40, "Unassigned" }, +/* 41 */ { 41, "Unassigned" }, +/* 42 */ { 42, "Unassigned" }, +/* 43 */ { 43, "Unassigned" }, +/* 44 */ { 44, "Unassigned" }, +/* 45 */ { 45, "Unassigned" }, +/* 46 */ { 46, "Unassigned" }, +/* 47 */ { 47, "Unassigned" }, +/* 48 */ { 48, "Unassigned" }, +/* 49 */ { 49, "Unassigned" }, +/* 50 */ { 50, "Unassigned" }, +/* 51 */ { 51, "Unassigned" }, +/* 52 */ { 52, "Unassigned" }, +/* 53 */ { 53, "Unassigned" }, +/* 54 */ { 54, "Unassigned" }, +/* 55 */ { 55, "Unassigned" }, +/* 56 */ { 56, "Unassigned" }, +/* 57 */ { 57, "Unassigned" }, +/* 58 */ { 58, "Unassigned" }, +/* 59 */ { 59, "Unassigned" }, +/* 60 */ { 60, "Unassigned" }, +/* 61 */ { 61, "Unassigned" }, +/* 62 */ { 62, "Unassigned" }, +/* 63 */ { 63, "Unassigned" }, +/* 64 */ { 64, "Unassigned" }, +/* 65 */ { 65, "Unassigned" }, +/* 66 */ { 66, "Unassigned" }, +/* 67 */ { 67, "Unassigned" }, +/* 68 */ { 68, "Unassigned" }, +/* 69 */ { 69, "Unassigned" }, +/* 70 */ { 70, "Unassigned" }, +/* 71 */ { 71, "Unassigned" }, +/* 72-76 Reserved for RTCP conflict avoidance [RFC3551] */ +/* 72 */ { 72, "Reserved for RTCP conflict avoidance" }, +/* 73 */ { 73, "Reserved for RTCP conflict avoidance" }, +/* 74 */ { 74, "Reserved for RTCP conflict avoidance" }, +/* 75 */ { 75, "Reserved for RTCP conflict avoidance" }, +/* 76 */ { 76, "Reserved for RTCP conflict avoidance" }, +/* 77-95 Unassigned, MAY be used if > 32 PT are used */ +/* 77 */ { 77, "Unassigned" }, +/* 78 */ { 78, "Unassigned" }, +/* 79 */ { 79, "Unassigned" }, +/* 80 */ { 80, "Unassigned" }, +/* 81 */ { 81, "Unassigned" }, +/* 82 */ { 82, "Unassigned" }, +/* 83 */ { 83, "Unassigned" }, +/* 84 */ { 84, "Unassigned" }, +/* 85 */ { 85, "Unassigned" }, +/* 86 */ { 86, "Unassigned" }, +/* 87 */ { 87, "Unassigned" }, +/* 88 */ { 88, "Unassigned" }, +/* 89 */ { 89, "Unassigned" }, +/* 90 */ { 90, "Unassigned" }, +/* 91 */ { 91, "Unassigned" }, +/* 92 */ { 92, "Unassigned" }, +/* 93 */ { 93, "Unassigned" }, +/* 94 */ { 94, "Unassigned" }, +/* 95 */ { 95, "Unassigned" }, + /* Added to support additional RTP payload types + * See epan/rtp_pt.h */ + { PT_UNDF_96, "DynamicRTP-Type-96" }, + { PT_UNDF_97, "DynamicRTP-Type-97" }, + { PT_UNDF_98, "DynamicRTP-Type-98" }, + { PT_UNDF_99, "DynamicRTP-Type-99" }, + { PT_UNDF_100, "DynamicRTP-Type-100" }, + { PT_UNDF_101, "DynamicRTP-Type-101" }, + { PT_UNDF_102, "DynamicRTP-Type-102" }, + { PT_UNDF_103, "DynamicRTP-Type-103" }, + { PT_UNDF_104, "DynamicRTP-Type-104" }, + { PT_UNDF_105, "DynamicRTP-Type-105" }, + { PT_UNDF_106, "DynamicRTP-Type-106" }, + { PT_UNDF_107, "DynamicRTP-Type-107" }, + { PT_UNDF_108, "DynamicRTP-Type-108" }, + { PT_UNDF_109, "DynamicRTP-Type-109" }, + { PT_UNDF_110, "DynamicRTP-Type-110" }, + { PT_UNDF_111, "DynamicRTP-Type-111" }, + { PT_UNDF_112, "DynamicRTP-Type-112" }, + { PT_UNDF_113, "DynamicRTP-Type-113" }, + { PT_UNDF_114, "DynamicRTP-Type-114" }, + { PT_UNDF_115, "DynamicRTP-Type-115" }, + { PT_UNDF_116, "DynamicRTP-Type-116" }, + { PT_UNDF_117, "DynamicRTP-Type-117" }, + { PT_UNDF_118, "DynamicRTP-Type-118" }, + { PT_UNDF_119, "DynamicRTP-Type-119" }, + { PT_UNDF_120, "DynamicRTP-Type-120" }, + { PT_UNDF_121, "DynamicRTP-Type-121" }, + { PT_UNDF_122, "DynamicRTP-Type-122" }, + { PT_UNDF_123, "DynamicRTP-Type-123" }, + { PT_UNDF_124, "DynamicRTP-Type-124" }, + { PT_UNDF_125, "DynamicRTP-Type-125" }, + { PT_UNDF_126, "DynamicRTP-Type-126" }, + { PT_UNDF_127, "DynamicRTP-Type-127" }, + + { 0, NULL }, +}; + +value_string_ext rtp_payload_type_vals_ext = VALUE_STRING_EXT_INIT(rtp_payload_type_vals); + +static const value_string rtp_payload_type_short_vals[] = +{ + { PT_PCMU, "g711U" }, + { PT_1016, "fs-1016" }, + { PT_G721, "g721" }, + { PT_GSM, "GSM" }, + { PT_G723, "g723" }, + { PT_DVI4_8000, "DVI4 8k" }, + { PT_DVI4_16000, "DVI4 16k" }, + { PT_LPC, "Exp. from Xerox PARC" }, + { PT_PCMA, "g711A" }, + { PT_G722, "g722" }, + { PT_L16_STEREO, "16-bit audio, stereo" }, + { PT_L16_MONO, "16-bit audio, monaural" }, + { PT_QCELP, "Qualcomm" }, + { PT_CN, "CN" }, + { PT_MPA, "MPEG-I/II Audio"}, + { PT_G728, "g728" }, + { PT_DVI4_11025, "DVI4 11k" }, + { PT_DVI4_22050, "DVI4 22k" }, + { PT_G729, "g729" }, + { PT_CN_OLD, "CN(old)" }, + { 20, "Unassigned" }, + { 21, "Unassigned" }, + { 22, "Unassigned" }, + { 23, "Unassigned" }, + { 24, "Unassigned" }, + { PT_CELB, "CellB" }, + { PT_JPEG, "JPEG" }, + { 27, "Unassigned" }, + { PT_NV, "NV" }, + { 29, "Unassigned" }, + { 30, "Unassigned" }, + { PT_H261, "h261" }, + { PT_MPV, "MPEG-I/II Video"}, + { PT_MP2T, "MPEG-II streams"}, + { PT_H263, "h263" }, +/* 35-71 Unassigned */ + { 35, "Unassigned" }, + { 36, "Unassigned" }, + { 37, "Unassigned" }, + { 38, "Unassigned" }, + { 39, "Unassigned" }, + { 40, "Unassigned" }, + { 41, "Unassigned" }, + { 42, "Unassigned" }, + { 43, "Unassigned" }, + { 44, "Unassigned" }, + { 45, "Unassigned" }, + { 46, "Unassigned" }, + { 47, "Unassigned" }, + { 48, "Unassigned" }, + { 49, "Unassigned" }, + { 50, "Unassigned" }, + { 51, "Unassigned" }, + { 52, "Unassigned" }, + { 53, "Unassigned" }, + { 54, "Unassigned" }, + { 55, "Unassigned" }, + { 56, "Unassigned" }, + { 57, "Unassigned" }, + { 58, "Unassigned" }, + { 59, "Unassigned" }, + { 60, "Unassigned" }, + { 61, "Unassigned" }, + { 62, "Unassigned" }, + { 63, "Unassigned" }, + { 64, "Unassigned" }, + { 65, "Unassigned" }, + { 66, "Unassigned" }, + { 67, "Unassigned" }, + { 68, "Unassigned" }, + { 69, "Unassigned" }, + { 70, "Unassigned" }, + { 71, "Unassigned" }, +/* 72-76 Reserved for RTCP conflict avoidance - [RFC3551] */ + { 72, "Reserved for RTCP conflict avoidance" }, + { 73, "Reserved for RTCP conflict avoidance" }, + { 74, "Reserved for RTCP conflict avoidance" }, + { 75, "Reserved for RTCP conflict avoidance" }, + { 76, "Reserved for RTCP conflict avoidance" }, +/* 77-95 Unassigned, MAY be used if > 32 PT are used */ + { 77, "Unassigned" }, + { 78, "Unassigned" }, + { 79, "Unassigned" }, + { 80, "Unassigned" }, + { 81, "Unassigned" }, + { 82, "Unassigned" }, + { 83, "Unassigned" }, + { 84, "Unassigned" }, + { 85, "Unassigned" }, + { 86, "Unassigned" }, + { 87, "Unassigned" }, + { 88, "Unassigned" }, + { 89, "Unassigned" }, + { 90, "Unassigned" }, + { 91, "Unassigned" }, + { 92, "Unassigned" }, + { 93, "Unassigned" }, + { 94, "Unassigned" }, + { 95, "Unassigned" }, + /* Short RTP types */ + { PT_UNDF_96, "RTPType-96" }, + { PT_UNDF_97, "RTPType-97" }, + { PT_UNDF_98, "RTPType-98" }, + { PT_UNDF_99, "RTPType-99" }, + { PT_UNDF_100, "RTPType-100" }, + { PT_UNDF_101, "RTPType-101" }, + { PT_UNDF_102, "RTPType-102" }, + { PT_UNDF_103, "RTPType-103" }, + { PT_UNDF_104, "RTPType-104" }, + { PT_UNDF_105, "RTPType-105" }, + { PT_UNDF_106, "RTPType-106" }, + { PT_UNDF_107, "RTPType-107" }, + { PT_UNDF_108, "RTPType-108" }, + { PT_UNDF_109, "RTPType-109" }, + { PT_UNDF_110, "RTPType-110" }, + { PT_UNDF_111, "RTPType-111" }, + { PT_UNDF_112, "RTPType-112" }, + { PT_UNDF_113, "RTPType-113" }, + { PT_UNDF_114, "RTPType-114" }, + { PT_UNDF_115, "RTPType-115" }, + { PT_UNDF_116, "RTPType-116" }, + { PT_UNDF_117, "RTPType-117" }, + { PT_UNDF_118, "RTPType-118" }, + { PT_UNDF_119, "RTPType-119" }, + { PT_UNDF_120, "RTPType-120" }, + { PT_UNDF_121, "RTPType-121" }, + { PT_UNDF_122, "RTPType-122" }, + { PT_UNDF_123, "RTPType-123" }, + { PT_UNDF_124, "RTPType-124" }, + { PT_UNDF_125, "RTPType-125" }, + { PT_UNDF_126, "RTPType-126" }, + { PT_UNDF_127, "RTPType-127" }, + + { 0, NULL }, +}; +value_string_ext rtp_payload_type_short_vals_ext = VALUE_STRING_EXT_INIT(rtp_payload_type_short_vals); + +#if 0 +static const value_string srtp_encryption_alg_vals[] = +{ + { SRTP_ENC_ALG_NULL, "Null Encryption" }, + { SRTP_ENC_ALG_AES_CM, "AES-128 Counter Mode" }, + { SRTP_ENC_ALG_AES_F8, "AES-128 F8 Mode" }, + { 0, NULL }, +}; + +static const value_string srtp_auth_alg_vals[] = +{ + { SRTP_AUTH_ALG_NONE, "No Authentication" }, + { SRTP_AUTH_ALG_HMAC_SHA1, "HMAC-SHA1" }, + { 0, NULL }, +}; +#endif + +static void rtp_prompt(packet_info *pinfo _U_, gchar* result) +{ + guint payload_type = GPOINTER_TO_UINT(p_get_proto_data(pinfo->pool, pinfo, proto_rtp, RTP_DECODE_AS_PROTO_DATA)); + + /* Dynamic payload range, don't expose value as it may change within conversation */ + if (payload_type > 95) + { + snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "RTP payload type as"); + } + else + { + snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "RTP payload type %d as", payload_type); + } +} + +static gpointer rtp_value(packet_info *pinfo) +{ + guint payload_type = GPOINTER_TO_UINT(p_get_proto_data(pinfo->pool, pinfo, proto_rtp, RTP_DECODE_AS_PROTO_DATA)); + + return GUINT_TO_POINTER(payload_type); +} + +#ifdef DEBUG_CONVERSATION +/* Called for each entry in the rtp_dyn_payload hash table. */ +static void +rtp_dyn_payload_table_foreach_func(gpointer key, gpointer value, gpointer user_data _U_) { + guint pt = GPOINTER_TO_UINT(key); + encoding_name_and_rate_t *encoding = (encoding_name_and_rate_t*) value; + + DPRINT2(("pt=%d", pt)); + if (encoding) { + DPRINT2(("encoding_name=%s", + encoding->encoding_name ? encoding->encoding_name : "NULL")); + DPRINT2(("sample_rate=%d", encoding->sample_rate)); + DPRINT2(("channels=%u", encoding->channels)); + } else { + DPRINT2(("encoding=NULL")); + } +} + +void +rtp_dump_dyn_payload(rtp_dyn_payload_t *rtp_dyn_payload) { + DPRINT2(("rtp_dyn_payload hash table contents:")); + DINDENT(); + if (!rtp_dyn_payload) { + DPRINT2(("null pointer to rtp_dyn_payload")); + DENDENT(); + return; + } + DPRINT2(("ref_count=%zu", rtp_dyn_payload->ref_count)); + if (!rtp_dyn_payload->table) { + DPRINT2(("null rtp_dyn_payload table")); + DENDENT(); + return; + } + if (g_hash_table_size(rtp_dyn_payload->table) == 0) { + DPRINT2(("rtp_dyn_payload has no entries")); + } else { + g_hash_table_foreach(rtp_dyn_payload->table, rtp_dyn_payload_table_foreach_func, NULL); + } + DENDENT(); +} +#endif /* DEBUG_CONVERSATION */ + +/* A single hash table to hold pointers to all the rtp_dyn_payload_t's we create/destroy. + This is necessary because we need to g_hash_table_destroy() them, either individually or + all at once at the end of the wmem file scope. Since rtp_dyn_payload_free() removes them + individually, we need to remove those then; and when the file scope is over, we have a + single registered callback walk this GHashTable and destroy each member as well as this + GHashTable. + */ +static GHashTable *rtp_dyn_payloads = NULL; + +static gboolean +fmtp_free(gpointer key, gpointer value, gpointer user_data) +{ + wmem_allocator_t *scope = (wmem_allocator_t*)user_data; + + wmem_free(scope, key); + wmem_free(scope, value); + + return TRUE; +} + +/* the following is the GDestroyNotify function used when the individual rtp_dyn_payload_t + GHashTables are destroyed */ +static void +rtp_dyn_payload_value_destroy(gpointer data) +{ + encoding_name_and_rate_t *encoding_name_and_rate_pt = (encoding_name_and_rate_t*) data; + wmem_free(wmem_file_scope(), encoding_name_and_rate_pt->encoding_name); + wmem_map_foreach_remove(encoding_name_and_rate_pt->fmtp_map, fmtp_free, wmem_file_scope()); + wmem_free(wmem_file_scope(), encoding_name_and_rate_pt->fmtp_map); + wmem_free(wmem_file_scope(), encoding_name_and_rate_pt); +} + +/* this gets called by wmem_rtp_dyn_payload_destroy_cb */ +static gboolean +rtp_dyn_payloads_table_steal_func(gpointer key _U_, gpointer value, gpointer user_data _U_) +{ + rtp_dyn_payload_t *rtp_dyn_payload = (rtp_dyn_payload_t *)value; + +#ifdef DEBUG_CONVERSATION + DPRINT(("about to steal_all and destroy the following:")); + DINDENT(); + rtp_dump_dyn_payload(rtp_dyn_payload); + DENDENT(); +#endif + + if (rtp_dyn_payload->ref_count == 0) { + /* this shouldn't happen */ + DPRINT(("rtp_dyn_payload cannot be free'd because it should already have been!\n")); + } + else if (rtp_dyn_payload->table) { + /* each member was created with a wmem file scope, so there's no point in calling the + destroy functions for the GHashTable entries, so we steal them instead */ + g_hash_table_steal_all(rtp_dyn_payload->table); + g_hash_table_destroy(rtp_dyn_payload->table); + } + + return TRUE; +} + +/* the following is used as the wmem callback to destroy *all* alive rtp_dyn_payload_t's, + which are pointed to by the single rtp_dyn_payloads GHashTable above. + */ +static bool +wmem_rtp_dyn_payload_destroy_cb(wmem_allocator_t *allocator _U_, wmem_cb_event_t event _U_, + void *user_data _U_) +{ + DPRINT(("destroying %u remaining rtp_dyn_payload_t's", g_hash_table_size(rtp_dyn_payloads))); + + /* each member was created with a wmem file scope, so there's no point in calling the + destroy functions for the GHashTable entries, so we steal them instead */ + g_hash_table_foreach_steal(rtp_dyn_payloads, rtp_dyn_payloads_table_steal_func, NULL); + g_hash_table_destroy(rtp_dyn_payloads); + rtp_dyn_payloads = NULL; + + /* remove this callback? */ + return FALSE; +} + +/* the following initializes the single GHashTable - this is invoked as an init_routine, + but those are called both at init and cleanup times, and the cleanup time is before + wmem scope is exited, so we ignore this if rtp_dyn_payloads is not NULL. + */ +static void +rtp_dyn_payloads_init(void) +{ + if (rtp_dyn_payloads == NULL) { + rtp_dyn_payloads = g_hash_table_new(NULL, NULL); + wmem_register_callback(wmem_file_scope(), wmem_rtp_dyn_payload_destroy_cb, NULL); + } +} + +/* creates a new hashtable and sets ref_count to 1, returning the newly created object */ +rtp_dyn_payload_t* rtp_dyn_payload_new(void) +{ + /* create the new entry */ + rtp_dyn_payload_t * rtp_dyn_payload = wmem_new(wmem_file_scope(), rtp_dyn_payload_t); + rtp_dyn_payload->table = g_hash_table_new_full(NULL, NULL, NULL, rtp_dyn_payload_value_destroy); + rtp_dyn_payload->ref_count = 1; + + /* now put it in our single rtp_dyn_payloads GHashTable */ + g_hash_table_insert(rtp_dyn_payloads, rtp_dyn_payload, rtp_dyn_payload); + + return rtp_dyn_payload; +} + +/* Creates a copy of the given dynamic payload information. */ +rtp_dyn_payload_t* rtp_dyn_payload_dup(rtp_dyn_payload_t *rtp_dyn_payload) +{ + rtp_dyn_payload_t *rtp_dyn_payload2 = rtp_dyn_payload_new(); + GHashTableIter iter; + gpointer key, value; + + g_hash_table_iter_init(&iter, rtp_dyn_payload->table); + while (g_hash_table_iter_next(&iter, &key, &value)) { + const guint pt = GPOINTER_TO_UINT(key); + encoding_name_and_rate_t *encoding_name_and_rate_pt = + (encoding_name_and_rate_t *)value; + + rtp_dyn_payload_insert_full(rtp_dyn_payload2, pt, + encoding_name_and_rate_pt->encoding_name, + encoding_name_and_rate_pt->sample_rate, + encoding_name_and_rate_pt->channels, + encoding_name_and_rate_pt->fmtp_map); + + } + + return rtp_dyn_payload2; +} + +static rtp_dyn_payload_t* +rtp_dyn_payload_ref(rtp_dyn_payload_t *rtp_dyn_payload) +{ + if (rtp_dyn_payload) { + rtp_dyn_payload->ref_count++; + } + return rtp_dyn_payload; +} + +static void +rtp_dyn_payload_add_fmtp_int(gpointer key, gpointer value, gpointer user_data) +{ + wmem_map_t *fmtp_map = (wmem_map_t*)user_data; + const char *k = (const char*)key; + const char *v = (const char*)value; + + wmem_map_insert(fmtp_map, wmem_strdup(wmem_file_scope(), k), wmem_strdup(wmem_file_scope(), v)); +} + +/* Inserts the given payload type key, for the encoding name and sample rate, + into the hash table. Copy all the format parameters in the map given into + the format parameter map for the new entry. + This makes copies of the encoding name and the format parameters, scoped to + the life of the capture file or sooner if rtp_dyn_payload_free is called. + */ +void +rtp_dyn_payload_insert_full(rtp_dyn_payload_t *rtp_dyn_payload, + const guint pt, + const gchar* encoding_name, + const int sample_rate, + const unsigned channels, + wmem_map_t *fmtp_map) +{ + if (rtp_dyn_payload && rtp_dyn_payload->table) { + encoding_name_and_rate_t *encoding_name_and_rate_pt = (encoding_name_and_rate_t*)g_hash_table_lookup(rtp_dyn_payload->table, + GUINT_TO_POINTER(pt)); + if (!encoding_name_and_rate_pt) { + encoding_name_and_rate_pt = wmem_new(wmem_file_scope(), encoding_name_and_rate_t); + encoding_name_and_rate_pt->fmtp_map = wmem_map_new(wmem_file_scope(), wmem_str_hash, g_str_equal); + g_hash_table_insert(rtp_dyn_payload->table, GUINT_TO_POINTER(pt), encoding_name_and_rate_pt); + } + encoding_name_and_rate_pt->encoding_name = wmem_strdup(wmem_file_scope(), encoding_name); + encoding_name_and_rate_pt->sample_rate = sample_rate; + encoding_name_and_rate_pt->channels = channels; + if (fmtp_map) { + wmem_map_foreach(fmtp_map, rtp_dyn_payload_add_fmtp_int, encoding_name_and_rate_pt->fmtp_map); + } + } +} + +/* Inserts the given payload type key, for the encoding name and sample rate, + into the hash table. + This makes copies of the encoding name, scoped to the life of the capture + file or sooner if rtp_dyn_payload_free is called. */ +void +rtp_dyn_payload_insert(rtp_dyn_payload_t *rtp_dyn_payload, + const guint pt, + const gchar* encoding_name, + const int sample_rate, + const unsigned channels) +{ + rtp_dyn_payload_insert_full(rtp_dyn_payload, pt, encoding_name, sample_rate, channels, NULL); +} + +/* Adds the given format parameter to the fmtp_map for the given payload type + in the RTP dynamic payload hashtable, if that payload type has been + inserted with rtp_dyn_payload_insert. The format parameter name and value + are copied, with scope the lifetime of the capture file. + */ +void +rtp_dyn_payload_add_fmtp(rtp_dyn_payload_t *rtp_dyn_payload, + const guint pt, + const char *key, const char *value) +{ + if (rtp_dyn_payload && rtp_dyn_payload->table) { + encoding_name_and_rate_t *encoding_name_and_rate_pt = (encoding_name_and_rate_t*)g_hash_table_lookup(rtp_dyn_payload->table, + GUINT_TO_POINTER(pt)); + + if (!encoding_name_and_rate_pt) { + rtp_dyn_payload_insert(rtp_dyn_payload, pt, "Unknown", 0, 1); + encoding_name_and_rate_pt = (encoding_name_and_rate_t*)g_hash_table_lookup(rtp_dyn_payload->table, GUINT_TO_POINTER(pt)); + } + + rtp_dyn_payload_add_fmtp_int((void*)key, (void*)value, encoding_name_and_rate_pt->fmtp_map); + } +} + +/* Replaces the given payload type key in the hash table, with the encoding name and sample rate. + This makes copies of the encoding name, scoped to the life of the capture file or sooner if + rtp_dyn_payload_free is called. */ +/* Not used anymore +void +rtp_dyn_payload_replace(rtp_dyn_payload_t *rtp_dyn_payload, + const guint pt, + const gchar* encoding_name, + const int sample_rate) +{ + if (rtp_dyn_payload && rtp_dyn_payload->table) { + encoding_name_and_rate_t *encoding_name_and_rate_pt = + wmem_new(wmem_file_scope(), encoding_name_and_rate_t); + encoding_name_and_rate_pt->encoding_name = wmem_strdup(wmem_file_scope(), encoding_name); + encoding_name_and_rate_pt->sample_rate = sample_rate; + g_hash_table_replace(rtp_dyn_payload->table, GUINT_TO_POINTER(pt), encoding_name_and_rate_pt); + } +} +*/ + +/* removes the given payload type */ +/* Not used anymore +gboolean +rtp_dyn_payload_remove(rtp_dyn_payload_t *rtp_dyn_payload, const guint pt) +{ + return (rtp_dyn_payload && rtp_dyn_payload->table && + g_hash_table_remove(rtp_dyn_payload->table, GUINT_TO_POINTER(pt))); +} +*/ + +/* retrieves the encoding name for the given payload type */ +const gchar* +rtp_dyn_payload_get_name(rtp_dyn_payload_t *rtp_dyn_payload, const guint pt) +{ + encoding_name_and_rate_t *encoding_name_and_rate_pt; + + if (!rtp_dyn_payload || !rtp_dyn_payload->table) return NULL; + + encoding_name_and_rate_pt = (encoding_name_and_rate_t*)g_hash_table_lookup(rtp_dyn_payload->table, + GUINT_TO_POINTER(pt)); + + return (encoding_name_and_rate_pt ? encoding_name_and_rate_pt->encoding_name : NULL); +} + +/* + Retrieves the encoding name, sample rate, and format parameters map for the + given payload type. The encoding string pointed to is only valid until + the entry is replaced, removed, or the hash table is destroyed, so duplicate + it if you need it long. Each of the three output parameters are optional and + can be NULL. + */ +gboolean +rtp_dyn_payload_get_full(rtp_dyn_payload_t *rtp_dyn_payload, const guint pt, + const gchar **encoding_name, int *sample_rate, + unsigned *channels, wmem_map_t **fmtp_map) +{ + encoding_name_and_rate_t *encoding_name_and_rate_pt; + if (encoding_name) { + *encoding_name = NULL; + } + if (sample_rate) { + *sample_rate = 0; + } + if (channels) { + *channels = 0; + } + if (fmtp_map) { + *fmtp_map = NULL; + } + + if (!rtp_dyn_payload || !rtp_dyn_payload->table) return FALSE; + + encoding_name_and_rate_pt = (encoding_name_and_rate_t*)g_hash_table_lookup(rtp_dyn_payload->table, + GUINT_TO_POINTER(pt)); + + if (encoding_name_and_rate_pt) { + if (encoding_name) { + *encoding_name = encoding_name_and_rate_pt->encoding_name; + } + if (sample_rate) { + *sample_rate = encoding_name_and_rate_pt->sample_rate; + } + if (channels) { + *channels = encoding_name_and_rate_pt->channels; + } + if (fmtp_map) { + *fmtp_map = encoding_name_and_rate_pt->fmtp_map; + } + } + + return (encoding_name_and_rate_pt != NULL); +} + +/* Free's and destroys the dyn_payload hash table; internally this decrements the ref_count + and only free's it if the ref_count == 0. */ +void +rtp_dyn_payload_free(rtp_dyn_payload_t *rtp_dyn_payload) +{ + if (!rtp_dyn_payload) return; + + if (rtp_dyn_payload->ref_count > 0) + --(rtp_dyn_payload->ref_count); + + if (rtp_dyn_payload->ref_count == 0) { + +#ifdef DEBUG_CONVERSATION + DPRINT(("free'ing the following rtp_dyn_payload:")); + DINDENT(); + rtp_dump_dyn_payload(rtp_dyn_payload); + DENDENT(); +#endif + + /* remove it from the single rtp_dyn_payloads GHashTable */ + if (!g_hash_table_remove(rtp_dyn_payloads, rtp_dyn_payload)) { + DPRINT(("rtp_dyn_payload not found in rtp_dyn_payloads table to remove!")); + } + + /* destroy the table GHashTable in it - this automatically deletes the + members too, because we used destroy function callbacks */ + if (rtp_dyn_payload->table) + g_hash_table_destroy(rtp_dyn_payload->table); + + /* free the object itself */ + wmem_free(wmem_file_scope(), rtp_dyn_payload); + } +} + +void +bluetooth_add_address(packet_info *pinfo, address *addr, guint32 stream_number, + const gchar *setup_method, guint32 setup_frame_number, + guint32 media_types, void *data) +{ + address null_addr; + conversation_t* p_conv; + struct _rtp_conversation_info *p_conv_data = NULL; + /* + * If this isn't the first time this packet has been processed, + * we've already done this work, so we don't need to do it + * again. + */ + if ((pinfo->fd->visited) || (rtp_handle == NULL)) + { + return; + } + + clear_address(&null_addr); + + /* + * Check if the ip address and port combination is not + * already registered as a conversation. + */ + p_conv = find_conversation(setup_frame_number, addr, &null_addr, CONVERSATION_BLUETOOTH, stream_number, stream_number, + NO_ADDR_B | NO_PORT_B); + + /* + * If not, create a new conversation. + */ + if (!p_conv || p_conv->setup_frame != setup_frame_number) { + p_conv = conversation_new(setup_frame_number, addr, &null_addr, CONVERSATION_BLUETOOTH, stream_number, stream_number, + NO_ADDR2 | NO_PORT2); + } + + /* Set dissector */ + conversation_set_dissector(p_conv, rtp_handle); + + /* + * Check if the conversation has data associated with it. + */ + p_conv_data = (struct _rtp_conversation_info *)conversation_get_proto_data(p_conv, proto_rtp); + + /* + * If not, add a new data item. + */ + if (! p_conv_data) { + /* Create conversation data */ + p_conv_data = wmem_new0(wmem_file_scope(), struct _rtp_conversation_info); + + p_conv_data->ssrc_number_space = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal); + p_conv_data->rtp_conv_info = wmem_new(wmem_file_scope(), rtp_private_conv_info); + p_conv_data->rtp_conv_info->multisegment_pdus = wmem_tree_new(wmem_file_scope()); + conversation_add_proto_data(p_conv, proto_rtp, p_conv_data); + + if (media_types == RTP_MEDIA_AUDIO) { + p_conv_data->bta2dp_info = (bta2dp_codec_info_t *) wmem_memdup(wmem_file_scope(), data, sizeof(bta2dp_codec_info_t)); + } else if (media_types == RTP_MEDIA_VIDEO) { + p_conv_data->btvdp_info = (btvdp_codec_info_t *) wmem_memdup(wmem_file_scope(), data, sizeof(btvdp_codec_info_t)); + } + } + + /* + * Update the conversation data. + */ + /* Free the hash if already exists */ + rtp_dyn_payload_free(p_conv_data->rtp_dyn_payload); + + (void) g_strlcpy(p_conv_data->method, setup_method, MAX_RTP_SETUP_METHOD_SIZE+1); + p_conv_data->frame_number = setup_frame_number; + p_conv_data->media_types = media_types; + p_conv_data->rtp_dyn_payload = NULL; + p_conv_data->srtp_info = NULL; +} +static void +rtp_add_setup_info_if_no_duplicate(sdp_setup_info_t *setup_info, wmem_array_t *sdp_conv_info_list) +{ + sdp_setup_info_t *stored_setup_info; + guint i; + + for (i = 0; i < wmem_array_get_count(sdp_conv_info_list); i++) { + stored_setup_info = (sdp_setup_info_t *)wmem_array_index(sdp_conv_info_list, i); + + /* Check if we have the call id allready */ + if ((stored_setup_info->hf_type == SDP_TRACE_ID_HF_TYPE_STR) && (setup_info->hf_type == SDP_TRACE_ID_HF_TYPE_STR)) { + if (strcmp(stored_setup_info->trace_id.str, setup_info->trace_id.str) == 0) { + return; /* Do not store the call id */ + } + } else if ((stored_setup_info->hf_type == SDP_TRACE_ID_HF_TYPE_GUINT32) && (setup_info->hf_type == SDP_TRACE_ID_HF_TYPE_GUINT32)) { + if (stored_setup_info->trace_id.num == setup_info->trace_id.num) { + return; /* Do not store the call id */ + } + } + } + + wmem_array_append(sdp_conv_info_list, setup_info, 1); + +} +/* Set up an SRTP conversation */ +void +srtp_add_address(packet_info *pinfo, const port_type ptype, address *addr, int port, int other_port, + const gchar *setup_method, guint32 setup_frame_number, + guint32 media_types _U_, rtp_dyn_payload_t *rtp_dyn_payload, + struct srtp_info *srtp_info, sdp_setup_info_t *setup_info) +{ + address null_addr; + conversation_t* p_conv, *sdp_conv; + struct _rtp_conversation_info *p_conv_data; + wmem_array_t *rtp_conv_info_list = NULL; + + /* + * If this isn't the first time this packet has been processed, + * we've already done this work, so we don't need to do it + * again. + */ + if ((pinfo->fd->visited) || (rtp_handle == NULL) || (rtp_rfc4571_handle == NULL)) + { + return; + } + + DPRINT(("#%u: %srtp_add_address(%d, %s, %u, %u, %s, %u)", + pinfo->num, (srtp_info)?"s":"", ptype, address_to_str(pinfo->pool, addr), port, + other_port, setup_method, setup_frame_number)); + DINDENT(); + + clear_address(&null_addr); + + /* + * Check if the ip address and port combination is not + * already registered as a conversation. + */ + p_conv = find_conversation(setup_frame_number, addr, &null_addr, conversation_pt_to_conversation_type(ptype), port, other_port, + NO_ADDR_B | (!other_port ? NO_PORT_B : 0)); + + if (p_conv) { + /* + * Check if the conversation has data associated with it. + */ + p_conv_data = (struct _rtp_conversation_info *)conversation_get_proto_data(p_conv, proto_rtp); + if (p_conv_data) { + rtp_conv_info_list = p_conv_data->rtp_sdp_setup_info_list; + } + } + + DENDENT(); + DPRINT(("did %sfind conversation", p_conv?"":"NOT ")); + + /* + * If not, create a new conversation. + */ + if (!p_conv || p_conv->setup_frame != setup_frame_number) { + p_conv = conversation_new(setup_frame_number, addr, &null_addr, conversation_pt_to_conversation_type(ptype), + (guint32)port, (guint32)other_port, + NO_ADDR2 | (!other_port ? NO_PORT2 : 0)); + } + + /* Set dissector */ + if (ptype == PT_UDP) { + /* For RFC 5761 multiplexing, go ahead and create/update [S]RTCP + * info for the conversation, since this dissector will pass RTCP PTs + * to the RTCP dissector anyway. + * XXX: We only do this on UDP, as RFC 4571 specifies RTP and RTCP on + * different ports, but the RTCP dissector (like SDP) doesn't support + * RFC 4571 currently anyway. + */ + srtcp_add_address(pinfo, addr, port, other_port, setup_method, setup_frame_number, srtp_info); + /* Set the dissector afterwards, since RTCP will set the conversation + * to its dissector, but packets should go to RTP first. + */ + conversation_set_dissector(p_conv, rtp_handle); + } else if (ptype == PT_TCP) { + conversation_set_dissector(p_conv, rtp_rfc4571_handle); + } else { + DISSECTOR_ASSERT(FALSE); + } + + /* + * Check if the conversation has data associated with it. + */ + p_conv_data = (struct _rtp_conversation_info *)conversation_get_proto_data(p_conv, proto_rtp); + + /* + * If not, add a new data item. + */ + if (! p_conv_data) { + DPRINT(("creating new conversation data")); + + /* Create conversation data */ + p_conv_data = wmem_new0(wmem_file_scope(), struct _rtp_conversation_info); + + p_conv_data->ssrc_number_space = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal); + p_conv_data->rtp_conv_info = wmem_new(wmem_file_scope(), rtp_private_conv_info); + p_conv_data->rtp_conv_info->multisegment_pdus = wmem_tree_new(wmem_file_scope()); + DINDENT(); + conversation_add_proto_data(p_conv, proto_rtp, p_conv_data); + DENDENT(); + } +#ifdef DEBUG_CONVERSATION + else { + DPRINT(("conversation already exists")); + } +#endif + + /* + * Update the conversation data. + */ + /* Free the hash if a different one already exists */ + if (p_conv_data->rtp_dyn_payload != rtp_dyn_payload) { + rtp_dyn_payload_free(p_conv_data->rtp_dyn_payload); + p_conv_data->rtp_dyn_payload = rtp_dyn_payload_ref(rtp_dyn_payload); + } else { + DPRINT(("passed-in rtp_dyn_payload is the same as in the conversation")); + } + + (void) g_strlcpy(p_conv_data->method, setup_method, MAX_RTP_SETUP_METHOD_SIZE+1); + p_conv_data->frame_number = setup_frame_number; + p_conv_data->media_types = media_types; + p_conv_data->srtp_info = srtp_info; + p_conv_data->bta2dp_info = NULL; + p_conv_data->btvdp_info = NULL; + + /* If we had a sdp setup info list put it back in the potentially new conversation*/ + p_conv_data->rtp_sdp_setup_info_list = rtp_conv_info_list; + if (setup_info) { + /* If we have new setup info add it to the list*/ + if (p_conv_data->rtp_sdp_setup_info_list) { + /* Add info to the SDP conversation */ + rtp_add_setup_info_if_no_duplicate(setup_info, p_conv_data->rtp_sdp_setup_info_list); + } else { + p_conv_data->rtp_sdp_setup_info_list = wmem_array_new(wmem_file_scope(), sizeof(sdp_setup_info_t)); + wmem_array_append(p_conv_data->rtp_sdp_setup_info_list, setup_info, 1); + } + } + sdp_conv = find_or_create_conversation(pinfo); + if (sdp_conv && p_conv_data->rtp_sdp_setup_info_list) { + /* Add the collected information to the SDP conversation */ + conversation_add_proto_data(sdp_conv, proto_sdp, p_conv_data->rtp_sdp_setup_info_list); + } + +} + +/* Set up an RTP conversation */ +void +rtp_add_address(packet_info *pinfo, const port_type ptype, address *addr, int port, int other_port, + const gchar *setup_method, guint32 setup_frame_number, + guint32 media_types , rtp_dyn_payload_t *rtp_dyn_payload) +{ + srtp_add_address(pinfo, ptype, addr, port, other_port, setup_method, setup_frame_number, media_types, rtp_dyn_payload, NULL, NULL); +} + +static gboolean +dissect_rtp_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data) +{ + guint8 octet1, octet2; + unsigned int version, payload_type; + unsigned int offset = 0; + gint padding_count; + + if (tvb_captured_length_remaining(tvb, offset) < 2) { + return FALSE; + } + + /* Get the fields in the first octet */ + octet1 = tvb_get_guint8( tvb, offset ); + version = RTP_VERSION( octet1 ); + + /* XXX: Why are we calling these dissectors from the *heuristic* + * RTP dissector? These almost all have their own heuristic dissector, + * enabled by default (unlike RTP, which has a much less accurate + * heuristic.) We should just reject and let the protocols' own heuristic + * dissectors handle this. + */ + if (version == 0) { + if (!(tvb_memeql(tvb, 4, (const guint8*)"ZRTP", 4))) + { + call_dissector_only(zrtp_handle, tvb, pinfo, tree, NULL); + return TRUE; + } else { + switch (global_rtp_version0_type) { + case RTP0_STUN: + return call_dissector_only(stun_heur_handle, tvb, pinfo, tree, NULL); + case RTP0_CLASSICSTUN: + return call_dissector_only(classicstun_heur_handle, tvb, pinfo, tree, NULL); + + case RTP0_T38: + /* XXX: Should really be calling a heuristic dissector for T38 ??? */ + call_dissector_only(t38_handle, tvb, pinfo, tree, NULL); + return TRUE; + + case RTP0_SPRT: + call_dissector_only(sprt_handle, tvb, pinfo, tree, NULL); + return TRUE; + + case RTP0_INVALID: + case RTP0_RFC7983: + default: + return FALSE; /* Unknown or unsupported version */ + } + } + } else if (version != 2) { + /* Unknown or unsupported version */ + return FALSE; + } + + octet2 = tvb_get_guint8( tvb, offset + 1 ); + payload_type = RTP_PAYLOAD_TYPE( octet2 ); + + if (payload_type >= 72 && payload_type <= 76) { + /* XXX: This range is definitely excluded by RFCs 3550, 3551. + * There's an argument, per RFC 5761, for expanding the + * excluded range to [FIRST_RTCP_CONFLICT_PAYLOAD_TYPE, + * LAST_RTCP_CONFLICT_PAYLOAD_TYPE] in the heuristic dissector, + * leaving those values only when specificed by other means + * (SDP, Decode As, etc.) + */ + return FALSE; + } + + /* Skip fixed header */ + offset += 12; + + offset += 4 * RTP_CSRC_COUNT( octet1 ); + if (RTP_EXTENSION( octet1 )) { + if (tvb_captured_length_remaining(tvb, offset) < 4) { + return FALSE; + } + offset += 4 + 4*tvb_get_guint16(tvb, offset+2, ENC_BIG_ENDIAN); + } + if (tvb_reported_length(tvb) < offset) { + return FALSE; + } + if (RTP_PADDING( octet1 )) { + if (tvb_captured_length(tvb) == tvb_reported_length(tvb)) { + /* We can test the padding if the last octet is present. */ + padding_count = tvb_get_guint8(tvb, tvb_reported_length(tvb) - 1); + if (tvb_reported_length_remaining(tvb, offset) < padding_count || + padding_count == 0) { + return FALSE; + } + } + } + + /* Create a conversation in case none exists so as to allow reassembly code to work */ + if (!find_conversation(pinfo->num, &pinfo->net_dst, &pinfo->net_src, conversation_pt_to_conversation_type(pinfo->ptype), + pinfo->destport, pinfo->srcport, NO_ADDR_B)) { + conversation_t *p_conv; + struct _rtp_conversation_info *p_conv_data; + p_conv = conversation_new(pinfo->num, &pinfo->net_dst, &pinfo->net_src, conversation_pt_to_conversation_type(pinfo->ptype), + pinfo->destport, pinfo->srcport, NO_ADDR2); + p_conv_data = (struct _rtp_conversation_info *)conversation_get_proto_data(p_conv, proto_rtp); + if (! p_conv_data) { + /* Create conversation data */ + p_conv_data = wmem_new0(wmem_file_scope(), struct _rtp_conversation_info); + p_conv_data->ssrc_number_space = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal); + p_conv_data->rtp_conv_info = wmem_new(wmem_file_scope(), rtp_private_conv_info); + p_conv_data->rtp_conv_info->multisegment_pdus = wmem_tree_new(wmem_file_scope()); + conversation_add_proto_data(p_conv, proto_rtp, p_conv_data); + } + (void) g_strlcpy(p_conv_data->method, "HEUR RTP", MAX_RTP_SETUP_METHOD_SIZE+1); + p_conv_data->frame_number = pinfo->num; + p_conv_data->media_types = 0; + p_conv_data->srtp_info = NULL; + p_conv_data->bta2dp_info = NULL; + p_conv_data->btvdp_info = NULL; + } + dissect_rtp( tvb, pinfo, tree, data ); + return TRUE; +} + +/* + * Process the payload of the RTP packet, hand it to the subdissector + */ +static void +process_rtp_payload(tvbuff_t *newtvb, packet_info *pinfo, proto_tree *tree, + proto_tree *rtp_tree, unsigned int payload_type, + struct _rtp_info *rtp_info) +{ + struct _rtp_packet_info *p_packet_data; + int payload_len; + struct srtp_info *srtp_info; + int offset = 0; + proto_item *rtp_data; + + payload_len = tvb_captured_length_remaining(newtvb, offset); + + /* first check if this is added as an SRTP stream - if so, don't try to dissector the payload data for now */ + p_packet_data = (struct _rtp_packet_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_rtp, RTP_CONVERSATION_PROTO_DATA); + if (p_packet_data && p_packet_data->srtp_info) { + srtp_info = p_packet_data->srtp_info; + payload_len -= srtp_info->mki_len + srtp_info->auth_tag_len; +#if 0 +#error Currently the srtp_info structure contains no cipher data, see packet-sdp.c adding dummy_srtp_info structure + if (p_conv_data->srtp_info->encryption_algorithm == SRTP_ENC_ALG_NULL) { + if (rtp_tree) + proto_tree_add_item(rtp_tree, hf_srtp_null_encrypted_payload, newtvb, offset, payload_len, ENC_NA); + } + else +#endif + { + if (rtp_tree) + proto_tree_add_item(rtp_tree, hf_srtp_encrypted_payload, newtvb, offset, payload_len, ENC_NA); + } + offset += payload_len; + + if (srtp_info->mki_len) { + proto_tree_add_item(rtp_tree, hf_srtp_mki, newtvb, offset, srtp_info->mki_len, ENC_NA); + offset += srtp_info->mki_len; + } + + if (srtp_info->auth_tag_len) { + proto_tree_add_item(rtp_tree, hf_srtp_auth_tag, newtvb, offset, srtp_info->auth_tag_len, ENC_NA); + /*offset += srtp_info->auth_tag_len;*/ + } + return; + + } if (p_packet_data && p_packet_data->bta2dp_info) { + tvbuff_t *nexttvb; + gint suboffset = 0; + + if (p_packet_data->bta2dp_info->content_protection_type == BTAVDTP_CONTENT_PROTECTION_TYPE_SCMS_T) { + nexttvb = tvb_new_subset_length(newtvb, 0, 1); + call_dissector(bta2dp_content_protection_header_scms_t, nexttvb, pinfo, tree); + suboffset = 1; + } + + nexttvb = tvb_new_subset_remaining(newtvb, suboffset); + if (p_packet_data->bta2dp_info->codec_dissector) + call_dissector_with_data(p_packet_data->bta2dp_info->codec_dissector, nexttvb, pinfo, tree, p_packet_data->bta2dp_info); + else + call_data_dissector(nexttvb, pinfo, tree); + + return; + + } if (p_packet_data && p_packet_data->btvdp_info) { + tvbuff_t *nexttvb; + gint suboffset = 0; + + if (p_packet_data->btvdp_info->content_protection_type == BTAVDTP_CONTENT_PROTECTION_TYPE_SCMS_T) { + nexttvb = tvb_new_subset_length(newtvb, 0, 1); + call_dissector(btvdp_content_protection_header_scms_t, nexttvb, pinfo, tree); + suboffset = 1; + } + + nexttvb = tvb_new_subset_remaining(newtvb, suboffset); + if (p_packet_data->btvdp_info->codec_dissector) + call_dissector_with_data(p_packet_data->btvdp_info->codec_dissector, nexttvb, pinfo, tree, p_packet_data->btvdp_info); + else + call_data_dissector(nexttvb, pinfo, tree); + + return; + } + + rtp_data = proto_tree_add_item(rtp_tree, hf_rtp_data, newtvb, 0, -1, ENC_NA); + + /* We have checked for !p_conv_data->bta2dp_info && !p_conv_data->btvdp_info above*/ + if (p_packet_data && payload_type >= PT_UNDF_96 && payload_type <= PT_UNDF_127) { + /* if the payload type is dynamic, we check if the conv is set and we look for the pt definition */ + if (p_packet_data->rtp_dyn_payload) { + const gchar *payload_type_str = rtp_dyn_payload_get_name(p_packet_data->rtp_dyn_payload, payload_type); + if (payload_type_str) { + int len; + len = dissector_try_string(rtp_dyn_pt_dissector_table, + payload_type_str, newtvb, pinfo, tree, rtp_info); + /* If payload type string set from conversation and + * no matching dissector found it's probably because no subdissector + * exists. Don't call the dissectors based on payload number + * as that'd probably be the wrong dissector in this case. + * Just add it as data. + */ + if (len > 0) + proto_item_set_hidden(rtp_data); + return; + } + } + } + + /* if we don't found, it is static OR could be set static from the preferences */ + if (dissector_try_uint_new(rtp_pt_dissector_table, payload_type, newtvb, pinfo, tree, TRUE, rtp_info)) + proto_item_set_hidden(rtp_data); +} + +/* Rtp payload reassembly + * + * This handles the reassembly of PDUs for higher-level protocols. + * + * We're a bit limited on how we can cope with out-of-order packets, because + * we don't have any idea of where the datagram boundaries are. So if we see + * packets A, C, B (all of which comprise a single datagram), we cannot know + * that C should be added to the same datagram as A, until we come to B (which + * may or may not actually be present...). + * + * What we end up doing in this case is passing A+B to the subdissector as one + * datagram, and make out that a new one starts on C. + */ +static void +dissect_rtp_data(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, + proto_tree *rtp_tree, int offset, unsigned int data_len, + unsigned int data_reported_len, unsigned int payload_type, + struct _rtp_info *rtp_info) +{ + tvbuff_t *newtvb; + struct _rtp_packet_info *p_packet_data; + gboolean must_desegment = FALSE; + rtp_private_conv_info *finfo = NULL; + rtp_multisegment_pdu *msp; + guint32 seqno; + guint16 save_can_desegment; + + /* Retrieve RTPs idea of a conversation */ + p_packet_data = (struct _rtp_packet_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_rtp, RTP_CONVERSATION_PROTO_DATA); + + if(p_packet_data != NULL) + finfo = p_packet_data->rtp_conv_info; + + if(finfo == NULL || !desegment_rtp) { + /* Hand the whole lot off to the subdissector */ + newtvb = tvb_new_subset_length_caplen(tvb, offset, data_len, data_reported_len); + process_rtp_payload(newtvb, pinfo, tree, rtp_tree, payload_type, rtp_info); + return; + } + + seqno = p_packet_data->extended_seqno; + + /* Preserve the current desegmentation ability in case this is + * RTP encapsulated in TCP (RFC 4571). + */ + save_can_desegment = pinfo->can_desegment; + pinfo->can_desegment = 2; + pinfo->desegment_offset = 0; + pinfo->desegment_len = 0; + +#ifdef DEBUG_FRAGMENTS + ws_debug("%d: RTP Part of convo %d(%p); seqno %d", + pinfo->num, + p_packet_data->frame_number, p_packet_data, + seqno + ); +#endif + + /* look for a pdu which we might be extending */ + msp = (rtp_multisegment_pdu *)wmem_tree_lookup32_le(finfo->multisegment_pdus, seqno-1); + + if(msp && msp->startseq < seqno && msp->endseq >= seqno) { + guint32 fid = msp->startseq; + fragment_head *fd_head; + +#ifdef DEBUG_FRAGMENTS + ws_debug("\tContinues fragment %d", fid); +#endif + + /* we always assume the datagram is complete; if this is the + * first pass, that's our best guess, and if it's not, what we + * say gets ignored anyway. + */ + fd_head = fragment_add_seq(&rtp_reassembly_table, + tvb, offset, pinfo, fid, NULL, + seqno-msp->startseq, data_len, + FALSE, 0); + + newtvb = process_reassembled_data(tvb, offset, pinfo, "Reassembled RTP", fd_head, + &rtp_fragment_items, NULL, tree); + +#ifdef DEBUG_FRAGMENTS + ws_debug("\tFragment Coalesced; fd_head=%p, newtvb=%p (len %d)", fd_head, newtvb, + newtvb?tvb_reported_length(newtvb):0); +#endif + + if(newtvb != NULL) { + /* Hand off to the subdissector */ + process_rtp_payload(newtvb, pinfo, tree, rtp_tree, payload_type, rtp_info); + + /* + * Check to see if there were any complete fragments within the chunk + */ + if( pinfo->desegment_len ) + { + if (pinfo->desegment_offset == 0) { +#ifdef DEBUG_FRAGMENTS + ws_debug("\tNo complete pdus in payload" ); +#endif + /* Mark the fragments as not complete yet */ + fragment_set_partial_reassembly(&rtp_reassembly_table, + pinfo, fid, NULL); + + /* we must need another segment */ + msp->endseq = MIN(msp->endseq, seqno) + 1; + + } + + /* the higher-level dissector has asked for some more data - ie, + the end of this segment does not coincide with the end of a + higher-level PDU. */ + must_desegment = TRUE; + } + + } + + } + else + { + /* + * The segment is not the continuation of a fragmented segment + * so process it as normal + */ +#ifdef DEBUG_FRAGMENTS + ws_debug("\tRTP non-fragment payload"); +#endif + newtvb = tvb_new_subset_length_caplen( tvb, offset, data_len, data_reported_len ); + + /* Hand off to the subdissector */ + process_rtp_payload(newtvb, pinfo, tree, rtp_tree, payload_type, rtp_info); + + if(pinfo->desegment_len) { + /* the higher-level dissector has asked for some more data - ie, + the end of this segment does not coincide with the end of a + higher-level PDU. */ + must_desegment = TRUE; + } + } + + /* + * There were bytes left over that the higher protocol couldn't dissect so save them + */ + if(must_desegment) + { + guint32 deseg_offset = pinfo->desegment_offset; + guint32 frag_len = tvb_reported_length_remaining(newtvb, deseg_offset); + fragment_head *fd_head; + +#ifdef DEBUG_FRAGMENTS + ws_debug("\tRTP Must Desegment: tvb_len=%d ds_len=%d %d frag_len=%d ds_off=%d", + tvb_reported_length(newtvb), + pinfo->desegment_len, + pinfo->fd->visited, + frag_len, + deseg_offset); +#endif + /* allocate a new msp for this pdu */ + if (!PINFO_FD_VISITED(pinfo)) { + msp = wmem_new(wmem_file_scope(), rtp_multisegment_pdu); + msp->startseq = seqno; + msp->endseq = seqno+1; + wmem_tree_insert32(finfo->multisegment_pdus, seqno, msp); + } + + /* + * Add the fragment to the fragment table + */ + fd_head = fragment_add_seq(&rtp_reassembly_table, + newtvb, deseg_offset, pinfo, seqno, NULL, 0, frag_len, + TRUE, 0); + + if(fd_head != NULL) + { + if( fd_head->reassembled_in != 0 && !(fd_head->flags & FD_PARTIAL_REASSEMBLY) ) + { + proto_item *rtp_tree_item; + rtp_tree_item = proto_tree_add_uint( tree, hf_rtp_reassembled_in, + newtvb, deseg_offset, tvb_reported_length_remaining(newtvb, deseg_offset), + fd_head->reassembled_in); + proto_item_set_generated(rtp_tree_item); +#ifdef DEBUG_FRAGMENTS + ws_debug("\tReassembled in %d", fd_head->reassembled_in); +#endif + } + else if (fd_head->reassembled_in == 0) + { +#ifdef DEBUG_FRAGMENTS + ws_debug("\tUnfinished fragment"); +#endif + /* this fragment is never reassembled */ + proto_tree_add_expert(tree, pinfo, &ei_rtp_fragment_unfinished, tvb, deseg_offset, -1); + } + } + else + { + /* + * This fragment was the first fragment in a new entry in the + * frag_table; we don't yet know where it is reassembled + */ +#ifdef DEBUG_FRAGMENTS + ws_debug("\tnew pdu"); +#endif + } + + if( pinfo->desegment_offset == 0 ) + { + col_set_str(pinfo->cinfo, COL_PROTOCOL, "RTP"); + col_set_str(pinfo->cinfo, COL_INFO, "[RTP segment of a reassembled PDU]"); + } + } + + /* Restore desegmentation ability */ + pinfo->can_desegment = save_can_desegment; + pinfo->desegment_offset = 0; + pinfo->desegment_len = 0; +} + +static int +dissect_rtp_rfc2198(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data) +{ + volatile gint offset = 0; + int cnt; + gboolean hdr_follow = TRUE; + proto_tree *rfc2198_tree; + rfc2198_hdr *hdr_last; + rfc2198_hdr *hdr_chain = NULL; + struct _rtp_packet_info *p_packet_data; + struct _rtp_info* rtp_info = NULL; + struct _rtp_info rfc2198_rtp_info; + volatile unsigned rtp_info_offset = 0; + + if (data) { + rtp_info = (struct _rtp_info*)data; + rfc2198_rtp_info = *rtp_info; + rtp_info_offset = rtp_info->info_payload_offset; + } + + /* Retrieve RTPs idea of a conversation */ + p_packet_data = (struct _rtp_packet_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_rtp, RTP_CONVERSATION_PROTO_DATA); + + /* Add try to RFC 2198 data */ + rfc2198_tree = proto_tree_add_subtree(tree, tvb, offset, -1, ett_rtp_rfc2198, NULL, "RFC 2198: Redundant Audio Data"); + + hdr_last = NULL; + cnt = 0; + while (hdr_follow) { + proto_item *ti; + proto_tree *rfc2198_hdr_tree; + const gchar *payload_type_str; + rfc2198_hdr *hdr_new; + guint8 octet1; + + cnt++; + payload_type_str = NULL; + + /* Allocate and fill in header */ + hdr_new = wmem_new0(pinfo->pool, rfc2198_hdr); + hdr_new->next = NULL; + octet1 = tvb_get_guint8(tvb, offset); + hdr_new->pt = RTP_PAYLOAD_TYPE(octet1); + hdr_follow = (octet1 & 0x80); + + /* Save the payload type for Decode As */ + p_add_proto_data(pinfo->pool, pinfo, proto_rtp, RTP_DECODE_AS_PROTO_DATA, GUINT_TO_POINTER(hdr_new->pt)); + + /* if it is dynamic payload, let use the conv data to see if it is defined */ + if ((hdr_new->pt > 95) && (hdr_new->pt < 128)) { + if (p_packet_data && p_packet_data->rtp_dyn_payload){ + rtp_dyn_payload_get_full(p_packet_data->rtp_dyn_payload, hdr_new->pt, &payload_type_str, &hdr_new->payload_rate, &hdr_new->payload_channels, &hdr_new->payload_fmtp_map); + hdr_new->payload_type_str = payload_type_str; + } else { + /* See if we have a dissector tied to the dynamic payload + * through preferences / Decode As */ + dissector_handle_t pt_dissector_handle; + + pt_dissector_handle = dissector_get_uint_handle(rtp_pt_dissector_table, hdr_new->pt); + if (pt_dissector_handle) { + hdr_new->payload_type_str = dissector_handle_get_dissector_name(pt_dissector_handle); + } + } + } + /* Add a subtree for this header and add items */ + rfc2198_hdr_tree = proto_tree_add_subtree_format(rfc2198_tree, tvb, offset, (hdr_follow)?4:1, + ett_rtp_rfc2198_hdr, &ti, "Header %u", cnt); + proto_tree_add_item(rfc2198_hdr_tree, hf_rtp_rfc2198_follow, tvb, offset, 1, ENC_BIG_ENDIAN ); + proto_tree_add_uint_format_value(rfc2198_hdr_tree, hf_rtp_payload_type, tvb, + offset, 1, octet1, "%s (%u)", + payload_type_str ? payload_type_str : val_to_str_ext_const(hdr_new->pt, &rtp_payload_type_vals_ext, "Unknown"), + hdr_new->pt); + proto_item_append_text(ti, ": PT=%s", + payload_type_str ? payload_type_str : + val_to_str_ext(hdr_new->pt, &rtp_payload_type_vals_ext, "Unknown (%u)")); + offset += 1; + + /* Timestamp offset and block length don't apply to last header */ + if (hdr_follow) { + proto_tree_add_item(rfc2198_hdr_tree, hf_rtp_rfc2198_tm_off, tvb, offset, 2, ENC_BIG_ENDIAN ); + proto_tree_add_item(rfc2198_hdr_tree, hf_rtp_rfc2198_bl_len, tvb, offset + 1, 2, ENC_BIG_ENDIAN ); + hdr_new->len = tvb_get_ntohs(tvb, offset + 1) & 0x03FF; + proto_item_append_text(ti, ", len=%u", hdr_new->len); + offset += 3; + } else { + hdr_new->len = -1; + hdr_follow = FALSE; + } + + if (hdr_last) { + hdr_last->next = hdr_new; + } else { + hdr_chain = hdr_new; + } + hdr_last = hdr_new; + } + + /* Dissect each data block according to the header info */ + hdr_last = hdr_chain; + while (hdr_last) { + hdr_last->offset = offset; + if (!hdr_last->next) { + hdr_last->len = tvb_reported_length_remaining(tvb, offset); + } + if (rtp_info) { + rfc2198_rtp_info.info_payload_offset = rtp_info_offset + hdr_last->offset; + rfc2198_rtp_info.info_payload_len = hdr_last->len; + rfc2198_rtp_info.info_payload_type = hdr_last->pt; + rfc2198_rtp_info.info_payload_type_str = hdr_last->payload_type_str; + rfc2198_rtp_info.info_payload_rate = hdr_last->payload_rate; + rfc2198_rtp_info.info_payload_channels = hdr_last->payload_channels; + rfc2198_rtp_info.info_payload_fmtp_map = hdr_last->payload_fmtp_map; + } + const char *saved_proto = pinfo->current_proto; + TRY { + dissect_rtp_data(tvb, pinfo, tree, rfc2198_tree, hdr_last->offset, hdr_last->len, hdr_last->len, hdr_last->pt, &rfc2198_rtp_info); + } + CATCH_NONFATAL_ERRORS { + show_exception(tvb, pinfo, rfc2198_tree, EXCEPT_CODE, GET_MESSAGE); + pinfo->current_proto = saved_proto; + } + ENDTRY; + if (rtp_info && rfc2198_deencapsulate && !hdr_last->next) { + /* Set the payload for the tap to that of the primary encoding + * to remove the RFC 2198 encapsulation. (Since this is the + * last encoding in the packet, the calculated length includes + * the padding and padding stays the same.) + * Ideally we should process the redundant encoding or FEC, + * but just treating the primary encoding as the only payload + * for the tap is closer than doing nothing, and at least has + * some chance of playing or saving the primary media payload. + * + * XXX: WebRTC/Chromium, when using RED with ULPFEC (RFC 5109), + * violates the RFCs by having the FEC set in separate packets + * as a different primary encoding (using duplicate sequence + * numbers already used by the video.) This is done because of + * a concern that the combined payload size of FEC plus video + * encodings like VP8 could push a packet over the MTU size, + * also a problem. + * See RFC 8872 3.2.4 "RTP Payload Type" and Appendix A + * "Dismissing Payload Type Multiplexing," also + * https://bugs.chromium.org/p/webrtc/issues/detail?id=9188 + * https://bugs.chromium.org/p/webrtc/issues/detail?id=12530 + * https://bugs.chromium.org/p/webrtc/issues/detail?id=1467 + * However, since duplicate sequence numbers as used, a user + * Ignoring all the FEC packets could be a workaround. + * RFC 2198 in WebRTC/Chromium with actual redundant audio is + * RFC-compliant, though: + * https://bugs.chromium.org/p/webrtc/issues/detail?id=11640 + */ + *rtp_info = rfc2198_rtp_info; + } + offset += hdr_last->len; + hdr_last = hdr_last->next; + } + return tvb_captured_length(tvb); +} + +static int +dissect_full_rfc4571(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data) +{ + /* rfc4571 packet frame + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + --------------------------------------------------------------- + | LENGTH | RTP or RTCP packet ... | + --------------------------------------------------------------- + */ + gint offset = 0; + guint32 length = 0; + proto_tree_add_item_ret_uint(tree, hf_rfc4571_header_len, tvb, offset, 2, ENC_NA, &length); + if (length == 0) { + return 2; + } + + offset += 2; + tvbuff_t *tvb_sub; + tvb_sub = tvb_new_subset_remaining(tvb, offset); + + dissect_rtp(tvb_sub, pinfo, tree, data); + return tvb_reported_length(tvb); +} + +static guint +get_rtp_rfc4571_len(packet_info *pinfo _U_, tvbuff_t *tvb, int offset, void *data _U_) +{ + guint16 rtp_length = tvb_get_ntohs(tvb, offset); /* length field is at the beginning, 2 bytes */ + return (guint)rtp_length + 2; /* plus the length field */ +} + +#define RTP_RFC4571_HEADER_LEN 2 + +static int +dissect_rtp_rfc4571(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data) +{ + tcp_dissect_pdus(tvb, pinfo, tree, TRUE, RTP_RFC4571_HEADER_LEN, + get_rtp_rfc4571_len, dissect_full_rfc4571, data); + return tvb_captured_length(tvb); +} + +static void +dissect_rtp_hext_rfc5285_onebyte( tvbuff_t *tvb, packet_info *pinfo, + proto_tree *rtp_hext_tree ) +{ + proto_tree *rtp_hext_rfc5285_tree = NULL; + guint ext_offset = 0; + + while (ext_offset < tvb_captured_length (tvb)) { + guint8 ext_hdr_hdr; + guint8 ext_id; + guint8 ext_length; + guint start_ext_offset; + tvbuff_t *subtvb; + + /* Skip bytes with the value 0, they are padding */ + start_ext_offset = ext_offset; + while (tvb_get_guint8 (tvb, ext_offset) == 0) { + ext_offset ++; + if (ext_offset >= tvb_captured_length (tvb)) + return; + } + + /* Add padding */ + if (ext_offset > start_ext_offset) + proto_tree_add_item(rtp_hext_tree, hf_rtp_padding_data, tvb, start_ext_offset, ext_offset-start_ext_offset, ENC_NA ); + + ext_hdr_hdr = tvb_get_guint8 (tvb, ext_offset); + ext_id = ext_hdr_hdr >> 4; + + /* 15 is for future extensibility, ignore length, etc and stop processing packet if it shows up */ + if (ext_id == 15) + return; + + ext_length = (ext_hdr_hdr & 0x0F) + 1; + + /* Exit on malformed extension headers */ + if (ext_offset + ext_length + 1 > tvb_captured_length (tvb)) { + return; + } + + if (rtp_hext_tree) { + rtp_hext_rfc5285_tree = proto_tree_add_subtree(rtp_hext_tree, tvb, ext_offset, ext_length + 1, + ett_hdr_ext_rfc5285, NULL, "RFC 5285 Header Extension (One-Byte Header)"); + + proto_tree_add_uint( rtp_hext_rfc5285_tree, hf_rtp_ext_rfc5285_id, tvb, ext_offset, 1, ext_id); + proto_tree_add_uint( rtp_hext_rfc5285_tree, hf_rtp_ext_rfc5285_length, tvb, ext_offset, 1, ext_length); + } + ext_offset ++; + + subtvb = tvb_new_subset_length(tvb, ext_offset, ext_length); + if (!dissector_try_uint (rtp_hdr_ext_rfc5285_dissector_table, ext_id, subtvb, pinfo, rtp_hext_rfc5285_tree)) { + if (rtp_hext_tree) + proto_tree_add_item(rtp_hext_rfc5285_tree, hf_rtp_ext_rfc5285_data, subtvb, 0, ext_length, ENC_NA ); + } + + ext_offset += ext_length; + } +} + + +static void +dissect_rtp_hext_rfc5285_twobytes(tvbuff_t *parent_tvb, guint id_offset, + guint8 id, tvbuff_t *tvb, packet_info *pinfo, proto_tree *rtp_hext_tree) +{ + proto_tree *rtp_hext_rfc5285_tree = NULL; + guint ext_offset = 0, start_ext_offset; + + while (ext_offset + 2 < tvb_captured_length (tvb)) { + guint8 ext_id; + guint8 ext_length; + tvbuff_t *subtvb; + + /* Skip bytes with the value 0, they are padding */ + start_ext_offset = ext_offset; + while (tvb_get_guint8 (tvb, ext_offset) == 0) { + if (ext_offset + 2 >= tvb_captured_length (tvb)) + return; + ext_offset ++; + } + /* Add padding */ + if (ext_offset > start_ext_offset) + proto_tree_add_item(rtp_hext_tree, hf_rtp_padding_data, tvb, start_ext_offset, ext_offset-start_ext_offset, ENC_NA ); + + ext_id = tvb_get_guint8 (tvb, ext_offset); + ext_length = tvb_get_guint8 (tvb, ext_offset + 1); + + if (rtp_hext_tree) { + rtp_hext_rfc5285_tree = proto_tree_add_subtree(rtp_hext_tree, tvb, ext_offset, ext_length + 2, + ett_hdr_ext_rfc5285, NULL, "RFC 5285 Header Extension (Two-Byte Header)"); + + proto_tree_add_uint( rtp_hext_rfc5285_tree, hf_rtp_ext_rfc5285_appbits, parent_tvb, id_offset + 1, 1, id & 0x000F); + proto_tree_add_uint( rtp_hext_rfc5285_tree, hf_rtp_ext_rfc5285_id, tvb, ext_offset, 1, ext_id); + proto_tree_add_uint( rtp_hext_rfc5285_tree, hf_rtp_ext_rfc5285_length, tvb, ext_offset + 1, 1, ext_length); + } + + ext_offset += 2; + + subtvb = tvb_new_subset_length(tvb, ext_offset, ext_length); + if (ext_length && !dissector_try_uint (rtp_hdr_ext_rfc5285_dissector_table, ext_id, subtvb, pinfo, rtp_hext_rfc5285_tree)) { + proto_tree_add_item(rtp_hext_rfc5285_tree, hf_rtp_ext_rfc5285_data, subtvb, 0, ext_length, ENC_NA ); + } + + ext_offset += ext_length; + } +} + +static gint +dissect_rtp( tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ + proto_item *ti = NULL; + proto_tree *volatile rtp_tree = NULL; + guint8 octet1, octet2; + unsigned int version; + gboolean padding_set; + gboolean extension_set; + unsigned int csrc_count; + gboolean marker_set; + unsigned int payload_type; + const gchar *payload_type_str = NULL; + gboolean is_srtp = FALSE; + unsigned int i; + gint length, reported_length; + int data_len; + volatile unsigned int offset = 0; + guint16 seq_num; + guint32 timestamp; + guint32 sync_src; + struct _rtp_packet_info *p_packet_data; + /*struct srtp_info *srtp_info = NULL;*/ + /*unsigned int srtp_offset;*/ + const char *pt = NULL; + struct _rtp_info *rtp_info; + static int * const octet1_fields[] = { + &hf_rtp_version, + &hf_rtp_padding, + &hf_rtp_extension, + &hf_rtp_csrc_count, + NULL + }; + + /* Get the fields in the first octet */ + octet1 = tvb_get_guint8( tvb, offset ); + version = RTP_VERSION( octet1 ); + + /* RFC 7983 gives current best practice in demultiplexing RTP packets: + * Examine the first byte of the packet: + * +----------------+ + * | [0..3] -+--> forward to STUN + * | | + * | [16..19] -+--> forward to ZRTP + * | | + * packet --> | [20..63] -+--> forward to DTLS + * | | + * | [64..79] -+--> forward to TURN Channel + * | | + * | [128..191] -+--> forward to RTP/RTCP + * +----------------+ + * + * DTLS-SRTP MUST support multiplexing of DTLS and RTP over the same + * port pair (RFCs 5764, 8835), and this frequently occurs after SDP + * has been used to set up a RTP conversation and set the conversation + * dissector RTP. In addition, STUN packets sharing one port are common + * as well. + * + * In practice, RTP0_INVALID rejects packets and lets heuristic dissectors + * take a look. The STUN, ZRTP, and DTLS heuristic dissectors are all + * enabled by default so out of the box it more or less looks correct - at + * least on the second pass, on tshark there's incorrect RTP information in + * the tree. However, the STUN heuristic dissector can change the + * dissector for the conversation to itself (the non-heuristic dissector + * does not), see #18832, and TURN ChannelData messages are impossible to + * heuristically detect. + */ + if (global_rtp_version0_type == RTP0_RFC7983) { + switch (version) { + case 0: + if (octet1 < 4) { + call_dissector(stun_handle, tvb, pinfo, tree); + return tvb_captured_length(tvb); + } else if ((octet1 & 0xfc) == 0x10) { + call_dissector(zrtp_handle, tvb,pinfo, tree); + return tvb_captured_length(tvb); + } else if (octet1 > 19) { + call_dissector(dtls_handle, tvb,pinfo, tree); + return tvb_captured_length(tvb); + } + break; + case 1: + if (octet1 < 80) { + /* The STUN dissector will dissect TURN ChannelData + * XXX: Maybe we should call the turnchannel dissector? + * + * Should we be assuming we have TURN ChannelData for + * the RTP0_STUN and option too? + */ + call_dissector(stun_handle, tvb, pinfo, tree); + return tvb_captured_length(tvb); + } + break; + case 3: + if (octet1 == 0xFF) { + if (tvb_get_guint8( tvb, offset + 1 ) == 0x10) { + /* Special MS-TURN Multiplexed TURN Channel */ + call_dissector(stun_handle, tvb, pinfo, tree); + return tvb_captured_length(tvb); + } + } + /* FALLTHROUGH */ + case 2: + default: + break; + } + } else if (version == 0) { + switch (global_rtp_version0_type) { + case RTP0_STUN: + call_dissector(stun_handle, tvb, pinfo, tree); + return tvb_captured_length(tvb); + case RTP0_CLASSICSTUN: + call_dissector(classicstun_handle, tvb, pinfo, tree); + return tvb_captured_length(tvb); + + case RTP0_T38: + call_dissector(t38_handle, tvb, pinfo, tree); + return tvb_captured_length(tvb); + + case RTP0_SPRT: + call_dissector(sprt_handle, tvb, pinfo, tree); + return tvb_captured_length(tvb); + + case RTP0_INVALID: + if (!(tvb_memeql(tvb, 4, (const guint8*)"ZRTP", 4))) + { + call_dissector(zrtp_handle, tvb,pinfo, tree); + return tvb_captured_length(tvb); + } + default: + ; /* Unknown or unsupported version (let it fall through) */ + } + } + + /* fill in the rtp_info structure */ + rtp_info = wmem_new0(pinfo->pool, struct _rtp_info); + rtp_info->info_version = version; + if (version != 2) { + /* + * Unknown or unsupported version. + */ + col_set_str(pinfo->cinfo, COL_PROTOCOL, "RTP"); + + col_add_fstr( pinfo->cinfo, COL_INFO, + "Unknown RTP version %u", version); + + if ( tree ) { + ti = proto_tree_add_item( tree, proto_rtp, tvb, offset, -1, ENC_NA ); + rtp_tree = proto_item_add_subtree( ti, ett_rtp ); + + proto_tree_add_uint( rtp_tree, hf_rtp_version, tvb, + offset, 1, octet1); + } + /* XXX: Offset is zero here, so in practice this rejects the packet + * and lets heuristic dissectors make an attempt, though after + * adding entries to the tree (at least on a first pass in tshark.) + */ + return offset; + } + + padding_set = RTP_PADDING( octet1 ); + extension_set = RTP_EXTENSION( octet1 ); + csrc_count = RTP_CSRC_COUNT( octet1 ); + + /* Get the fields in the second octet */ + octet2 = tvb_get_guint8( tvb, offset + 1 ); + marker_set = RTP_MARKER( octet2 ); + payload_type = RTP_PAYLOAD_TYPE( octet2 ); + + /* Save the payload type for Decode As */ + p_add_proto_data(pinfo->pool, pinfo, proto_rtp, RTP_DECODE_AS_PROTO_DATA, GUINT_TO_POINTER(payload_type)); + + if (marker_set && payload_type >= FIRST_RTCP_CONFLICT_PAYLOAD_TYPE && payload_type <= LAST_RTCP_CONFLICT_PAYLOAD_TYPE) { + call_dissector(rtcp_handle, tvb, pinfo, tree); + return tvb_captured_length(tvb); + } + + /* Get the subsequent fields */ + seq_num = tvb_get_ntohs( tvb, offset + 2 ); + timestamp = tvb_get_ntohl( tvb, offset + 4 ); + sync_src = tvb_get_ntohl( tvb, offset + 8 ); + + /* fill in the rtp_info structure */ + rtp_info->info_padding_set = padding_set; + rtp_info->info_marker_set = marker_set; + rtp_info->info_media_types = 0; + rtp_info->info_payload_type = payload_type; + rtp_info->info_seq_num = seq_num; + rtp_info->info_extended_seq_num = seq_num; /* initial with seq_number */ + rtp_info->info_timestamp = timestamp; + rtp_info->info_extended_timestamp = timestamp; /* initial with timestamp */ + rtp_info->info_sync_src = sync_src; + rtp_info->info_is_srtp = FALSE; + rtp_info->info_setup_frame_num = 0; + rtp_info->info_payload_type_str = NULL; + rtp_info->info_payload_rate = 0; + rtp_info->info_payload_fmtp_map = NULL; + rtp_info->info_is_ed137 = FALSE; + rtp_info->info_ed137_info = NULL; + + /* + * Do we have all the data? + */ + length = tvb_captured_length_remaining(tvb, offset); + reported_length = tvb_reported_length_remaining(tvb, offset); + if (reported_length >= 0 && length >= reported_length) { + /* + * Yes. + */ + rtp_info->info_all_data_present = TRUE; + rtp_info->info_data_len = reported_length; + + /* + * Save the pointer to raw rtp data (header + payload incl. + * padding). + * That should be safe because the "epan_dissect_t" + * constructed for the packet has not yet been freed when + * the taps are called. + * (Destroying the "epan_dissect_t" will end up freeing + * all the tvbuffs and hence invalidating pointers to + * their data.) + * See "add_packet_to_packet_list()" for details. + */ + rtp_info->info_data = tvb_get_ptr(tvb, 0, -1); + } else { + /* + * No - packet was cut short at capture time. + */ + rtp_info->info_all_data_present = FALSE; + rtp_info->info_data_len = 0; + rtp_info->info_data = NULL; + } + + /* Look for conv and add to the frame if found */ + + p_packet_data = get_rtp_packet_info(pinfo, rtp_info); + + if (p_packet_data && p_packet_data->srtp_info) is_srtp = TRUE; + rtp_info->info_is_srtp = is_srtp; + + col_set_str( pinfo->cinfo, COL_PROTOCOL, (is_srtp) ? "SRTP" : "RTP" ); + +#if 0 /* XXX: srtp_offset never actually used ?? */ + /* check if this is added as an SRTP stream - if so, don't try to dissect the payload data for now */ + if (p_conv_data && p_conv_data->srtp_info) { + srtp_info = p_conv_data->srtp_info; + if (rtp_info->info_all_data_present) { + srtp_offset = rtp_info->info_data_len - srtp_info->mki_len - srtp_info->auth_tag_len; + } + } +#endif + + if (p_packet_data && p_packet_data->bta2dp_info && p_packet_data->bta2dp_info->codec_dissector) { + rtp_info->info_payload_type_str = (const char *) dissector_handle_get_protocol_short_name(p_packet_data->bta2dp_info->codec_dissector); + } else if (p_packet_data && p_packet_data->btvdp_info && p_packet_data->btvdp_info->codec_dissector) { + rtp_info->info_payload_type_str = (const char *) dissector_handle_get_protocol_short_name(p_packet_data->btvdp_info->codec_dissector); + } + + /* if it is dynamic payload, let use the conv data to see if it is defined */ + if ( (payload_type>95) && (payload_type<128) ) { + if (p_packet_data && p_packet_data->rtp_dyn_payload) { + int sample_rate = 0; + unsigned channels = 1; + wmem_map_t *fmtp_map; + +#ifdef DEBUG_CONVERSATION + rtp_dump_dyn_payload(p_conv_data->rtp_dyn_payload); +#endif + DPRINT(("looking up conversation data for dyn_pt=%d", payload_type)); + + if (rtp_dyn_payload_get_full(p_packet_data->rtp_dyn_payload, payload_type, + &payload_type_str, &sample_rate, + &channels, &fmtp_map)) { + DPRINT(("found conversation data for dyn_pt=%d, enc_name=%s", + payload_type, payload_type_str)); + rtp_info->info_payload_type_str = payload_type_str; + rtp_info->info_payload_rate = sample_rate; + rtp_info->info_payload_channels = channels; + rtp_info->info_payload_fmtp_map = fmtp_map; + } + } else { + /* See if we have a dissector tied to the dynamic payload trough preferences*/ + dissector_handle_t pt_dissector_handle; + const char *name; + + pt_dissector_handle = dissector_get_uint_handle(rtp_pt_dissector_table, payload_type); + if (pt_dissector_handle) { + name = dissector_handle_get_dissector_name(pt_dissector_handle); + if (name) { + rtp_info->info_payload_type_str = name; + } + } + } + } + + if (p_packet_data && p_packet_data->bta2dp_info) { + pt = (p_packet_data->bta2dp_info->codec_dissector) ? dissector_handle_get_protocol_short_name(p_packet_data->bta2dp_info->codec_dissector) : "Unknown"; + } else if (p_packet_data && p_packet_data->btvdp_info) { + pt = (p_packet_data->btvdp_info->codec_dissector) ? dissector_handle_get_protocol_short_name(p_packet_data->btvdp_info->codec_dissector) : "Unknown"; + } else { + pt = (payload_type_str ? payload_type_str : val_to_str_ext(payload_type, &rtp_payload_type_vals_ext, "Unknown (%u)")); + } + + col_add_fstr( pinfo->cinfo, COL_INFO, + "PT=%s, SSRC=0x%X, Seq=%u, Time=%u%s", + pt, + sync_src, + seq_num, + timestamp, + marker_set ? ", Mark" : ""); + + if ( tree ) { + proto_tree *item; + /* Create RTP protocol tree */ + ti = proto_tree_add_item(tree, proto_rtp, tvb, offset, -1, ENC_NA ); + rtp_tree = proto_item_add_subtree(ti, ett_rtp ); + + /* Conversation setup info */ + if (global_rtp_show_setup_info) + { + show_setup_info(tvb, pinfo, rtp_tree); + } + + proto_tree_add_bitmask_list(rtp_tree, tvb, offset, 1, octet1_fields, ENC_NA); + offset++; + + proto_tree_add_boolean( rtp_tree, hf_rtp_marker, tvb, offset, + 1, octet2 ); + + proto_tree_add_uint_format( rtp_tree, hf_rtp_payload_type, tvb, + offset, 1, octet2, "Payload type: %s (%u)", pt, payload_type); + + offset++; + + /* Sequence number 16 bits (2 octets) */ + proto_tree_add_uint( rtp_tree, hf_rtp_seq_nr, tvb, offset, 2, seq_num ); + if(p_packet_data != NULL) { + item = proto_tree_add_uint(rtp_tree, hf_rtp_ext_seq_nr, tvb, offset, 2, p_packet_data->extended_seqno); + proto_item_set_generated(item); + } + offset += 2; + + /* Timestamp 32 bits (4 octets) */ + proto_tree_add_uint( rtp_tree, hf_rtp_timestamp, tvb, offset, 4, timestamp ); + offset += 4; + + /* Synchronization source identifier 32 bits (4 octets) */ + proto_tree_add_uint( rtp_tree, hf_rtp_ssrc, tvb, offset, 4, sync_src ); + offset += 4; + } else { + offset += 12; + } + /* CSRC list*/ + if ( csrc_count > 0 ) { + proto_tree *rtp_csrc_tree; + guint32 csrc_item; + ti = proto_tree_add_item(rtp_tree, hf_rtp_csrc_items, tvb, offset, + csrc_count * 4, ENC_NA); + proto_item_append_text(ti, " (%u items)", csrc_count); + rtp_csrc_tree = proto_item_add_subtree( ti, ett_csrc_list ); + + for (i = 0; i < csrc_count; i++ ) { + csrc_item = tvb_get_ntohl( tvb, offset ); + proto_tree_add_uint_format( rtp_csrc_tree, + hf_rtp_csrc_item, tvb, offset, 4, + csrc_item, + "CSRC item %d: 0x%X", + i, csrc_item ); + offset += 4; + } + } + + /* Optional RTP header extension */ + if ( extension_set ) { + unsigned int hdr_extension_len; + unsigned int hdr_extension_id; + + /* Defined by profile field is 16 bits (2 octets) */ + hdr_extension_id = tvb_get_ntohs( tvb, offset ); + proto_tree_add_uint( rtp_tree, hf_rtp_prof_define, tvb, offset, 2, hdr_extension_id ); + offset += 2; + + hdr_extension_len = tvb_get_ntohs( tvb, offset ); + proto_tree_add_uint( rtp_tree, hf_rtp_length, tvb, offset, 2, hdr_extension_len); + offset += 2; + if ( hdr_extension_len > 0 ) { + proto_tree *rtp_hext_tree = NULL; + tvbuff_t *newtvb; + + ti = proto_tree_add_item(rtp_tree, hf_rtp_hdr_exts, tvb, offset, hdr_extension_len * 4, ENC_NA); + rtp_hext_tree = proto_item_add_subtree( ti, ett_hdr_ext ); + + /* pass interpretation of header extension to a registered subdissector */ + newtvb = tvb_new_subset_length(tvb, offset, hdr_extension_len * 4); + + if (hdr_extension_id == RTP_RFC5285_ONE_BYTE_SIG) { + dissect_rtp_hext_rfc5285_onebyte (newtvb, pinfo, rtp_hext_tree); + } + else if ((hdr_extension_id & RTP_RFC5285_TWO_BYTE_MASK) == RTP_RFC5285_TWO_BYTE_SIG) { + dissect_rtp_hext_rfc5285_twobytes(tvb, + offset - 4, hdr_extension_id, newtvb, + pinfo, rtp_hext_tree); + } + else { + if ( !(dissector_try_uint_new(rtp_hdr_ext_dissector_table, hdr_extension_id, newtvb, pinfo, rtp_hext_tree, FALSE, rtp_info)) ) { + unsigned int hdrext_offset; + + hdrext_offset = offset; + for ( i = 0; i < hdr_extension_len; i++ ) { + proto_tree_add_item( rtp_hext_tree, hf_rtp_hdr_ext, tvb, hdrext_offset, 4, ENC_BIG_ENDIAN ); + hdrext_offset += 4; + } + } + } + } + offset += hdr_extension_len * 4; + } + + if ( padding_set ) { + /* + * This RTP frame has padding - find it. + * + * The padding count is found in the LAST octet of + * the packet; it contains the number of octets + * that can be ignored at the end of the packet. + */ + volatile unsigned int padding_count; + if (tvb_captured_length(tvb) < tvb_reported_length(tvb)) { + /* + * We don't *have* the last octet of the + * packet, so we can't get the padding + * count. + * + * Put an indication of that into the + * tree, and just put in a raw data + * item. + */ + proto_tree_add_expert(rtp_tree, pinfo, &ei_rtp_padding_missing, tvb, 0, 0); + call_data_dissector(tvb_new_subset_remaining(tvb, offset), + pinfo, rtp_tree); + return tvb_captured_length(tvb); + } + + padding_count = tvb_get_guint8( tvb, + tvb_reported_length( tvb ) - 1 ); + data_len = + tvb_reported_length_remaining( tvb, offset ) - padding_count; + + rtp_info->info_payload_offset = offset; + + if (p_packet_data && p_packet_data->bta2dp_info) { + if (p_packet_data->bta2dp_info->codec_dissector == sbc_handle) { + rtp_info->info_payload_offset += 1; + } + + if (p_packet_data->bta2dp_info->content_protection_type == BTAVDTP_CONTENT_PROTECTION_TYPE_SCMS_T) { + rtp_info->info_payload_offset += 1; + } + } + + if (p_packet_data && p_packet_data->btvdp_info && + p_packet_data->btvdp_info->content_protection_type == BTAVDTP_CONTENT_PROTECTION_TYPE_SCMS_T) { + rtp_info->info_payload_offset += 1; + } + + rtp_info->info_payload_len = tvb_reported_length_remaining(tvb, rtp_info->info_payload_offset); + + if (rtp_info->info_payload_len > padding_count) { + rtp_info->info_payload_len -= padding_count; + } else { + rtp_info->info_payload_len = 0; + } + + if (data_len > 0) { + /* + * There's data left over when you take out + * the padding; dissect it. + */ + struct _rtp_pkt_info *rtp_pkt_info = wmem_new(pinfo->pool, struct _rtp_pkt_info); + + rtp_pkt_info->payload_len = data_len; + rtp_pkt_info->padding_len = padding_count - 1; + p_add_proto_data(pinfo->pool, pinfo, proto_rtp, pinfo->curr_layer_num, rtp_pkt_info); + + /* Ensure that tap is called after packet dissection, even in case of exception */ + TRY { + dissect_rtp_data( tvb, pinfo, tree, rtp_tree, + offset, + data_len, + data_len, + payload_type, + rtp_info); + } CATCH_ALL { + if (!pinfo->flags.in_error_pkt) + tap_queue_packet(rtp_tap, pinfo, rtp_info); + RETHROW; + } + ENDTRY; + offset += data_len; + } else if (data_len < 0) { + /* + * The padding count is bigger than the + * amount of RTP payload in the packet! + * Clip the padding count. + * + * XXX - put an item in the tree to indicate + * that the padding count is bogus? + */ + padding_count = + tvb_reported_length_remaining(tvb, offset); + } + if (padding_count > 1) { + /* + * There's more than one byte of padding; + * show all but the last byte as padding + * data. + */ + proto_tree_add_item( rtp_tree, hf_rtp_padding_data, + tvb, offset, padding_count - 1, ENC_NA ); + offset += padding_count - 1; + } + /* + * Show the last byte in the PDU as the padding + * count. + */ + proto_tree_add_item( rtp_tree, hf_rtp_padding_count, + tvb, offset, 1, ENC_BIG_ENDIAN ); + } + else { + /* + * No padding. + */ + rtp_info->info_payload_offset = offset; + rtp_info->info_payload_len = tvb_captured_length_remaining(tvb, offset); + + if (p_packet_data && p_packet_data->bta2dp_info) { + if (p_packet_data->bta2dp_info->codec_dissector == sbc_handle) { + rtp_info->info_payload_offset += 1; + rtp_info->info_payload_len -= 1; + } + + if (p_packet_data->bta2dp_info->content_protection_type == BTAVDTP_CONTENT_PROTECTION_TYPE_SCMS_T) { + rtp_info->info_payload_offset += 1; + rtp_info->info_payload_len -= 1; + } + } + + if (p_packet_data && p_packet_data->btvdp_info && + p_packet_data->btvdp_info->content_protection_type == BTAVDTP_CONTENT_PROTECTION_TYPE_SCMS_T) { + rtp_info->info_payload_offset += 1; + rtp_info->info_payload_len -= 1; + } + + if (tvb_reported_length_remaining(tvb, offset) > 0) { + struct _rtp_pkt_info *rtp_pkt_info = wmem_new(pinfo->pool, struct _rtp_pkt_info); + + rtp_pkt_info->payload_len = tvb_captured_length_remaining(tvb, offset); + rtp_pkt_info->padding_len = 0; + p_set_proto_data(pinfo->pool, pinfo, proto_rtp, pinfo->curr_layer_num, rtp_pkt_info); + + /* Ensure that tap is called after packet dissection, even in case of exception */ + TRY { + dissect_rtp_data( tvb, pinfo, tree, rtp_tree, offset, + tvb_captured_length_remaining( tvb, offset ), + tvb_reported_length_remaining( tvb, offset ), + payload_type, rtp_info); + } CATCH_ALL { + if (!pinfo->flags.in_error_pkt) + tap_queue_packet(rtp_tap, pinfo, rtp_info); + RETHROW; + } + ENDTRY; + } + } + if (!pinfo->flags.in_error_pkt) + tap_queue_packet(rtp_tap, pinfo, rtp_info); + + return offset; +} + +gint +dissect_rtp_shim_header(tvbuff_t *tvb, gint start, packet_info *pinfo _U_, proto_tree *tree, struct _rtp_info *rtp_info) +{ + proto_item *rtp_ti = NULL; + proto_tree *rtp_tree = NULL; + proto_item *ti; + guint8 octet1, octet2; + unsigned int version; + gboolean padding_set; + gboolean extension_set; + unsigned int csrc_count; + gboolean marker_set; + unsigned int payload_type; + unsigned int i; + gint offset = start; + guint16 seq_num; + guint32 timestamp; + guint32 sync_src; + const char *pt = NULL; + static int * const octet1_fields[] = { + &hf_rtp_version, + &hf_rtp_padding, + &hf_rtp_extension, + &hf_rtp_csrc_count, + NULL + }; + + /* Get the fields in the first octet */ + octet1 = tvb_get_guint8( tvb, offset ); + version = RTP_VERSION( octet1 ); + + /* fill in the rtp_info structure */ + if (rtp_info) rtp_info->info_version = version; + if (version != 2) { + /* + * Unknown or unsupported version. + */ + if ( tree ) { + ti = proto_tree_add_item( tree, proto_rtp, tvb, offset, 1, ENC_NA ); + rtp_tree = proto_item_add_subtree( ti, ett_rtp ); + + proto_tree_add_uint( rtp_tree, hf_rtp_version, tvb, + offset, 1, octet1); + } + return offset; + } + + padding_set = RTP_PADDING( octet1 ); + extension_set = RTP_EXTENSION( octet1 ); + csrc_count = RTP_CSRC_COUNT( octet1 ); + + /* Get the fields in the second octet */ + octet2 = tvb_get_guint8( tvb, offset + 1 ); + marker_set = RTP_MARKER( octet2 ); + payload_type = RTP_PAYLOAD_TYPE( octet2 ); + + /* Get the subsequent fields */ + seq_num = tvb_get_ntohs( tvb, offset + 2 ); + timestamp = tvb_get_ntohl( tvb, offset + 4 ); + sync_src = tvb_get_ntohl( tvb, offset + 8 ); + + /* fill in the rtp_info structure */ + if (rtp_info) { + rtp_info->info_padding_set = padding_set; + rtp_info->info_marker_set = marker_set; + rtp_info->info_media_types = 0; + rtp_info->info_payload_type = payload_type; + rtp_info->info_seq_num = seq_num; + rtp_info->info_timestamp = timestamp; + rtp_info->info_sync_src = sync_src; + rtp_info->info_data_len = 0; + rtp_info->info_all_data_present = FALSE; + rtp_info->info_payload_offset = 0; + rtp_info->info_payload_len = 0; + rtp_info->info_is_srtp = FALSE; + rtp_info->info_setup_frame_num = 0; + rtp_info->info_data = NULL; + rtp_info->info_payload_type_str = NULL; + rtp_info->info_payload_rate = 0; + rtp_info->info_payload_fmtp_map = NULL; + rtp_info->info_is_ed137 = FALSE; + rtp_info->info_ed137_info = NULL; + } + + if ( tree ) { + /* Create RTP protocol tree */ + rtp_ti = proto_tree_add_item(tree, proto_rtp, tvb, offset, 0, ENC_NA ); + rtp_tree = proto_item_add_subtree(rtp_ti, ett_rtp ); + + proto_tree_add_bitmask_list(rtp_tree, tvb, offset, 1, octet1_fields, ENC_NA); + offset++; + + proto_tree_add_boolean( rtp_tree, hf_rtp_marker, tvb, offset, + 1, octet2 ); + + pt = val_to_str_ext(payload_type, &rtp_payload_type_vals_ext, "Unknown (%u)"); + + proto_tree_add_uint_format( rtp_tree, hf_rtp_payload_type, tvb, + offset, 1, octet2, "Payload type: %s (%u)", pt, payload_type); + + offset++; + + /* Sequence number 16 bits (2 octets) */ + proto_tree_add_uint( rtp_tree, hf_rtp_seq_nr, tvb, offset, 2, seq_num ); + offset += 2; + + /* Timestamp 32 bits (4 octets) */ + proto_tree_add_uint( rtp_tree, hf_rtp_timestamp, tvb, offset, 4, timestamp ); + offset += 4; + + /* Synchronization source identifier 32 bits (4 octets) */ + proto_tree_add_uint( rtp_tree, hf_rtp_ssrc, tvb, offset, 4, sync_src ); + offset += 4; + } else { + offset += 12; + } + /* CSRC list*/ + if ( csrc_count > 0 ) { + proto_tree *rtp_csrc_tree; + guint32 csrc_item; + ti = proto_tree_add_item(rtp_tree, hf_rtp_csrc_items, tvb, offset, + csrc_count * 4, ENC_NA); + proto_item_append_text(ti, " (%u items)", csrc_count); + rtp_csrc_tree = proto_item_add_subtree( ti, ett_csrc_list ); + + for (i = 0; i < csrc_count; i++ ) { + csrc_item = tvb_get_ntohl( tvb, offset ); + proto_tree_add_uint_format( rtp_csrc_tree, + hf_rtp_csrc_item, tvb, offset, 4, + csrc_item, + "CSRC item %d: 0x%X", + i, csrc_item ); + offset += 4; + } + } + + /* Optional RTP header extension */ + if ( extension_set ) { + unsigned int hdr_extension_len; + unsigned int hdr_extension_id; + + /* Defined by profile field is 16 bits (2 octets) */ + hdr_extension_id = tvb_get_ntohs( tvb, offset ); + proto_tree_add_uint( rtp_tree, hf_rtp_prof_define, tvb, offset, 2, hdr_extension_id ); + offset += 2; + + hdr_extension_len = tvb_get_ntohs( tvb, offset ); + proto_tree_add_uint( rtp_tree, hf_rtp_length, tvb, offset, 2, hdr_extension_len); + offset += 2; + if ( hdr_extension_len > 0 ) { + proto_tree *rtp_hext_tree = NULL; + + ti = proto_tree_add_item(rtp_tree, hf_rtp_hdr_exts, tvb, offset, hdr_extension_len * 4, ENC_NA); + rtp_hext_tree = proto_item_add_subtree( ti, ett_hdr_ext ); + + for ( i = 0; i < hdr_extension_len; i++ ) { + proto_tree_add_item( rtp_hext_tree, hf_rtp_hdr_ext, tvb, offset, 4, ENC_BIG_ENDIAN ); + offset += 4; + } + } + } + + proto_item_set_len(rtp_ti, offset - start); + + return (offset - start); +} + +/* calculate the extended sequence number - top 16 bits of the previous sequence number, + * plus our own; then correct for wrapping */ +static guint32 +calculate_extended_seqno(guint32 previous_seqno, guint16 raw_seqno) +{ + guint32 seqno = (previous_seqno & 0xffff0000) | raw_seqno; + if (seqno + 0x8000 < previous_seqno) { + seqno += 0x10000; + } else if (previous_seqno + 0x8000 < seqno) { + /* we got an out-of-order packet which happened to go backwards over the + * wrap boundary */ + seqno -= 0x10000; + } + return seqno; +} + +/* calculate the extended sequence number - top 16 bits of the previous sequence number, + * plus our own; then correct for wrapping */ +static guint64 +calculate_extended_timestamp(guint64 previous_timestamp, guint32 raw_timestamp) +{ + guint64 timestamp = (previous_timestamp & 0xffffffff00000000) | raw_timestamp; + if (timestamp + 0x80000000 < previous_timestamp) { + timestamp += 0x100000000; + } else if (previous_timestamp + 0x80000000 < timestamp) { + /* we got an out-of-order packet which happened to go backwards over the + * wrap boundary */ + timestamp -= 0x100000000; + } + return timestamp; +} + +/* Look for conversation info */ +static struct _rtp_packet_info * +get_rtp_packet_info(packet_info *pinfo, struct _rtp_info *rtp_info) +{ + /* Conversation and current data */ + struct _rtp_packet_info *p_packet_data; + + /* Use existing packet info if available */ + p_packet_data = (struct _rtp_packet_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_rtp, RTP_CONVERSATION_PROTO_DATA); + + if (!p_packet_data) + { + conversation_t *p_conv; + + /* First time, get info from conversation */ + p_conv = find_conversation(pinfo->num, &pinfo->net_dst, &pinfo->net_src, + conversation_pt_to_conversation_type(pinfo->ptype), + pinfo->destport, pinfo->srcport, NO_ADDR_B); + if (!p_conv) { + /* Create a conversation in case none exists (decode as is used for marking the packet as RTP) */ + p_conv = conversation_new(pinfo->num, &pinfo->net_dst, &pinfo->net_src, + conversation_pt_to_conversation_type(pinfo->ptype), + pinfo->destport, pinfo->srcport, NO_ADDR2); + } + + /* Create space for packet info */ + struct _rtp_conversation_info *p_conv_data; + p_conv_data = (struct _rtp_conversation_info *)conversation_get_proto_data(p_conv, proto_rtp); + + if (!p_conv_data) { + /* Create conversation data. If RTP was set up by an SDP or by + * the heuristic dissector, conversation data should already + * have been created. Therefore, we should only reach this + * case if Decode As is being used (See Issue #18829). + */ + p_conv_data = wmem_new0(wmem_file_scope(), struct _rtp_conversation_info); + p_conv_data->ssrc_number_space = wmem_map_new(wmem_file_scope(), g_direct_hash, g_direct_equal); + p_conv_data->rtp_conv_info = wmem_new(wmem_file_scope(), rtp_private_conv_info); + p_conv_data->rtp_conv_info->multisegment_pdus = wmem_tree_new(wmem_file_scope()); + (void)g_strlcpy(p_conv_data->method, "DECODE AS", MAX_RTP_SETUP_METHOD_SIZE + 1); + p_conv_data->frame_number = pinfo->num; + p_conv_data->media_types = 0; + p_conv_data->srtp_info = NULL; + p_conv_data->bta2dp_info = NULL; + p_conv_data->btvdp_info = NULL; + conversation_add_proto_data(p_conv, proto_rtp, p_conv_data); + } + + guint32 seqno; + guint64 timestamp; + + /* Save this conversation info into packet info */ + /* This is file scoped because we only do this on the first pass. + * On nonsequential passes, the conversation data has the values + * from the last dissected frame, which is not necessarily the + * immediately previous frame. + */ + p_packet_data = wmem_new(wmem_file_scope(), struct _rtp_packet_info); + (void)g_strlcpy(p_packet_data->method, p_conv_data->method, MAX_RTP_SETUP_METHOD_SIZE + 1); + p_packet_data->frame_number = p_conv_data->frame_number; + p_packet_data->media_types = p_conv_data->media_types; + /* do not increment ref count for the rtp_dyn_payload */ + p_packet_data->rtp_dyn_payload = p_conv_data->rtp_dyn_payload; + p_packet_data->rtp_conv_info = p_conv_data->rtp_conv_info; + p_packet_data->srtp_info = p_conv_data->srtp_info; + p_packet_data->rtp_sdp_setup_info_list = p_conv_data->rtp_sdp_setup_info_list; + p_packet_data->bta2dp_info = p_conv_data->bta2dp_info; + p_packet_data->btvdp_info = p_conv_data->btvdp_info; + p_add_proto_data(wmem_file_scope(), pinfo, proto_rtp, RTP_CONVERSATION_PROTO_DATA, p_packet_data); + + rtp_number_space* number_space = wmem_map_lookup(p_conv_data->ssrc_number_space, GUINT_TO_POINTER(rtp_info->info_sync_src)); + if (number_space == NULL) { + /* Start the extended numbers up one cycle, to cope gracefully + with the first few packets being out of order. */ + number_space = wmem_new0(wmem_file_scope(), rtp_number_space); + number_space->extended_seqno = 0x10000; + number_space->extended_timestamp = 0x100000000; + wmem_map_insert(p_conv_data->ssrc_number_space, GUINT_TO_POINTER(rtp_info->info_sync_src), number_space); + } + /* calculate extended sequence number */ + seqno = calculate_extended_seqno(number_space->extended_seqno, + rtp_info->info_seq_num); + + p_packet_data->extended_seqno = seqno; + number_space->extended_seqno = seqno; + + /* calculate extended timestamp */ + timestamp = calculate_extended_timestamp(number_space->extended_timestamp, + rtp_info->info_timestamp); + + p_packet_data->extended_timestamp = timestamp; + number_space->extended_timestamp = timestamp; + } + rtp_info->info_setup_frame_num = p_packet_data->frame_number; + rtp_info->info_media_types = p_packet_data->media_types; + rtp_info->info_extended_seq_num = p_packet_data->extended_seqno; + rtp_info->info_extended_timestamp = p_packet_data->extended_timestamp; + return p_packet_data; +} + + +/* Display setup info */ +static void +show_setup_info(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) +{ + /* Conversation and current data */ + struct _rtp_packet_info *p_packet_data; + proto_tree *rtp_setup_tree; + proto_item *ti; + + /* Use existing packet info if available */ + p_packet_data = (struct _rtp_packet_info *)p_get_proto_data(wmem_file_scope(), pinfo, proto_rtp, RTP_CONVERSATION_PROTO_DATA); + + if (!p_packet_data) return; + + /* Create setup info subtree with summary info. */ + ti = proto_tree_add_string_format(tree, hf_rtp_setup, tvb, 0, 0, + "", "Stream setup by %s (frame %u)", + p_packet_data->method, + p_packet_data->frame_number); + proto_item_set_generated(ti); + rtp_setup_tree = proto_item_add_subtree(ti, ett_rtp_setup); + if (rtp_setup_tree) + { + /* Add details into subtree */ + proto_item* item = proto_tree_add_uint(rtp_setup_tree, hf_rtp_setup_frame, + tvb, 0, 0, p_packet_data->frame_number); + proto_item_set_generated(item); + item = proto_tree_add_string(rtp_setup_tree, hf_rtp_setup_method, + tvb, 0, 0, p_packet_data->method); + proto_item_set_generated(item); + + if (p_packet_data->rtp_sdp_setup_info_list){ + guint i; + sdp_setup_info_t *stored_setup_info; + for (i = 0; i < wmem_array_get_count(p_packet_data->rtp_sdp_setup_info_list); i++) { + stored_setup_info = (sdp_setup_info_t *)wmem_array_index(p_packet_data->rtp_sdp_setup_info_list, i); + if (stored_setup_info->hf_id) { + if (stored_setup_info->hf_type == SDP_TRACE_ID_HF_TYPE_STR) { + item = proto_tree_add_string(rtp_setup_tree, stored_setup_info->hf_id, tvb, 0, 0, stored_setup_info->trace_id.str); + proto_item_set_generated(item); + if (stored_setup_info->add_hidden == TRUE) { + proto_item_set_hidden(item); + } + } else if (stored_setup_info->hf_type == SDP_TRACE_ID_HF_TYPE_GUINT32) { + item = proto_tree_add_uint(rtp_setup_tree, stored_setup_info->hf_id, tvb, 0, 0, stored_setup_info->trace_id.num); + proto_item_set_generated(item); + if (stored_setup_info->add_hidden == TRUE) { + proto_item_set_hidden(item); + } + } + } + } + } + } +} + +/* Dissect PacketCable CCC header */ + +static int +dissect_pkt_ccc(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data) +{ + + if ( tree ) { + proto_item *ti; + proto_tree *pkt_ccc_tree; + + ti = proto_tree_add_item(tree, proto_pkt_ccc, tvb, 0, 12, ENC_NA); + pkt_ccc_tree = proto_item_add_subtree(ti, ett_pkt_ccc); + + proto_tree_add_item(pkt_ccc_tree, hf_pkt_ccc_id, tvb, 0, 4, ENC_BIG_ENDIAN); + proto_tree_add_item(pkt_ccc_tree, hf_pkt_ccc_ts, tvb, 4, 8, + ENC_TIME_NTP|ENC_BIG_ENDIAN); + } + + return dissect_rtp(tvb, pinfo, tree, data); +} + + +/* Register PacketCable CCC */ + +void +proto_register_pkt_ccc(void) +{ + static hf_register_info hf[] = + { + { + &hf_pkt_ccc_id, + { + "PacketCable CCC Identifier", + "pkt_ccc.ccc_id", + FT_UINT32, + BASE_DEC, + NULL, + 0x0, + NULL, HFILL + } + }, + { + &hf_pkt_ccc_ts, + { + "PacketCable CCC Timestamp", + "pkt_ccc.ts", + FT_ABSOLUTE_TIME, + ABSOLUTE_TIME_UTC, + NULL, + 0x0, + NULL, HFILL + } + }, + + }; + + static gint *ett[] = + { + &ett_pkt_ccc, + }; + + proto_pkt_ccc = proto_register_protocol("PacketCable Call Content Connection", "PKT CCC", "pkt_ccc"); + proto_register_field_array(proto_pkt_ccc, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + + register_dissector("pkt_ccc", dissect_pkt_ccc, proto_pkt_ccc); +} + +void +proto_reg_handoff_pkt_ccc(void) +{ + /* + * Register this dissector as one that can be selected by a + * UDP port number. + */ + dissector_handle_t pkt_ccc_handle; + + pkt_ccc_handle = find_dissector("pkt_ccc"); + dissector_add_for_decode_as_with_preference("udp.port", pkt_ccc_handle); +} + +/* Register RTP */ + +void +proto_register_rtp(void) +{ + static hf_register_info hf[] = + { + { + &hf_rtp_version, + { + "Version", + "rtp.version", + FT_UINT8, + BASE_DEC, + VALS(rtp_version_vals), + 0xC0, + NULL, HFILL + } + }, + { + &hf_rtp_padding, + { + "Padding", + "rtp.padding", + FT_BOOLEAN, + 8, + NULL, + 0x20, + NULL, HFILL + } + }, + { + &hf_rtp_extension, + { + "Extension", + "rtp.ext", + FT_BOOLEAN, + 8, + NULL, + 0x10, + NULL, HFILL + } + }, + { + &hf_rtp_csrc_count, + { + "Contributing source identifiers count", + "rtp.cc", + FT_UINT8, + BASE_DEC, + NULL, + 0x0F, + NULL, HFILL + } + }, + { + &hf_rtp_marker, + { + "Marker", + "rtp.marker", + FT_BOOLEAN, + 8, + NULL, + 0x80, + NULL, HFILL + } + }, + { + &hf_rtp_payload_type, + { + "Payload type", + "rtp.p_type", + FT_UINT8, + BASE_DEC, + NULL, + 0x7F, + NULL, HFILL + } + }, + { + &hf_rtp_seq_nr, + { + "Sequence number", + "rtp.seq", + FT_UINT16, + BASE_DEC, + NULL, + 0x0, + NULL, HFILL + } + }, + { + &hf_rtp_ext_seq_nr, + { + "Extended sequence number", + "rtp.extseq", + FT_UINT32, + BASE_DEC, + NULL, + 0x0, + NULL, HFILL + } + }, + { + &hf_rtp_timestamp, + { + "Timestamp", + "rtp.timestamp", + FT_UINT32, + BASE_DEC, + NULL, + 0x0, + NULL, HFILL + } + }, + { + &hf_rtp_ssrc, + { + "Synchronization Source identifier", + "rtp.ssrc", + FT_UINT32, + BASE_HEX_DEC, + NULL, + 0x0, + NULL, HFILL + } + }, + { + &hf_rtp_prof_define, + { + "Defined by profile", + "rtp.ext.profile", + FT_UINT16, + BASE_HEX_DEC | BASE_RANGE_STRING, + RVALS(rtp_ext_profile_rvals), + 0x0, + NULL, HFILL + } + }, + { + &hf_rtp_length, + { + "Extension length", + "rtp.ext.len", + FT_UINT16, + BASE_DEC, + NULL, + 0x0, + NULL, HFILL + } + }, + { + &hf_rtp_csrc_items, + { + "Contributing Source identifiers", + "rtp.csrc.items", + FT_NONE, + BASE_NONE, + NULL, + 0x0, + NULL, HFILL + } + }, + { + &hf_rtp_csrc_item, + { + "CSRC item", + "rtp.csrc.item", + FT_UINT32, + BASE_HEX_DEC, + NULL, + 0x0, + NULL, HFILL + } + }, + { + &hf_rtp_hdr_exts, + { + "Header extensions", + "rtp.hdr_exts", + FT_NONE, + BASE_NONE, + NULL, + 0x0, + NULL, HFILL + } + }, +/* Other RTP structures */ + { + &hf_rtp_hdr_ext, + { + "Header extension", + "rtp.hdr_ext", + FT_UINT32, + BASE_HEX_DEC, + NULL, + 0x0, + NULL, HFILL + } + }, + { + &hf_rtp_data, + { + "Payload", + "rtp.payload", + FT_BYTES, + BASE_NONE, + NULL, + 0x0, + NULL, HFILL + } + }, + { + &hf_rtp_padding_data, + { + "Padding data", + "rtp.padding.data", + FT_BYTES, + BASE_NONE, + NULL, + 0x0, + NULL, HFILL + } + }, + { + &hf_rtp_padding_count, + { + "Padding count", + "rtp.padding.count", + FT_UINT8, + BASE_DEC, + NULL, + 0x0, + NULL, HFILL + } + }, + { + &hf_rtp_setup, + { + "Stream setup", + "rtp.setup", + FT_STRING, + BASE_NONE, + NULL, + 0x0, + "Stream setup, method and frame number", HFILL + } + }, + { + &hf_rtp_setup_frame, + { + "Setup frame", + "rtp.setup-frame", + FT_FRAMENUM, + BASE_NONE, + NULL, + 0x0, + "Frame that set up this stream", HFILL + } + }, + { + &hf_rtp_setup_method, + { + "Setup Method", + "rtp.setup-method", + FT_STRING, + BASE_NONE, + NULL, + 0x0, + "Method used to set up this stream", HFILL + } + }, + { + &hf_rtp_rfc2198_follow, + { + "Follow", + "rtp.follow", + FT_BOOLEAN, + 8, + TFS(&tfs_set_notset), + 0x80, + "Next header follows", HFILL + } + }, + { + &hf_rtp_rfc2198_tm_off, + { + "Timestamp offset", + "rtp.timestamp-offset", + FT_UINT16, + BASE_DEC, + NULL, + 0xFFFC, + NULL, HFILL + } + }, + { + &hf_rtp_rfc2198_bl_len, + { + "Block length", + "rtp.block-length", + FT_UINT16, + BASE_DEC, + NULL, + 0x03FF, + NULL, HFILL + } + }, + { + &hf_rtp_ext_rfc5285_id, + { + "Identifier", + "rtp.ext.rfc5285.id", + FT_UINT8, + BASE_DEC, + NULL, + 0x0, + "RFC 5285 Header Extension Identifier", + HFILL + } + }, + { + &hf_rtp_ext_rfc5285_length, + { + "Length", + "rtp.ext.rfc5285.len", + FT_UINT8, + BASE_DEC, + NULL, + 0x0, + "RFC 5285 Header Extension length", + HFILL + } + }, + { + &hf_rtp_ext_rfc5285_appbits, + { + "Application Bits", + "rtp.ext.rfc5285.appbits", + FT_UINT8, + BASE_DEC, + NULL, + 0x0, + "RFC 5285 2-bytes header application bits", + HFILL + } + }, + { + &hf_rtp_ext_rfc5285_data, + { + "Extension Data", + "rtp.ext.rfc5285.data", + FT_BYTES, + BASE_NONE, + NULL, + 0x0, + "RFC 5285 Extension Data", + HFILL + } + }, + { + &hf_rfc4571_header_len, + { + "RFC 4571 packet len", + "rtp.rfc4571.len", + FT_UINT16, + BASE_DEC, + NULL, + 0x0, + NULL, HFILL + } + }, + + /* reassembly stuff */ + {&hf_rtp_fragments, + {"RTP Fragments", "rtp.fragments", FT_NONE, BASE_NONE, NULL, 0x0, + NULL, HFILL } + }, + + {&hf_rtp_fragment, + {"RTP Fragment data", "rtp.fragment", FT_FRAMENUM, BASE_NONE, NULL, 0x0, + NULL, HFILL } + }, + + {&hf_rtp_fragment_overlap, + {"Fragment overlap", "rtp.fragment.overlap", FT_BOOLEAN, BASE_NONE, + NULL, 0x0, "Fragment overlaps with other fragments", HFILL } + }, + + {&hf_rtp_fragment_overlap_conflict, + {"Conflicting data in fragment overlap", "rtp.fragment.overlap.conflict", + FT_BOOLEAN, BASE_NONE, NULL, 0x0, + "Overlapping fragments contained conflicting data", HFILL } + }, + + {&hf_rtp_fragment_multiple_tails, + {"Multiple tail fragments found", "rtp.fragment.multipletails", + FT_BOOLEAN, BASE_NONE, NULL, 0x0, + "Several tails were found when defragmenting the packet", HFILL } + }, + + {&hf_rtp_fragment_too_long_fragment, + {"Fragment too long", "rtp.fragment.toolongfragment", + FT_BOOLEAN, BASE_NONE, NULL, 0x0, + "Fragment contained data past end of packet", HFILL } + }, + + {&hf_rtp_fragment_error, + {"Defragmentation error", "rtp.fragment.error", + FT_FRAMENUM, BASE_NONE, NULL, 0x0, + "Defragmentation error due to illegal fragments", HFILL } + }, + + {&hf_rtp_fragment_count, + {"Fragment count", "rtp.fragment.count", + FT_UINT32, BASE_DEC, NULL, 0x0, + NULL, HFILL } + }, + + {&hf_rtp_reassembled_in, + {"RTP fragment, reassembled in frame", "rtp.reassembled_in", + FT_FRAMENUM, BASE_NONE, NULL, 0x0, + "This RTP packet is reassembled in this frame", HFILL } + }, + {&hf_rtp_reassembled_length, + {"Reassembled RTP length", "rtp.reassembled.length", + FT_UINT32, BASE_DEC, NULL, 0x0, + "The total length of the reassembled payload", HFILL } + }, + {&hf_srtp_encrypted_payload, + {"SRTP Encrypted Payload", "srtp.enc_payload", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL } + }, +#if 0 + {&hf_srtp_null_encrypted_payload, + {"SRTP Payload with NULL encryption", "srtp.null_enc_payload", + FT_BYTES, BASE_NONE, NULL, 0x0, + NULL, HFILL } + }, +#endif + {&hf_srtp_mki, + {"SRTP MKI", "srtp.mki", + FT_BYTES, BASE_NONE, NULL, 0x0, + "SRTP Master Key Index", HFILL } + }, + {&hf_srtp_auth_tag, + {"SRTP Auth Tag", "srtp.auth_tag", + FT_BYTES, BASE_NONE, NULL, 0x0, + "SRTP Authentication Tag", HFILL } + } + + }; + + static gint *ett[] = + { + &ett_rtp, + &ett_csrc_list, + &ett_hdr_ext, + &ett_hdr_ext_rfc5285, + &ett_rtp_setup, + &ett_rtp_rfc2198, + &ett_rtp_rfc2198_hdr, + &ett_rtp_fragment, + &ett_rtp_fragments + }; + + static ei_register_info ei[] = { + { &ei_rtp_fragment_unfinished, { "rtp.fragment_unfinished", PI_REASSEMBLE, PI_CHAT, "RTP fragment, unfinished", EXPFILL }}, + { &ei_rtp_padding_missing, { "rtp.padding_missing", PI_MALFORMED, PI_ERROR, "Frame has padding, but not all the frame data was captured", EXPFILL }}, + }; + + /* Decode As handling */ + static build_valid_func rtp_da_build_value[1] = {rtp_value}; + static decode_as_value_t rtp_da_values = {rtp_prompt, 1, rtp_da_build_value}; + static decode_as_t rtp_da = {"rtp", "rtp.pt", 1, 0, &rtp_da_values, NULL, NULL, + decode_as_default_populate_list, decode_as_default_reset, decode_as_default_change, NULL}; + + module_t *rtp_module; + expert_module_t *expert_rtp; + + proto_rtp = proto_register_protocol("Real-Time Transport Protocol", "RTP", "rtp"); + proto_rtp_rfc2198 = proto_register_protocol_in_name_only("RTP Payload for Redundant Audio Data (RFC 2198)", + "RAD (RFC2198)", "rtp_rfc2198", proto_rtp, FT_PROTOCOL); + + proto_register_field_array(proto_rtp, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + expert_rtp = expert_register_protocol(proto_rtp); + expert_register_field_array(expert_rtp, ei, array_length(ei)); + + rtp_handle = register_dissector("rtp", dissect_rtp, proto_rtp); + rtp_rfc2198_handle = register_dissector("rtp.rfc2198", dissect_rtp_rfc2198, proto_rtp_rfc2198); + rtp_rfc4571_handle = register_dissector("rtp.rfc4571", dissect_rtp_rfc4571, proto_rtp); + + rtp_tap = register_tap("rtp"); + + rtp_pt_dissector_table = register_dissector_table("rtp.pt", + "RTP payload type", proto_rtp, FT_UINT8, BASE_DEC); + rtp_dyn_pt_dissector_table = register_dissector_table("rtp_dyn_payload_type", + "Dynamic RTP payload type", proto_rtp, FT_STRING, STRING_CASE_INSENSITIVE); + + + rtp_hdr_ext_dissector_table = register_dissector_table("rtp.hdr_ext", + "RTP header extension", proto_rtp, FT_UINT32, BASE_HEX); + rtp_hdr_ext_rfc5285_dissector_table = register_dissector_table("rtp.ext.rfc5285.id", + "RTP Generic header extension (RFC 5285)", proto_rtp, FT_UINT8, BASE_DEC); + + rtp_module = prefs_register_protocol(proto_rtp, NULL); + + prefs_register_bool_preference(rtp_module, "show_setup_info", + "Show stream setup information", + "Where available, show which protocol and frame caused " + "this RTP stream to be created", + &global_rtp_show_setup_info); + + prefs_register_obsolete_preference(rtp_module, "heuristic_rtp"); + + prefs_register_bool_preference(rtp_module, "desegment_rtp_streams", + "Allow subdissector to reassemble RTP streams", + "Whether subdissector can request RTP streams to be reassembled", + &desegment_rtp); + + prefs_register_enum_preference(rtp_module, "version0_type", + "Treat RTP version 0 packets as", + "If an RTP version 0 packet is encountered, it can be treated as " + "an invalid or ZRTP packet, a CLASSIC-STUN packet, or a T.38 packet", + &global_rtp_version0_type, + rtp_version0_types, FALSE); + prefs_register_obsolete_preference(rtp_module, "rfc2198_payload_type"); + + prefs_register_bool_preference(rtp_module, "rfc2198_deencapsulate", + "De-encapsulate RFC 2198 primary encoding", + "De-encapsulate the primary encoding from " + "the RAD header for RTP analysis and " + "playback", + &rfc2198_deencapsulate); + + reassembly_table_register(&rtp_reassembly_table, + &addresses_reassembly_table_functions); + + register_init_routine(rtp_dyn_payloads_init); + register_decode_as(&rtp_da); +} + +void +proto_reg_handoff_rtp(void) +{ + dissector_add_for_decode_as("udp.port", rtp_handle); + dissector_add_for_decode_as("tcp.port", rtp_rfc4571_handle); + dissector_add_string("rtp_dyn_payload_type", "red", rtp_rfc2198_handle); + heur_dissector_add( "udp", dissect_rtp_heur, "RTP over UDP", "rtp_udp", proto_rtp, HEURISTIC_DISABLE); + heur_dissector_add("stun", dissect_rtp_heur, "RTP over TURN", "rtp_stun", proto_rtp, HEURISTIC_DISABLE); + heur_dissector_add("classicstun", dissect_rtp_heur, "RTP over CLASSICSTUN", "rtp_classicstun", proto_rtp, HEURISTIC_DISABLE); + heur_dissector_add("rtsp", dissect_rtp_heur, "RTP over RTSP", "rtp_rtsp", proto_rtp, HEURISTIC_DISABLE); + + dissector_add_for_decode_as("flip.payload", rtp_handle ); + + + rtcp_handle = find_dissector_add_dependency("rtcp", proto_rtp); + stun_handle = find_dissector_add_dependency("stun-udp", proto_rtp); + classicstun_handle = find_dissector_add_dependency("classicstun", proto_rtp); + classicstun_heur_handle = find_dissector_add_dependency("classicstun-heur", proto_rtp); + stun_heur_handle = find_dissector_add_dependency("stun-heur", proto_rtp); + t38_handle = find_dissector_add_dependency("t38_udp", proto_rtp); + zrtp_handle = find_dissector_add_dependency("zrtp", proto_rtp); + dtls_handle = find_dissector_add_dependency("dtls", proto_rtp); + + sprt_handle = find_dissector_add_dependency("sprt", proto_rtp); + v150fw_handle = find_dissector("v150fw"); + + bta2dp_content_protection_header_scms_t = find_dissector_add_dependency("bta2dp_content_protection_header_scms_t", proto_rtp); + btvdp_content_protection_header_scms_t = find_dissector_add_dependency("btvdp_content_protection_header_scms_t", proto_rtp); + bta2dp_handle = find_dissector_add_dependency("bta2dp", proto_rtp); + btvdp_handle = find_dissector_add_dependency("btvdp", proto_rtp); + sbc_handle = find_dissector_add_dependency("sbc", proto_rtp); + + dissector_add_string("rtp_dyn_payload_type", "v150fw", v150fw_handle); + dissector_add_for_decode_as("rtp.pt", v150fw_handle); + + dissector_add_for_decode_as("btl2cap.cid", rtp_handle); + + dissector_add_uint_range_with_preference("rtp.pt", RFC2198_DEFAULT_PT_RANGE, rtp_rfc2198_handle); + proto_sdp = proto_get_id_by_filter_name("sdp"); +} + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ |