From: Evan Huus Date: Sun, 18 Aug 2013 19:49:08 +0000 Subject: [PATCH] Dissector for the Sippy RTPproxy controlling protocol From Peter Lemenkov via https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=8326 Dissector for the Sippy RTPproxy controlling protocol. RTPproxy is a well-known (among SIP-engineers) application and it operates using its own simple text-based protocol. There are several competing products but all of them implements it (sometimes slightly extending). svn path=/trunk/; revision=51417 Add a cast to try and fix packet-rtpproxy.c:226: warning: implicit conversion shortens 64-bit value into a 32-bit value I'm not quite sure what's going on here, all the values in use are either gint or guint so they should all be the same size? svn path=/trunk/; revision=51419 Take another stab at packet-rtpproxy.c:226: warning: implicit conversion shortens 64-bit value into a 32-bit value svn path=/trunk/; revision=51420 One more 64/32-conversion fix for rtpproxy svn path=/trunk/; revision=51421 Fix Coverity CID 1063335: Unused pointer value. svn path=/trunk/; revision=51430 From Peter Lemenkov via https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=9211 : Add basic conversation support for rtpproxy dissector svn path=/trunk/; revision=52331 Let's please fix-encoding-args.pl svn path=/trunk/; revision=52334 Make RTPProxy a newstyle dissector. svn path=/trunk/; revision=52476 Try to fix packet-rtpproxy.c:217: warning: comparison between signed and unsigned svn path=/trunk/; revision=52477 Fix signed vs. unsigned comparison warnings. svn path=/trunk/; revision=52483 Add response time to the rtpproxy dissector. Bug 9380 (https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=9380) From Peter Lemenkov. svn path=/trunk/; revision=53115 Decode more error replies in RTPproxy dissector. Bug 9408 (https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=9408) From Peter Lemenkov svn path=/trunk/; revision=53245 From Peter Lemenkov display a text description for the RTPProxy version request https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=9416 svn path=/trunk/; revision=53249 replace proto_tree_add_string_format_value() with proto_tree_add_item() svn path=/trunk/; revision=53261 From Peter Lemenkov via https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=9429 : Fix Notify info parsing in RTPproxy dissector svn path=/trunk/; revision=53385 From Peter Lemenkov via https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=9430 : Fix empty ToTag in RTPproxy's Offer/Update command svn path=/trunk/; revision=53391 From Peter Lemenkov via https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=9440 : RTPproxy: use proto_item_append_text instead of rewriting the entire item's text svn path=/trunk/; revision=53393 From Peter Lemenkov via https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=9441 : Fix Notify IPv6 address parsing in RTPproxy dissector svn path=/trunk/; revision=53394 add a cast to fix compiler warning svn path=/trunk/; revision=53401 Fix typo errors svn path=/trunk/; revision=53585 From Peter Lemenkov via https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=9397 : Add RTP/RTCP dissector setup based on the replies found in rtpproxy dissector svn path=/trunk/; revision=53603 From Peter Lemenkov via https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=9444 : Add initial parameter parser for commands in RTPproxy dissector svn path=/trunk/; revision=53604 From Peter Lemenkov via https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=9476 : Add DTMF dissector setup based on the information found in RTPproxy requests svn path=/trunk/; revision=53605 Squelch a compiler warning. svn path=/trunk/; revision=53606 Reinitialize counter between loops to avoid an out of bound access found with fuzz testing. svn path=/trunk/; revision=53612 RTPproxy dissector: Add expert info about timeouts. Bug 9484 (https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=9484) From Peter Lemenkov svn path=/trunk/; revision=53622 From Peter Lemenkov via https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=9487 : Use RTP payload descriptions from RTP dissector in RTPproxy codec param value svn path=/trunk/; revision=53658 From Peter Lemenkov via https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=9490 : Couple packets in RTPproxy dissector not only with CookieID but also with Call-IDs svn path=/trunk/; revision=53673 Fix Coverity CID 1134045: Printf format string issue. (As documented in the printf man page for the ' option, "Note that many versions of gcc(1) cannot parse this option and will issue a warning." (r47940 reverted a similar change, so this keeps things consistent.) svn path=/trunk/; revision=53705 Clean up indentation. svn path=/trunk/; revision=53742 diff --git a/AUTHORS b/AUTHORS index 10782b0..e7b3c18 100644 --- a/AUTHORS +++ b/AUTHORS @@ -3733,6 +3733,7 @@ Max Baker Mike Garratt Bart Van Assche Karl Beldan +Peter Lemenkov Masayuki Takemura diff --git a/epan/CMakeLists.txt b/epan/CMakeLists.txt index 69eb75f..610cea7 100644 --- a/epan/CMakeLists.txt +++ b/epan/CMakeLists.txt @@ -1083,6 +1083,7 @@ set(DISSECTOR_SRC dissectors/packet-rtp-events.c dissectors/packet-rtp-midi.c dissectors/packet-rtp.c + dissectors/packet-rtpproxy.c dissectors/packet-rtps.c dissectors/packet-rtsp.c dissectors/packet-rudp.c diff --git a/epan/dissectors/Makefile.common b/epan/dissectors/Makefile.common index 937f522..73217d7 100644 --- a/epan/dissectors/Makefile.common +++ b/epan/dissectors/Makefile.common @@ -1004,6 +1004,7 @@ DISSECTOR_SRC = \ packet-rtp-events.c \ packet-rtp-midi.c \ packet-rtp.c \ + packet-rtpproxy.c \ packet-rtps.c \ packet-rtsp.c \ packet-rudp.c \ diff --git a/epan/dissectors/packet-rtpproxy.c b/epan/dissectors/packet-rtpproxy.c new file mode 100644 index 0000000..0afee3a --- /dev/null +++ b/epan/dissectors/packet-rtpproxy.c @@ -0,0 +1,1387 @@ +/* packet-rtpproxy.c + * RTPproxy command protocol dissector + * Copyright 2013, Peter Lemenkov + * + * This dissector tries to dissect rtpproxy control protocol. Please visit this + * link for brief details on the command format: + * + * http://www.rtpproxy.org/wiki/RTPproxy/Protocol + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1999 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_ARPA_INET_H +#include +#endif +#ifdef HAVE_WINSOCK2_H +#include /* needed to define AF_ values on Windows */ +#endif +#ifdef NEED_INET_V6DEFS_H +#include "wsutil/inet_v6defs.h" +#endif + +/* For setting up RTP/RTCP dissectors based on the RTPproxy's answers */ +#include "packet-rtp.h" +#include "packet-rtcp.h" + +static int proto_rtpproxy = -1; + +static int hf_rtpproxy_cookie = -1; +static int hf_rtpproxy_error = -1; +static int hf_rtpproxy_status = -1; +static int hf_rtpproxy_ok = -1; +static int hf_rtpproxy_ipv4 = -1; +static int hf_rtpproxy_ipv6 = -1; +static int hf_rtpproxy_port = -1; +static int hf_rtpproxy_lf = -1; +static int hf_rtpproxy_request = -1; +static int hf_rtpproxy_command = -1; +static int hf_rtpproxy_command_parameters = -1; +static int hf_rtpproxy_command_parameter = -1; +static int hf_rtpproxy_command_parameter_codec = -1; +static int hf_rtpproxy_command_parameter_local = -1; +static int hf_rtpproxy_command_parameter_remote = -1; +static int hf_rtpproxy_command_parameter_repacketize = -1; +static int hf_rtpproxy_command_parameter_dtmf = -1; +/* static int hf_rtpproxy_command_parameter_cmap = -1; TODO */ +static int hf_rtpproxy_command_parameter_proto = -1; +static int hf_rtpproxy_command_parameter_transcode = -1; +static int hf_rtpproxy_command_parameter_acc = -1; +static int hf_rtpproxy_callid = -1; +static int hf_rtpproxy_copy_target = -1; +static int hf_rtpproxy_playback_filename = -1; +static int hf_rtpproxy_playback_codec = -1; +static int hf_rtpproxy_notify = -1; +static int hf_rtpproxy_notify_ipv4 = -1; +static int hf_rtpproxy_notify_ipv6 = -1; +static int hf_rtpproxy_notify_port = -1; +static int hf_rtpproxy_notify_tag = -1; +static int hf_rtpproxy_tag = -1; +static int hf_rtpproxy_mediaid = -1; +static int hf_rtpproxy_reply = -1; +static int hf_rtpproxy_version_request = -1; +static int hf_rtpproxy_version_supported = -1; + +/* Request/response tracking */ +static int hf_rtpproxy_request_in = -1; +static int hf_rtpproxy_response_in = -1; +static int hf_rtpproxy_response_time = -1; + +typedef struct _rtpproxy_info { + guint32 req_frame; + guint32 resp_frame; + nstime_t req_time; + gchar* callid; +} rtpproxy_info_t; + +static dissector_handle_t rtcp_handle; +static dissector_handle_t rtp_events_handle; +static dissector_handle_t rtp_handle; + +typedef struct _rtpproxy_conv_info { + emem_tree_t *trans; +} rtpproxy_conv_info_t; + + +static const string_string versiontypenames[] = { + { "20040107", "Basic RTP proxy functionality" }, + { "20050322", "Support for multiple RTP streams and MOH" }, + { "20060704", "Support for extra parameter in the V command" }, + { "20071116", "Support for RTP re-packetization" }, + { "20071218", "Support for forking (copying) RTP stream" }, + { "20080403", "Support for RTP statistics querying" }, + { "20081102", "Support for setting codecs in the update/lookup command" }, + { "20081224", "Support for session timeout notifications" }, + { "20090810", "Support for automatic bridging" }, + { 0, NULL } +}; + +static const value_string commandtypenames[] = { + { 'V', "Handshake/Ping" }, + { 'v', "Handshake/Ping" }, + { 'U', "Offer/Update" }, + { 'u', "Offer/Update" }, + { 'L', "Answer/Lookup" }, + { 'l', "Answer/Lookup" }, + { 'I', "Information"}, + { 'i', "Information"}, + { 'X', "Close all active sessions"}, + { 'x', "Close all active sessions"}, + { 'D', "Delete an active session (Bye/Cancel/Error)"}, + { 'd', "Delete an active session (Bye/Cancel/Error)"}, + { 'P', "Start playback (music-on-hold)"}, + { 'p', "Start playback (music-on-hold)"}, + { 'S', "Stop playback (music-on-hold)"}, + { 's', "Stop playback (music-on-hold)"}, + { 'R', "Start recording"}, + { 'r', "Start recording"}, + { 'C', "Copy stream"}, + { 'c', "Copy stream"}, + { 'Q', "Query info about a session"}, + { 'q', "Query info about a session"}, + { 0, NULL } +}; + +static const value_string paramtypenames[] = { + /* Official command parameters */ + {'4', "Remote address is IPv4"}, + {'6', "Remote address is IPv6"}, + {'a', "Asymmetric RTP"}, + {'A', "Asymmetric RTP"}, + {'b', "Brief stats"}, + {'B', "Brief stats"}, + {'c', "Codecs"}, + {'C', "Codecs"}, + {'e', "External network (non RFC 1918)"}, + {'E', "External network (non RFC 1918)"}, + {'i', "Internal network (RFC 1918)"}, + {'I', "Internal network (RFC 1918)"}, + {'l', "Local address"}, + {'L', "Local address"}, + {'r', "Remote address"}, + {'R', "Remote address"}, + {'s', "Symmetric RTP (default)"}, + {'S', "Symmetric RTP (default)"}, + {'w', "Weak connection (allows roaming)"}, + {'W', "Weak connection (allows roaming)"}, + {'z', "repacketiZe"}, + {'Z', "repacketiZe"}, + /* Unofficial command parameters / expensions */ + {'d', "DTMF payload ID (unofficial extension)"}, + {'D', "DTMF payload ID (unofficial extension)"}, + {'m', "codec Mapping (unofficial extension)"}, + {'M', "codec Mapping (unofficial extension)"}, + {'p', "Protocol type (unofficial extension)"}, + {'P', "Protocol type (unofficial extension)"}, + {'t', "Transcode to (unofficial extension)"}, + {'T', "Transcode to (unofficial extension)"}, + {'v', "Accounting (unofficial extension)"}, + {'V', "Accounting (unofficial extension)"}, + {0, NULL} +}; + +static const value_string prototypenames[] = { + { '0', "UDP (default)"}, + { '1', "TCP"}, + { '2', "SCTP"}, + { 0, NULL } +}; +static const value_string acctypenames[] = { + { '0', "Start"}, + { '1', "Interim update"}, + { '2', "Stop"}, + { 0, NULL } +}; + +static const value_string oktypenames[] = { + { '0', "Ok"}, + { '1', "Version Supported"}, + { 0, NULL } +}; + +static const string_string errortypenames[] = { + { "E0", "Syntax error" }, + { "E1", "Syntax error" }, + { "E2", "Syntax error" }, + { "E3", "Unknown command" }, + { "E4", "Syntax error" }, + { "E5", "Out of memory" }, + { "E6", "" }, + { "E7", "Software error (can't create listener)" }, + { "E8", "Not Found" }, + { "E10", "Software error (can't create listener)" }, + { "E11", "Out of memory" }, + { "E12", "Out of memory" }, + { "E13", "Out of memory" }, + { "E14", "Out of memory" }, + { 0, NULL } +}; + +static const value_string flowcontroltypenames[] = { + { '\n', "Yes"}, + { 0, NULL } +}; + +static gint ett_rtpproxy = -1; + +static gint ett_rtpproxy_request = -1; +static gint ett_rtpproxy_command = -1; +static gint ett_rtpproxy_command_parameters = -1; +static gint ett_rtpproxy_command_parameters_codecs = -1; +static gint ett_rtpproxy_command_parameters_local = -1; +static gint ett_rtpproxy_command_parameters_remote = -1; +static gint ett_rtpproxy_command_parameters_repacketize = -1; +static gint ett_rtpproxy_command_parameters_dtmf = -1; +static gint ett_rtpproxy_command_parameters_cmap = -1; +static gint ett_rtpproxy_command_parameters_proto = -1; +static gint ett_rtpproxy_command_parameters_transcode = -1; +static gint ett_rtpproxy_command_parameters_acc = -1; +static gint ett_rtpproxy_tag = -1; +static gint ett_rtpproxy_notify = -1; + +static gint ett_rtpproxy_reply = -1; + +/* Default values */ +static guint rtpproxy_tcp_port = 22222; +static guint rtpproxy_udp_port = 22222; +static gboolean rtpproxy_establish_conversation = TRUE; +/* See - http://www.opensips.org/html/docs/modules/1.11.x/rtpproxy.html#id250018 */ +/* See - http://www.kamailio.org/docs/modules/devel/modules/rtpproxy.html#idm448 */ +static guint rtpproxy_timeout = 1000; +static nstime_t rtpproxy_timeout_ns = {1, 0}; + +void proto_reg_handoff_rtpproxy(void); + +gint +rtpproxy_add_tag(proto_tree *rtpproxy_tree, tvbuff_t *tvb, guint begin, guint realsize) +{ + proto_item *ti = NULL; + proto_tree *another_tree = NULL; + gint new_offset; + guint end; + + new_offset = tvb_find_guint8(tvb, begin, -1, ' '); + if(new_offset < 0) + end = realsize; /* No more parameters */ + else + end = new_offset; + + /* SER/OpenSER/OpenSIPS/Kamailio adds Media-ID right after the Tag + * separated by a semicolon + */ + new_offset = tvb_find_guint8(tvb, begin, end, ';'); + if(new_offset == -1){ + ti = proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_tag, tvb, begin, end - begin, ENC_ASCII | ENC_NA); + another_tree = proto_item_add_subtree(ti, ett_rtpproxy_tag); + ti = proto_tree_add_item(another_tree, hf_rtpproxy_mediaid, tvb, new_offset+1, 0, ENC_ASCII | ENC_NA); + proto_item_append_text(ti, ""); + } + else{ + ti = proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_tag, tvb, begin, new_offset - begin, ENC_ASCII | ENC_NA); + if ((guint)new_offset == begin) + proto_item_append_text(ti, ""); /* A very first Offer/Update command */ + another_tree = proto_item_add_subtree(ti, ett_rtpproxy_tag); + proto_tree_add_item(another_tree, hf_rtpproxy_mediaid, tvb, new_offset+1, end - (new_offset+1), ENC_ASCII | ENC_NA); + } + return (end == realsize ? -1 : (gint)end); +} + +void +rtpproxy_add_parameter(proto_tree *rtpproxy_tree, tvbuff_t *tvb, guint begin, guint realsize) +{ + proto_item *ti; + proto_tree *another_tree = NULL; + guint offset = 0; + guint new_offset = 0; + gint i; + guint pt = 0; + gchar** codecs = NULL; + guint codec_len; + guint8* rawstr = NULL; + + /* Extract the entire parameters line. */ + /* Something like "t4p1iic8,0,2,4,18,96,97,98,100,101" */ + rawstr = tvb_get_ephemeral_string(tvb, begin, realsize); + + while(offset < realsize){ + ti = proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_command_parameter, tvb, begin + offset, 1, ENC_NA); + offset++; /* Skip 1-byte parameter's type */ + switch (g_ascii_tolower(tvb_get_guint8(tvb, begin+offset-1))) + { + /* Official long parameters */ + case 'c': + new_offset = (gint)strspn(rawstr+offset, "0123456789,"); + another_tree = proto_item_add_subtree(ti, ett_rtpproxy_command_parameters_codecs); + codecs = g_strsplit(tvb_get_ephemeral_string(tvb, begin+offset, new_offset), ",", 0); + i = 0; + while(codecs[i]){ + /* We assume strings < 2^32-1 bytes long. :-) */ + codec_len = (guint)strlen(codecs[i]); + ti = proto_tree_add_item(another_tree, hf_rtpproxy_command_parameter_codec, tvb, begin+offset, codec_len, ENC_ASCII | ENC_NA); + proto_item_append_text(ti, " (%s)", val_to_str_ext((guint)strtoul(tvb_format_text(tvb,begin+offset,codec_len),NULL,10), &rtp_payload_type_vals_ext, "Unknown")); + offset += codec_len; + if(codecs[i+1]) + offset++; /* skip comma */ + i++; + }; + g_strfreev(codecs); + break; + case 'l': + new_offset = (gint)strspn(rawstr+offset, "0123456789."); + another_tree = proto_item_add_subtree(ti, ett_rtpproxy_command_parameters_local); + proto_tree_add_item(another_tree, hf_rtpproxy_command_parameter_local, tvb, begin+offset, new_offset, ENC_ASCII | ENC_NA); + offset += new_offset; + break; + case 'r': + new_offset = (gint)strspn(rawstr+offset, "0123456789."); + another_tree = proto_item_add_subtree(ti, ett_rtpproxy_command_parameters_remote); + proto_tree_add_item(another_tree, hf_rtpproxy_command_parameter_remote, tvb, begin+offset, new_offset, ENC_ASCII | ENC_NA); + offset += new_offset; + break; + case 'z': + new_offset = (gint)strspn(rawstr+offset, "0123456789"); + another_tree = proto_item_add_subtree(ti, ett_rtpproxy_command_parameters_repacketize); + proto_tree_add_item(another_tree, hf_rtpproxy_command_parameter_repacketize, tvb, begin+offset, new_offset, ENC_ASCII | ENC_NA); + offset += new_offset; + break; + /* Unofficial long parameters */ + case 'd': + new_offset = (gint)strspn(rawstr+offset, "0123456789"); + another_tree = proto_item_add_subtree(ti, ett_rtpproxy_command_parameters_dtmf); + proto_tree_add_item(another_tree, hf_rtpproxy_command_parameter_dtmf, tvb, begin+offset, new_offset, ENC_ASCII | ENC_NA); + if(rtpproxy_establish_conversation){ + pt = (guint)strtoul(tvb_format_text(tvb,begin+offset,new_offset),NULL,10); + dissector_add_uint("rtp.pt", pt, rtp_events_handle); + } + offset += new_offset; + break; + case 'm': + new_offset = (gint)strspn(rawstr+offset, "0123456789=,"); + /* TODO */ + offset += new_offset; + break; + case 'p': + another_tree = proto_item_add_subtree(ti, ett_rtpproxy_command_parameters_proto); + proto_tree_add_item(another_tree, hf_rtpproxy_command_parameter_proto, tvb, begin+offset, 1, ENC_NA); + offset++; + break; + case 't': + new_offset = (gint)strspn(rawstr+offset, "0123456789"); + another_tree = proto_item_add_subtree(ti, ett_rtpproxy_command_parameters_transcode); + ti = proto_tree_add_item(another_tree, hf_rtpproxy_command_parameter_transcode, tvb, begin+offset, new_offset, ENC_ASCII | ENC_NA); + proto_item_append_text(ti, " (%s)", val_to_str_ext((guint)strtoul(tvb_format_text(tvb,begin+offset, new_offset),NULL,10), &rtp_payload_type_vals_ext, "Unknown")); + offset += new_offset; + break; + case 'v': + another_tree = proto_item_add_subtree(ti, ett_rtpproxy_command_parameters_acc); + proto_tree_add_item(another_tree, hf_rtpproxy_command_parameter_acc, tvb, begin+offset, 1, ENC_NA); + offset++; + break; + default: + break; + } + } +} + +rtpproxy_info_t * +rtpproxy_add_tid(gboolean is_request, tvbuff_t *tvb, packet_info *pinfo, proto_tree *rtpproxy_tree, rtpproxy_conv_info_t *rtpproxy_conv, gchar* cookie) +{ + rtpproxy_info_t *rtpproxy_info; + proto_item *pi; + + if (!PINFO_FD_VISITED(pinfo)) { + if (is_request){ + rtpproxy_info = se_new(rtpproxy_info_t); + rtpproxy_info->req_frame = PINFO_FD_NUM(pinfo); + rtpproxy_info->resp_frame = 0; + rtpproxy_info->req_time = pinfo->fd->abs_ts; + rtpproxy_info->callid = NULL; + se_tree_insert_string(rtpproxy_conv->trans, cookie, rtpproxy_info, 0); + } else { + rtpproxy_info = (rtpproxy_info_t *)se_tree_lookup_string(rtpproxy_conv->trans, cookie, 0); + if (rtpproxy_info) { + rtpproxy_info->resp_frame = PINFO_FD_NUM(pinfo); + } + } + } else { + rtpproxy_info = (rtpproxy_info_t *)se_tree_lookup_string(rtpproxy_conv->trans, cookie, 0); + if (rtpproxy_info && (is_request ? rtpproxy_info->resp_frame : rtpproxy_info->req_frame)) { + nstime_t ns; + + pi = proto_tree_add_uint(rtpproxy_tree, is_request ? hf_rtpproxy_response_in : hf_rtpproxy_request_in, tvb, 0, 0, is_request ? rtpproxy_info->resp_frame : rtpproxy_info->req_frame); + PROTO_ITEM_SET_GENERATED(pi); + + /* If reply then calculate response time */ + if (!is_request){ + nstime_delta(&ns, &pinfo->fd->abs_ts, &rtpproxy_info->req_time); + pi = proto_tree_add_time(rtpproxy_tree, hf_rtpproxy_response_time, tvb, 0, 0, &ns); + PROTO_ITEM_SET_GENERATED(pi); + if (nstime_cmp(&rtpproxy_timeout_ns, &ns) < 0) + expert_add_info_format(pinfo, rtpproxy_tree, PI_RESPONSE_CODE, PI_WARN, "Response timeout %.3f seconds", nstime_to_sec(&ns)); + } + } + } + /* Could be NULL so we should check it before dereferencing */ + return rtpproxy_info; +} + +void +rtpproxy_add_notify_addr(proto_tree *rtpproxy_tree, tvbuff_t *tvb, guint begin, guint end) +{ + gint offset = 0; + gint tmp = 0; + gboolean ipv6 = FALSE; + proto_item *ti; + + /* Check for at least one colon */ + offset = tvb_find_guint8(tvb, begin, end, ':'); + if(offset != -1){ + /* Find if it's the latest colon (not in case of a IPv6) */ + while((tmp = tvb_find_guint8(tvb, offset+1, end, ':')) != -1){ + ipv6 = TRUE; + offset = tmp; + } + /* We have ip:port */ + if(ipv6) + proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_notify_ipv6, tvb, begin, offset - begin, ENC_ASCII | ENC_NA); + else + proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_notify_ipv4, tvb, begin, offset - begin, ENC_ASCII | ENC_NA); + proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_notify_port, tvb, offset+1, end - (offset+1), ENC_ASCII | ENC_NA); + } + else{ + /* Only port is supplied */ + ti = proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_notify_ipv4, tvb, begin, 0, ENC_ASCII | ENC_NA); + proto_item_append_text(ti, ""); + proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_notify_port, tvb, begin, end - begin, ENC_ASCII | ENC_NA); + } +} + +static int +dissect_rtpproxy(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ + gboolean has_lf = FALSE; + gint offset = 0; + gint new_offset = 0; + guint tmp; + gint realsize = 0; + guint8* rawstr; + guint8* tmpstr; + proto_item *ti; + proto_item *ti2; + proto_tree *rtpproxy_tree; + conversation_t *conversation; + rtpproxy_conv_info_t *rtpproxy_conv; + gchar* cookie = NULL; + /* For RT(C)P setup */ + address addr; + guint16 port; + guint32 ipaddr[4]; + rtpproxy_info_t *rtpproxy_info = NULL; + + /* If it does not start with a printable character it's not RTPProxy */ + if(!isprint(tvb_get_guint8(tvb, 0))) + return 0; + + /* Extract Cookie */ + offset = tvb_find_guint8(tvb, offset, -1, ' '); + if(offset == -1) + return 0; + + /* Clear out stuff in the info column - we''l set it later */ + col_clear(pinfo->cinfo, COL_INFO); + + ti = proto_tree_add_item(tree, proto_rtpproxy, tvb, 0, -1, ENC_NA); + rtpproxy_tree = proto_item_add_subtree(ti, ett_rtpproxy); + + proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_cookie, tvb, 0, offset, ENC_ASCII | ENC_NA); + cookie = tvb_get_ephemeral_string(tvb, 0, offset); + + /* Skip whitespace */ + offset = tvb_skip_wsp(tvb, offset+1, -1); + + /* Calculate size to prevent recalculation in the future */ + realsize = tvb_reported_length(tvb); + + + /* Check for LF (required for TCP connection, optional for UDP) */ + if (tvb_get_guint8(tvb, realsize - 1) == '\n'){ + col_set_str(pinfo->cinfo, COL_PROTOCOL, "RTPproxy"); + /* Don't count trailing LF */ + realsize -= 1; + has_lf = TRUE; + } + else + col_set_str(pinfo->cinfo, COL_PROTOCOL, "RTPproxy (no LF)"); + + + /* Try to create conversation */ + conversation = find_or_create_conversation(pinfo); + rtpproxy_conv = (rtpproxy_conv_info_t *)conversation_get_proto_data(conversation, proto_rtpproxy); + if (!rtpproxy_conv) { + rtpproxy_conv = se_new(rtpproxy_conv_info_t); + rtpproxy_conv->trans = se_tree_create_non_persistent(EMEM_TREE_TYPE_RED_BLACK, "rtpproxy transactions");; + conversation_add_proto_data(conversation, proto_rtpproxy, rtpproxy_conv); + } + + /* Get payload string */ + rawstr = tvb_get_ephemeral_string(tvb, offset, realsize - offset); + + /* Extract command */ + tmp = g_ascii_tolower(tvb_get_guint8(tvb, offset)); + switch (tmp) + { + case 's': + /* A specific case - long statistics answer */ + /* %COOKIE% sessions created %NUM0% active sessions: %NUM1% */ + rtpproxy_add_tid(FALSE, tvb, pinfo, rtpproxy_tree, rtpproxy_conv, cookie); + if ('e' == tvb_get_guint8(tvb, offset+1)){ + col_add_fstr(pinfo->cinfo, COL_INFO, "Reply: %s", rawstr); + ti = proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_reply, tvb, offset, -1, ENC_NA); + + rtpproxy_tree = proto_item_add_subtree(ti, ett_rtpproxy_reply); + proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_status, tvb, offset, realsize - offset, ENC_ASCII | ENC_NA); + break; + } + case 'i': + case 'x': + case 'u': + case 'l': + case 'd': + case 'p': + case 'v': + case 'r': + case 'c': + case 'q': + rtpproxy_info = rtpproxy_add_tid(TRUE, tvb, pinfo, rtpproxy_tree, rtpproxy_conv, cookie); + col_add_fstr(pinfo->cinfo, COL_INFO, "Request: %s", rawstr); + ti = proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_request, tvb, offset, -1, ENC_NA); + rtpproxy_tree = proto_item_add_subtree(ti, ett_rtpproxy_request); + + /* A specific case - version request */ + if ((tmp == 'v') && (offset + (gint)strlen("VF YYYMMDD") + 1 == realsize)){ + /* Skip whitespace */ + new_offset = tvb_skip_wsp(tvb, offset + ((guint)strlen("VF") + 1), -1); + ti = proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_version_request, tvb, new_offset, (gint)strlen("YYYYMMDD"), ENC_ASCII | ENC_NA); + tmpstr = tvb_get_ephemeral_string(tvb, new_offset, (gint)strlen("YYYYMMDD")); + proto_item_append_text(ti, " (%s)", str_to_str(tmpstr, versiontypenames, "Unknown")); + break; + } + + /* All other commands */ + ti = proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_command, tvb, offset, 1, ENC_NA); + + /* A specific case - handshake/ping */ + if (tmp == 'v') + break; /* No more parameters */ + + /* A specific case - close all calls */ + if (tmp == 'x') + break; /* No more parameters */ + + /* Extract parameters */ + /* Parameters should be right after the command and before EOL (in case of Info command) or before whitespace */ + new_offset = (tmp == 'i' ? (realsize - 1 > offset ? offset + (gint)strlen("Ib") : offset + (gint)strlen("I")) : tvb_find_guint8(tvb, offset, -1, ' ')); + + if (new_offset != offset + 1){ + rtpproxy_tree = proto_item_add_subtree(ti, ett_rtpproxy_command); + ti2 = proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_command_parameters, tvb, offset+1, new_offset - (offset+1), ENC_ASCII | ENC_NA); + rtpproxy_add_parameter(proto_item_add_subtree(ti2, ett_rtpproxy_command_parameters), tvb, offset+1, new_offset - (offset+1)); + rtpproxy_tree = proto_item_get_parent(ti); + } + + /* A specific case - query information */ + if (tmp == 'i') + break; /* No more parameters */ + + /* Skip whitespace */ + offset = tvb_skip_wsp(tvb, new_offset+1, -1); + + /* Extract Call-ID */ + new_offset = tvb_find_guint8(tvb, offset, -1, ' '); + proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_callid, tvb, offset, new_offset - offset, ENC_ASCII | ENC_NA); + if(rtpproxy_info && !rtpproxy_info->callid) + rtpproxy_info->callid = tvb_get_seasonal_string(tvb, offset, new_offset - offset); + /* Skip whitespace */ + offset = tvb_skip_wsp(tvb, new_offset+1, -1); + + /* Extract IP and Port in case of Offer/Answer */ + if ((tmp == 'u') || (tmp == 'l')){ + /* Extract IP */ + new_offset = tvb_find_guint8(tvb, offset, -1, ' '); + if (tvb_find_guint8(tvb, offset, new_offset - offset, ':') == -1) + proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_ipv4, tvb, offset, new_offset - offset, ENC_ASCII | ENC_NA); + else + proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_ipv6, tvb, offset, new_offset - offset, ENC_ASCII | ENC_NA); + /* Skip whitespace */ + offset = tvb_skip_wsp(tvb, new_offset+1, -1); + + /* Extract Port */ + new_offset = tvb_find_guint8(tvb, offset, -1, ' '); + proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_port, tvb, offset, new_offset - offset, ENC_ASCII | ENC_NA); + /* Skip whitespace */ + offset = tvb_skip_wsp(tvb, new_offset+1, -1); + } + + /* Extract Copy target */ + if (tmp == 'c'){ + new_offset = tvb_find_guint8(tvb, offset, -1, ' '); + proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_copy_target, tvb, offset, new_offset - offset, ENC_ASCII | ENC_NA); + /* Skip whitespace */ + offset = tvb_skip_wsp(tvb, new_offset+1, -1); + } + + /* Extract Playback file and codecs */ + if (tmp == 'p'){ + /* Extract filename */ + new_offset = tvb_find_guint8(tvb, offset, -1, ' '); + proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_playback_filename, tvb, offset, new_offset - offset, ENC_ASCII | ENC_NA); + /* Skip whitespace */ + offset = tvb_skip_wsp(tvb, new_offset+1, -1); + + /* Extract codec */ + new_offset = tvb_find_guint8(tvb, offset, -1, ' '); + proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_playback_codec, tvb, offset, new_offset - offset, ENC_ASCII | ENC_NA); + /* Skip whitespace */ + offset = tvb_skip_wsp(tvb, new_offset+1, -1); + } + + /* Extract first tag */ + new_offset = rtpproxy_add_tag(rtpproxy_tree, tvb, offset, realsize); + if(new_offset == -1) + break; /* No more parameters */ + /* Skip whitespace */ + offset = tvb_skip_wsp(tvb, new_offset+1, -1); + + /* Extract second tag */ + new_offset = rtpproxy_add_tag(rtpproxy_tree, tvb, offset, realsize); + if(new_offset == -1) + break; /* No more parameters */ + /* Skip whitespace */ + offset = tvb_skip_wsp(tvb, new_offset+1, -1); + + /* Extract Notification address */ + if (tmp == 'u'){ + ti = proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_notify, tvb, offset, realsize - offset, ENC_ASCII | ENC_NA); + proto_item_set_text(ti, "Notify"); + rtpproxy_tree = proto_item_add_subtree(ti, ett_rtpproxy_notify); + + /* Check for NotifyTag parameter (separated by space) */ + new_offset = tvb_find_guint8(tvb, offset, -1, ' '); + if(new_offset == -1){ + /* NotifyTag wasn't found (we should re-use Call-ID instead) */ + rtpproxy_add_notify_addr(rtpproxy_tree, tvb, offset, realsize); + break; /* No more parameters */ + } + + /* NotifyTag was found */ + rtpproxy_add_notify_addr(rtpproxy_tree, tvb, offset, new_offset); + /* Skip whitespace */ + offset = tvb_skip_wsp(tvb, new_offset+1, -1); + + proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_notify_tag, tvb, offset, realsize - offset, ENC_ASCII | ENC_NA); + } + break; + case 'a': + case 'e': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + rtpproxy_info = rtpproxy_add_tid(FALSE, tvb, pinfo, rtpproxy_tree, rtpproxy_conv, cookie); + if (tmp == 'e') + col_add_fstr(pinfo->cinfo, COL_INFO, "Error reply: %s", rawstr); + else + col_add_fstr(pinfo->cinfo, COL_INFO, "Reply: %s", rawstr); + + ti = proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_reply, tvb, offset, -1, ENC_NA); + rtpproxy_tree = proto_item_add_subtree(ti, ett_rtpproxy_reply); + + if(rtpproxy_info && rtpproxy_info->callid){ + ti = proto_tree_add_string(rtpproxy_tree, hf_rtpproxy_callid, tvb, offset, 0, rtpproxy_info->callid); + PROTO_ITEM_SET_GENERATED(ti); + } + + if (tmp == 'e'){ + tmp = tvb_find_line_end(tvb, offset, -1, &new_offset, FALSE); + tmpstr = tvb_get_ephemeral_string(tvb, offset, tmp); + ti = proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_error, tvb, offset, (gint)strlen(tmpstr), ENC_ASCII | ENC_NA); + proto_item_append_text(ti, " (%s)", str_to_str(tmpstr, errortypenames, "Unknown")); + break; + } + + if (tmp == 'a'){ + /* A specific case - short statistics answer */ + /* %COOKIE% active sessions: %NUM1% */ + proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_status, tvb, offset, realsize - offset, ENC_ASCII | ENC_NA); + break; + } + if ((tmp == '0')&& ((tvb_reported_length(tvb) == (guint)(offset+1))||(tvb_reported_length(tvb) == (guint)(offset+2)))){ + proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_ok, tvb, offset, 1, ENC_ASCII | ENC_NA); + break; + } + if ((tmp == '1') && ((tvb_reported_length(tvb) == (guint)(offset+1))||(tvb_reported_length(tvb) == (guint)(offset+2)))){ + proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_ok, tvb, offset, 1, ENC_ASCII | ENC_NA); + break; + } + if (tvb_reported_length(tvb) == (guint)(offset+9)){ + proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_version_supported, tvb, offset, 8, ENC_ASCII | ENC_NA); + break; + } + + /* Extract Port */ + new_offset = tvb_find_guint8(tvb, offset, -1, ' '); + /* Convert port to unsigned 16-bit number */ + port = (guint16) g_ascii_strtoull((gchar*)tvb_get_ephemeral_string(tvb, offset, new_offset - offset), NULL, 10); + proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_port, tvb, offset, new_offset - offset, ENC_ASCII | ENC_NA); + /* Skip whitespace */ + offset = tvb_skip_wsp(tvb, new_offset+1, -1); + + /* Extract IP */ + tmp = tvb_find_line_end(tvb, offset, -1, &new_offset, FALSE); + if (tvb_find_guint8(tvb, offset, -1, ':') == -1){ + inet_pton(AF_INET, (char*)tvb_get_ephemeral_string(tvb, offset, tmp), &ipaddr); + addr.type = AT_IPv4; + addr.len = 4; + addr.data = ep_memdup(&ipaddr, 4); + proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_ipv4, tvb, offset, tmp, ENC_ASCII | ENC_NA); + } + else{ + inet_pton(AF_INET6, (char*)tvb_get_ephemeral_string(tvb, offset, tmp), &ipaddr); + addr.type = AT_IPv6; + addr.len = 16; + addr.data = ep_memdup(&ipaddr, 16); + proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_ipv6, tvb, offset, tmp, ENC_ASCII | ENC_NA); + } + + if(rtpproxy_establish_conversation){ + if (rtp_handle) { + /* FIXME tell if isn't a video stream, and setup codec mapping */ + rtp_add_address(pinfo, &addr, port, 0, "RTPproxy", pinfo->fd->num, 0, NULL); + } + if (rtcp_handle) { + rtcp_add_address(pinfo, &addr, port+1, 0, "RTPproxy", pinfo->fd->num); + } + } + break; + default: + break; + } + if (has_lf) + proto_tree_add_item(rtpproxy_tree, hf_rtpproxy_lf, tvb, realsize, 1, ENC_NA); + + return tvb_length(tvb); +} + +void +proto_register_rtpproxy(void) +{ + module_t *rtpproxy_module; + + static hf_register_info hf[] = { + { + &hf_rtpproxy_cookie, + { + "Cookie", + "rtpproxy.cookie", + FT_STRING, + BASE_NONE, + NULL, + 0x0, + NULL, + HFILL + } + }, + { + &hf_rtpproxy_version_request, + { + "Version Request", + "rtpproxy.version", + FT_STRING, + BASE_NONE, + NULL, + 0x0, + NULL, + HFILL + } + }, + { + &hf_rtpproxy_version_supported, + { + "Version Supported", + "rtpproxy.version_supported", + FT_STRING, + BASE_NONE, + NULL, + 0x0, + NULL, + HFILL + } + }, + { + &hf_rtpproxy_error, + { + "Error", + "rtpproxy.error", + FT_STRING, + BASE_NONE, + NULL, + 0x0, + NULL, + HFILL + } + }, + { + &hf_rtpproxy_ok, + { + "Ok", + "rtpproxy.ok", + FT_UINT8, + BASE_DEC, + VALS(oktypenames), + 0x0, + NULL, + HFILL + } + }, + { + &hf_rtpproxy_status, + { + "Status", + "rtpproxy.status", + FT_STRING, + BASE_NONE, + NULL, + 0x0, + NULL, + HFILL + } + }, + { + &hf_rtpproxy_ipv4, + { + "IPv4", + "rtpproxy.ipv4", + FT_STRING, + BASE_NONE, + NULL, + 0x0, + NULL, + HFILL + } + }, + { + &hf_rtpproxy_ipv6, + { + "IPv6", + "rtpproxy.ipv6", + FT_STRING, + BASE_NONE, + NULL, + 0x0, + NULL, + HFILL + } + }, + { + &hf_rtpproxy_port, + { + "Port", + "rtpproxy.port", + FT_STRING, + BASE_NONE, + NULL, + 0x0, + NULL, + HFILL + } + }, + { + &hf_rtpproxy_request, + { + "Request", + "rtpproxy.request", + FT_NONE, + BASE_NONE, + NULL, + 0x0, + NULL, + HFILL + } + }, + { + &hf_rtpproxy_command, + { + "Command", + "rtpproxy.command", + FT_UINT8, + BASE_DEC, + VALS(commandtypenames), + 0x0, + NULL, + HFILL + } + }, + { + &hf_rtpproxy_command_parameters, + { + "Command parameters", + "rtpproxy.command_parameters", + FT_STRING, + BASE_NONE, + NULL, + 0x0, + NULL, + HFILL + } + }, + { + &hf_rtpproxy_command_parameter, + { + "Parameter", + "rtpproxy.command_parameter", + FT_UINT8, + BASE_DEC, + VALS(paramtypenames), + 0x0, + NULL, + HFILL + } + }, + { + &hf_rtpproxy_command_parameter_codec, + { + "Allowed codec", + "rtpproxy.command_parameter_codec", + FT_STRING, + BASE_NONE, + NULL, + 0x0, + NULL, + HFILL + } + }, + { + &hf_rtpproxy_command_parameter_local, + { + "Local IP address", + "rtpproxy.command_parameter_local", + FT_STRING, + BASE_NONE, + NULL, + 0x0, + NULL, + HFILL + } + }, + { + &hf_rtpproxy_command_parameter_remote, + { + "Remote IP address", + "rtpproxy.command_parameter_remote", + FT_STRING, + BASE_NONE, + NULL, + 0x0, + NULL, + HFILL + } + }, + { + &hf_rtpproxy_command_parameter_repacketize, + { + "Repacketize (ms)", + "rtpproxy.command_parameter_repacketize", + FT_STRING, + BASE_NONE, + NULL, + 0x0, + NULL, + HFILL + } + }, + { + &hf_rtpproxy_command_parameter_dtmf, + { + "DTMF payload ID", + "rtpproxy.command_parameter_dtmf", + FT_STRING, + BASE_NONE, + NULL, + 0x0, + NULL, + HFILL + } + }, + { + &hf_rtpproxy_command_parameter_proto, + { + "RTP tramsission protocol", + "rtpproxy.command_parameter_proto", + FT_UINT8, + BASE_DEC, + VALS(prototypenames), + 0x0, + NULL, + HFILL + } + }, + { + &hf_rtpproxy_command_parameter_transcode, + { + "Transcode to", + "rtpproxy.command_parameter_transcode", + FT_STRING, + BASE_NONE, + NULL, + 0x0, + NULL, + HFILL + } + }, + { + &hf_rtpproxy_command_parameter_acc, + { + "Accounting", + "rtpproxy.command_parameter_acc", + FT_UINT8, + BASE_DEC, + VALS(acctypenames), + 0x0, + NULL, + HFILL + } + }, + { + &hf_rtpproxy_copy_target, + { + "Copy target", + "rtpproxy.copy_target", + FT_STRING, + BASE_NONE, + NULL, + 0x0, + NULL, + HFILL + } + }, + { + &hf_rtpproxy_playback_filename, + { + "Playback filename", + "rtpproxy.playback_filename", + FT_STRING, + BASE_NONE, + NULL, + 0x0, + NULL, + HFILL + } + }, + { + &hf_rtpproxy_playback_codec, + { + "Playback codec", + "rtpproxy.playback_codec", + FT_STRING, + BASE_NONE, + NULL, + 0x0, + NULL, + HFILL + } + }, + { + &hf_rtpproxy_callid, + { + "Call-ID", + "rtpproxy.callid", + FT_STRING, + BASE_NONE, + NULL, + 0x0, + NULL, + HFILL + } + }, + { + &hf_rtpproxy_notify, + { + "Notify", + "rtpproxy.notify", + FT_STRING, + BASE_NONE, + NULL, + 0x0, + NULL, + HFILL + } + }, + { + &hf_rtpproxy_tag, + { + "Tag", + "rtpproxy.tag", + FT_STRING, + BASE_NONE, + NULL, + 0x0, + NULL, + HFILL + } + }, + { + &hf_rtpproxy_mediaid, + { + "Media-ID", + "rtpproxy.mediaid", + FT_STRING, + BASE_NONE, + NULL, + 0x0, + NULL, + HFILL + } + }, + { + &hf_rtpproxy_notify_ipv4, + { + "Notification IPv4", + "rtpproxy.notify_ipv4", + FT_STRING, + BASE_NONE, + NULL, + 0x0, + NULL, + HFILL + } + }, + { + &hf_rtpproxy_notify_ipv6, + { + "Notification IPv6", + "rtpproxy.notify_ipv6", + FT_STRING, + BASE_NONE, + NULL, + 0x0, + NULL, + HFILL + } + }, + { + &hf_rtpproxy_notify_port, + { + "Notification Port", + "rtpproxy.notify_port", + FT_STRING, + BASE_NONE, + NULL, + 0x0, + NULL, + HFILL + } + }, + { + &hf_rtpproxy_notify_tag, + { + "Notification Tag", + "rtpproxy.notify_tag", + FT_STRING, + BASE_NONE, + NULL, + 0x0, + NULL, + HFILL + } + }, + { + &hf_rtpproxy_reply, + { + "Reply", + "rtpproxy.reply", + FT_NONE, + BASE_NONE, + NULL, + 0x0, + NULL, + HFILL + } + }, + { + &hf_rtpproxy_lf, + { + "LF", + "rtpproxy.lf", + FT_UINT8, + BASE_DEC, + VALS(flowcontroltypenames), + 0x0, + NULL, + HFILL + } + }, + { + &hf_rtpproxy_request_in, + { + "Request In", + "rtpproxy.request_in", + FT_FRAMENUM, + BASE_NONE, + NULL, + 0x0, + NULL, + HFILL + } + + }, + { + &hf_rtpproxy_response_in, + { + "Response In", + "rtpproxy.response_in", + FT_FRAMENUM, + BASE_NONE, + NULL, + 0x0, + NULL, + HFILL + } + }, + { + &hf_rtpproxy_response_time, + { + "Response Time", + "rtpproxy.response_time", + FT_RELATIVE_TIME, + BASE_NONE, + NULL, + 0x0, + "The time between the Request and the Reply", + HFILL + } + } + }; + + /* Setup protocol subtree array */ + static gint *ett[] = { + &ett_rtpproxy, + &ett_rtpproxy_request, + &ett_rtpproxy_command, + &ett_rtpproxy_command_parameters, + &ett_rtpproxy_command_parameters_codecs, + &ett_rtpproxy_command_parameters_local, + &ett_rtpproxy_command_parameters_remote, + &ett_rtpproxy_command_parameters_repacketize, + &ett_rtpproxy_command_parameters_dtmf, + &ett_rtpproxy_command_parameters_cmap, + &ett_rtpproxy_command_parameters_proto, + &ett_rtpproxy_command_parameters_transcode, + &ett_rtpproxy_command_parameters_acc, + &ett_rtpproxy_tag, + &ett_rtpproxy_notify, + &ett_rtpproxy_reply + }; + + proto_rtpproxy = proto_register_protocol ( + "Sippy RTPproxy Protocol", /* name */ + "RTPproxy", /* short name */ + "rtpproxy" /* abbrev */ + ); + + proto_register_field_array(proto_rtpproxy, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + + rtpproxy_module = prefs_register_protocol(proto_rtpproxy, proto_reg_handoff_rtpproxy); + + prefs_register_uint_preference(rtpproxy_module, "tcp.port", + "RTPproxy TCP Port", /* Title */ + "RTPproxy TCP Port", /* Descr */ + 10, + &rtpproxy_tcp_port); + + prefs_register_uint_preference(rtpproxy_module, "udp.port", + "RTPproxy UDP Port", /* Title */ + "RTPproxy UDP Port", /* Descr */ + 10, + &rtpproxy_udp_port); + + prefs_register_bool_preference(rtpproxy_module, "establish_conversation", + "Establish Media Conversation", + "Specifies that RTP/RTCP/T.38/MSRP/etc streams are decoded based " + "upon port numbers found in RTPproxy answers", + &rtpproxy_establish_conversation); + + prefs_register_uint_preference(rtpproxy_module, "reply.timeout", + "RTPproxy reply timeout", /* Title */ + "Maximum timeout value in waiting for reply from RTPProxy (in milliseconds).", /* Descr */ + 10, + &rtpproxy_timeout); +} + +void +proto_reg_handoff_rtpproxy(void) +{ + static guint old_rtpproxy_tcp_port = 0; + static guint old_rtpproxy_udp_port = 0; + + static gboolean rtpproxy_initialized = FALSE; + + static dissector_handle_t rtpproxy_tcp_handle, rtpproxy_udp_handle; + + if(!rtpproxy_initialized){ + rtpproxy_tcp_handle = new_create_dissector_handle(dissect_rtpproxy, proto_rtpproxy); + rtpproxy_udp_handle = new_create_dissector_handle(dissect_rtpproxy, proto_rtpproxy); + rtpproxy_initialized = TRUE; + } + + /* Register TCP port for dissection */ + if(old_rtpproxy_tcp_port != 0 && old_rtpproxy_tcp_port != rtpproxy_tcp_port) + dissector_delete_uint("tcp.port", old_rtpproxy_tcp_port, rtpproxy_tcp_handle); + if(rtpproxy_tcp_port != 0 && old_rtpproxy_tcp_port != rtpproxy_tcp_port) + dissector_add_uint("tcp.port", rtpproxy_tcp_port, rtpproxy_tcp_handle); + old_rtpproxy_tcp_port = rtpproxy_tcp_port; + + /* Register UDP port for dissection */ + if(old_rtpproxy_udp_port != 0 && old_rtpproxy_udp_port != rtpproxy_udp_port) + dissector_delete_uint("udp.port", old_rtpproxy_udp_port, rtpproxy_udp_handle); + if(rtpproxy_udp_port != 0 && old_rtpproxy_udp_port != rtpproxy_udp_port) + dissector_add_uint("udp.port", rtpproxy_udp_port, rtpproxy_udp_handle); + old_rtpproxy_udp_port = rtpproxy_udp_port; + + rtcp_handle = find_dissector("rtcp"); + rtp_events_handle = find_dissector("rtpevent"); + rtp_handle = find_dissector("rtp"); + + /* Calculate nstime_t struct for the timeout from the rtpproxy_timeout value in milliseconds */ + rtpproxy_timeout_ns.secs = (rtpproxy_timeout - rtpproxy_timeout % 1000) / 1000; + rtpproxy_timeout_ns.nsecs = (rtpproxy_timeout % 1000) * 1000; +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 8 + * tab-width: 8 + * indent-tabs-mode: t + * End: + * + * vi: set shiftwidth=8 tabstop=8 noexpandtab: + * :indentSize=8:tabSize=8:noTabs=false: + */