GSM A DTAP: add UMTS EVS to supported codecs list IE
[metze/wireshark/wip.git] / epan / dissectors / packet-rtsp.c
index 3bbbc4abc249e160f8934674a2dc00a03aea5af7..295c91bc00806571b275ca960bb5ae580726da3d 100644 (file)
@@ -8,19 +8,7 @@
  * By Gerald Combs <gerald@wireshark.org>
  * Copyright 1998 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.
+ * SPDX-License-Identifier: GPL-2.0-or-later
  *
  * References:
  * RTSP is defined in RFC 2326, http://www.ietf.org/rfc/rfc2326.txt?number=2326
 
 #include "config.h"
 
-#include <stdio.h>
-#include <stdlib.h>
+#include <stdio.h>     /* for sscanf() */
 
 #include <glib.h>
-
 #include <epan/packet.h>
 #include <epan/req_resp_hdrs.h>
 #include <epan/prefs.h>
 #include <epan/conversation.h>
+#include <epan/expert.h>
 #include <epan/strutil.h>
-#include <epan/tap.h>
 #include <epan/tap-voip.h>
 #include <epan/stats_tree.h>
-#include <epan/wmem/wmem.h>
+#include <epan/addr_resolv.h>
 #include <wsutil/str_util.h>
+#include <wsutil/strtoi.h>
 
 #include "packet-rdt.h"
 #include "packet-rtp.h"
@@ -135,13 +122,26 @@ static int hf_rtsp_X_Vig_Msisdn = -1;
 static int hf_rtsp_magic = -1;
 static int hf_rtsp_channel = -1;
 static int hf_rtsp_length = -1;
+static int hf_rtsp_data = -1;
 
 static int voip_tap = -1;
 
+static expert_field ei_rtsp_unknown_transport_type = EI_INIT;
+static expert_field ei_rtsp_bad_server_port = EI_INIT;
+static expert_field ei_rtsp_bad_client_port = EI_INIT;
+static expert_field ei_rtsp_bad_interleaved_channel = EI_INIT;
+static expert_field ei_rtsp_content_length_invalid = EI_INIT;
+static expert_field ei_rtsp_rdtfeaturelevel_invalid = EI_INIT;
+static expert_field ei_rtsp_bad_server_ip_address = EI_INIT;
+static expert_field ei_rtsp_bad_client_ip_address = EI_INIT;
+
+static dissector_handle_t rtsp_handle;
 static dissector_handle_t rtp_handle;
+static dissector_handle_t rtp_rfc4571_handle;
 static dissector_handle_t rtcp_handle;
 static dissector_handle_t rdt_handle;
 static dissector_table_t media_type_dissector_table;
+static heur_dissector_list_t heur_subdissector_list;
 
 static const gchar *st_str_packets = "Total RTSP Packets";
 static const gchar *st_str_requests = "RTSP Request Packets";
@@ -247,8 +247,6 @@ static gboolean rtsp_desegment_body = TRUE;
  */
 #define RTSP_TCP_PORT_RANGE           "554,8554,7236"
 
-static range_t *global_rtsp_tcp_port_range = NULL;
-static range_t *rtsp_tcp_port_range        = NULL;
 /*
  * Takes an array of bytes, assumed to contain a null-terminated
  * string, as an argument, and returns the length of the string -
@@ -294,7 +292,7 @@ dissect_rtspinterleaved(tvbuff_t *tvb, int offset, packet_info *pinfo,
      * That's what we want.  (See "tcp_dissect_pdus()", which is
      * similar.)
      */
-    length_remaining = tvb_ensure_length_remaining(tvb, offset);
+    length_remaining = tvb_ensure_captured_length_remaining(tvb, offset);
 
     /*
      * Can we do reassembly?
@@ -356,11 +354,11 @@ dissect_rtspinterleaved(tvbuff_t *tvb, int offset, packet_info *pinfo,
         rf_chan, rf_len);
     rtspframe_tree = proto_item_add_subtree(ti, ett_rtspframe);
 
-    proto_tree_add_item(rtspframe_tree, hf_rtsp_magic, tvb, offset, 1, ENC_NA);
+    proto_tree_add_item(rtspframe_tree, hf_rtsp_magic, tvb, offset, 1, ENC_BIG_ENDIAN);
 
     offset += 1;
 
-    proto_tree_add_item(rtspframe_tree, hf_rtsp_channel, tvb, offset, 1, ENC_NA);
+    proto_tree_add_item(rtspframe_tree, hf_rtsp_channel, tvb, offset, 1, ENC_BIG_ENDIAN);
 
     offset += 1;
 
@@ -378,10 +376,9 @@ dissect_rtspinterleaved(tvbuff_t *tvb, int offset, packet_info *pinfo,
      */
     if (length_remaining > rf_len)
         length_remaining = rf_len;
-    next_tvb = tvb_new_subset(tvb, offset, length_remaining, rf_len);
+    next_tvb = tvb_new_subset_length_caplen(tvb, offset, length_remaining, rf_len);
 
-    conv = find_conversation(pinfo->fd->num, &pinfo->src, &pinfo->dst, pinfo->ptype,
-        pinfo->srcport, pinfo->destport, 0);
+    conv = find_conversation_pinfo(pinfo, 0);
 
     if (conv &&
         (data = (rtsp_conversation_data_t *)conversation_get_proto_data(conv, proto_rtsp)) &&
@@ -391,8 +388,15 @@ dissect_rtspinterleaved(tvbuff_t *tvb, int offset, packet_info *pinfo,
         (dissector = data->interleaved[rf_chan].dissector)) {
         call_dissector(dissector, next_tvb, pinfo, tree);
     } else {
-        proto_tree_add_text(rtspframe_tree, tvb, offset, rf_len,
-            "Data (%u bytes)", rf_len);
+        gboolean dissected = FALSE;
+        heur_dtbl_entry_t *hdtbl_entry = NULL;
+
+        dissected = dissector_try_heuristic(heur_subdissector_list,
+                            next_tvb, pinfo, tree, &hdtbl_entry, NULL);
+
+        if (!dissected) {
+            proto_tree_add_item(rtspframe_tree, hf_rtsp_data, tvb, offset, rf_len, ENC_NA);
+        }
     }
 
     offset += rf_len;
@@ -452,7 +456,7 @@ is_rtsp_request_or_reply(const guchar *line, size_t linelen, rtsp_type_t *type)
             if (tokenlen >= 3) {
                 memcpy(response_chars, token, 3);
                 response_chars[3] = '\0';
-                rtsp_stat_info->response_code = (guint)strtoul(response_chars, NULL, 10);
+                ws_strtou32(response_chars, NULL, &rtsp_stat_info->response_code);
             }
         }
         return TRUE;
@@ -483,26 +487,44 @@ is_rtsp_request_or_reply(const guchar *line, size_t linelen, rtsp_type_t *type)
 
 static const char rtsp_content_type[]      = "Content-Type:";
 static const char rtsp_transport[]         = "Transport:";
-static const char rtsp_sps[]               = "server_port=";
-static const char rtsp_cps[]               = "client_port=";
-static const char rtsp_rtp[]               = "rtp/";
+static const char rtsp_sps_server_port[]   = "server_port=";
+static const char rtsp_cps_server_port[]   = "client_port=";
+static const char rtsp_sps_dest_addr[]     = "dest_addr=";
+static const char rtsp_cps_src_addr[]      = "src_addr=";
+static const char rtsp_rtp_udp_default[]   = "rtp/avp";
+static const char rtsp_rtp_udp[]           = "rtp/avp/udp";
+static const char rtsp_rtp_tcp[]           = "rtp/avp/tcp";
 static const char rtsp_rdt_feature_level[] = "RDTFeatureLevel";
 static const char rtsp_real_rdt[]          = "x-real-rdt/";
 static const char rtsp_real_tng[]          = "x-pn-tng/"; /* synonym for x-real-rdt */
 static const char rtsp_inter[]             = "interleaved=";
 
 static void
-rtsp_create_conversation(packet_info *pinfo, const guchar *line_begin,
-                         size_t line_len, gint rdt_feature_level)
+rtsp_create_conversation(packet_info *pinfo, proto_item *ti,
+                         const guchar *line_begin, size_t line_len,
+                         gint rdt_feature_level,
+                         rtsp_type_t rtsp_type_packet)
 {
     conversation_t  *conv;
     guchar    buf[256];
     guchar   *tmp;
-    gboolean  rtp_transport = FALSE;
+    gboolean  rtp_udp_transport = FALSE;
+    gboolean  rtp_tcp_transport = FALSE;
     gboolean  rdt_transport = FALSE;
     guint     c_data_port, c_mon_port;
     guint     s_data_port, s_mon_port;
+    guint     ipv4_1, ipv4_2, ipv4_3, ipv4_4;
     gboolean  is_video      = FALSE; /* FIX ME - need to indicate video or not */
+    address   src_addr;
+    address   dst_addr;
+    guint32   ip4_addr;
+
+    if (rtsp_type_packet != RTSP_REPLY) {
+        return;
+    }
+
+    src_addr=pinfo->src;
+    dst_addr=pinfo->dst;
 
     /* Copy line into buf */
     if (line_len > sizeof(buf) - 1)
@@ -519,15 +541,27 @@ rtsp_create_conversation(packet_info *pinfo, const guchar *line_begin,
         tmp++;
 
     /* Work out which transport type is here */
-    if (g_ascii_strncasecmp(tmp, rtsp_rtp, strlen(rtsp_rtp)) == 0)
-        rtp_transport = TRUE;
-    else
-    if (g_ascii_strncasecmp(tmp, rtsp_real_rdt, strlen(rtsp_real_rdt)) == 0 ||
-        g_ascii_strncasecmp(tmp, rtsp_real_tng, strlen(rtsp_real_tng)) == 0)
+    if (g_ascii_strncasecmp(tmp, rtsp_rtp_udp, strlen(rtsp_rtp_udp)) == 0)
+    {
+        rtp_udp_transport = TRUE;
+    }
+    else if (g_ascii_strncasecmp(tmp, rtsp_rtp_tcp, strlen(rtsp_rtp_tcp)) == 0)
+    {
+        rtp_tcp_transport = TRUE;
+    }
+    else if (g_ascii_strncasecmp(tmp, rtsp_rtp_udp_default, strlen(rtsp_rtp_udp_default)) == 0)
+    {
+        rtp_udp_transport = TRUE;
+    }
+    else if (g_ascii_strncasecmp(tmp, rtsp_real_rdt, strlen(rtsp_real_rdt)) == 0 ||
+                 g_ascii_strncasecmp(tmp, rtsp_real_tng, strlen(rtsp_real_tng)) == 0)
+    {
         rdt_transport = TRUE;
+    }
     else
     {
         /* Give up on unknown transport types */
+        expert_add_info(pinfo, ti, &ei_rtsp_unknown_transport_type);
         return;
     }
 
@@ -535,47 +569,103 @@ rtsp_create_conversation(packet_info *pinfo, const guchar *line_begin,
     s_data_port = s_mon_port = 0;
 
     /* Look for server port */
-    if ((tmp = strstr(buf, rtsp_sps))) {
-        tmp += strlen(rtsp_sps);
+    if ((tmp = strstr(buf, rtsp_sps_server_port))) {
+        tmp += strlen(rtsp_sps_server_port);
         if (sscanf(tmp, "%u-%u", &s_data_port, &s_mon_port) < 1) {
-            g_warning("Frame %u: rtsp: bad server_port",
-                pinfo->fd->num);
+            expert_add_info(pinfo, ti, &ei_rtsp_bad_server_port);
+            return;
+        }
+    }
+    else if ((tmp = strstr(buf, rtsp_sps_dest_addr))) {
+        tmp += strlen(rtsp_sps_dest_addr);
+        if (sscanf(tmp, "\":%u\"", &s_data_port) == 1) {
+            /* :9 mean ignore */
+            if (s_data_port == 9) {
+                s_data_port = 0;
+            }
+        }
+        else if (sscanf(tmp, "\"%u.%u.%u.%u:%u\"", &ipv4_1, &ipv4_2, &ipv4_3, &ipv4_4, &s_data_port) == 5) {
+            guchar *tmp2;
+            guchar *tmp3;
+
+            /* Skip leading " */
+            tmp++;
+            tmp2=strstr(tmp,":");
+            tmp3=g_strndup(tmp,tmp2-tmp);
+            if (!str_to_ip(tmp3, &ip4_addr)) {
+                g_free(tmp3);
+                expert_add_info(pinfo, ti, &ei_rtsp_bad_server_ip_address);
+                return;
+            }
+            set_address(&dst_addr, AT_IPv4, 4, &ip4_addr);
+            g_free(tmp3);
+        }
+        else if (sscanf(tmp, "\"%u.%u.%u.%u\"", &ipv4_1, &ipv4_2, &ipv4_3, &ipv4_4) == 4) {
+            guchar *tmp2;
+            guchar *tmp3;
+
+            /* Skip leading " */
+            tmp++;
+            tmp2=strstr(tmp,"\"");
+            tmp3=g_strndup(tmp,tmp2-tmp);
+            if (!str_to_ip(tmp3, &ip4_addr)) {
+                g_free(tmp3);
+                expert_add_info(pinfo, ti, &ei_rtsp_bad_server_ip_address);
+                return;
+            }
+            set_address(&dst_addr, AT_IPv4, 4, &ip4_addr);
+            g_free(tmp3);
+        }
+        else
+        {
+            expert_add_info(pinfo, ti, &ei_rtsp_bad_server_port);
             return;
         }
     }
+
+
     /* Look for client port */
-    if ((tmp = strstr(buf, rtsp_cps))) {
-        tmp += strlen(rtsp_cps);
+    if ((tmp = strstr(buf, rtsp_cps_server_port))) {
+        tmp += strlen(rtsp_cps_server_port);
         if (sscanf(tmp, "%u-%u", &c_data_port, &c_mon_port) < 1) {
-            g_warning("Frame %u: rtsp: bad client_port",
-                pinfo->fd->num);
+            expert_add_info(pinfo, ti, &ei_rtsp_bad_client_port);
             return;
         }
     }
-
+    else if ((tmp = strstr(buf, rtsp_cps_src_addr))) {
+        tmp += strlen(rtsp_cps_src_addr);
+        if (sscanf(tmp, "\"%u.%u.%u.%u:%u\"", &ipv4_1, &ipv4_2, &ipv4_3, &ipv4_4, &c_data_port) == 5) {
+            guchar *tmp2;
+            guchar *tmp3;
+
+            /* Skip leading " */
+            tmp++;
+            tmp2=strstr(tmp,":");
+            tmp3=g_strndup(tmp,tmp2-tmp);
+            if (!str_to_ip(tmp3, &ip4_addr)) {
+                g_free(tmp3);
+                expert_add_info(pinfo, ti, &ei_rtsp_bad_client_ip_address);
+                return;
+            }
+            set_address(&src_addr, AT_IPv4, 4, &ip4_addr);
+            g_free(tmp3);
+        }
+    }
 
     /* Deal with RTSP TCP-interleaved conversations. */
-    if (!c_data_port) {
+    tmp = strstr(buf, rtsp_inter);
+    if (tmp != NULL) {
         rtsp_conversation_data_t    *data;
         guint               s_data_chan, s_mon_chan;
         int             i;
 
-        /* Search tranport line for interleaved string */
-        if ((tmp = strstr(buf, rtsp_inter)) == NULL) {
-            /*
-             * No interleaved or server_port - probably a
-             * SETUP request, rather than reply.
-             */
-            return;
-        }
-
-        /* Move tmp to beyone interleaved string */
+        /* Move tmp to beyond interleaved string */
         tmp += strlen(rtsp_inter);
         /* Look for channel number(s) */
         i = sscanf(tmp, "%u-%u", &s_data_chan, &s_mon_chan);
         if (i < 1)
         {
-            g_warning("Frame %u: rtsp: bad interleaved", pinfo->fd->num);
+            expert_add_info(pinfo, ti, &ei_rtsp_bad_interleaved_channel);
             return;
         }
 
@@ -594,7 +684,7 @@ rtsp_create_conversation(packet_info *pinfo, const guchar *line_begin,
 
         /* Now set the dissector handle of the interleaved channel
            according to the transport protocol used */
-        if (rtp_transport)
+        if (rtp_tcp_transport)
         {
             if (s_data_chan < RTSP_MAX_INTERLEAVED) {
                 data->interleaved[s_data_chan].dissector =
@@ -614,7 +704,7 @@ rtsp_create_conversation(packet_info *pinfo, const guchar *line_begin,
         }
         return;
     }
-
+    /* Noninterleaved options follow */
     /*
      * We only want to match on the destination address, not the
      * source address, because the server might send back a packet
@@ -622,26 +712,40 @@ rtsp_create_conversation(packet_info *pinfo, const guchar *line_begin,
      * sent the packet, so we construct a conversation with no
      * second address.
      */
-    if (rtp_transport)
+    else if (rtp_udp_transport)
     {
-        /* There is always data for RTP */
-        rtp_add_address(pinfo, &pinfo->dst, c_data_port, s_data_port,
-                        "RTSP", pinfo->fd->num, is_video, NULL);
+        /* RTP only if indicated */
+        if (c_data_port)
+        {
+            rtp_add_address(pinfo, PT_UDP, &dst_addr, c_data_port, s_data_port,
+                            "RTSP", pinfo->num, is_video, NULL);
+        }
+        else if (s_data_port)
+        {
+            rtp_add_address(pinfo, PT_UDP, &src_addr, s_data_port, 0,
+                            "RTSP", pinfo->num, is_video, NULL);
+        }
 
         /* RTCP only if indicated */
         if (c_mon_port)
         {
             rtcp_add_address(pinfo, &pinfo->dst, c_mon_port, s_mon_port,
-                             "RTSP", pinfo->fd->num);
+                             "RTSP", pinfo->num);
         }
     }
-    else
-    if (rdt_transport)
+    else if (rtp_tcp_transport)
+    {
+        /* RTP only if indicated */
+        rtp_add_address(pinfo, PT_TCP, &src_addr, c_data_port, s_data_port,
+                        "RTSP", pinfo->num, is_video, NULL);
+    }
+    else if (rdt_transport)
     {
         /* Real Data Transport */
         rdt_add_address(pinfo, &pinfo->dst, c_data_port, s_data_port,
                         "RTSP", rdt_feature_level);
     }
+    return;
 }
 
 static const char rtsp_content_length[] = "Content-Length:";
@@ -651,9 +755,9 @@ rtsp_get_content_length(const guchar *line_begin, size_t line_len)
 {
     guchar  buf[256];
     guchar *tmp;
-    long    content_length;
-    char   *p;
-    guchar *up;
+    guint32 content_length;
+    const char *p;
+    const guchar *up;
 
     if (line_len > sizeof(buf) - 1) {
         /*
@@ -667,7 +771,7 @@ rtsp_get_content_length(const guchar *line_begin, size_t line_len)
     tmp = buf + STRLEN_CONST(rtsp_content_length);
     while (*tmp && g_ascii_isspace(*tmp))
         tmp++;
-    content_length = strtol(tmp, &p, 10);
+    ws_strtoi32(tmp, &p, &content_length);
     up = p;
     if (up == tmp || (*up != '\0' && !g_ascii_isspace(*up)))
         return -1;  /* not a valid number */
@@ -683,7 +787,7 @@ dissect_rtspmessage(tvbuff_t *tvb, int offset, packet_info *pinfo,
 {
     proto_tree   *rtsp_tree = NULL;
     proto_tree   *sub_tree  = NULL;
-    proto_item   *ti        = NULL;
+    proto_item   *ti_top    = NULL;
     const guchar *line;
     gint          next_offset;
     const guchar *linep, *lineend;
@@ -695,7 +799,8 @@ dissect_rtspmessage(tvbuff_t *tvb, int offset, packet_info *pinfo,
     gboolean      body_requires_content_len;
     gboolean      saw_req_resp_or_header;
     guchar        c;
-    rtsp_type_t   rtsp_type;
+    rtsp_type_t   rtsp_type_packet;
+    rtsp_type_t   rtsp_type_line;
     gboolean      is_header;
     int           datalen;
     int           content_length;
@@ -712,7 +817,7 @@ dissect_rtspmessage(tvbuff_t *tvb, int offset, packet_info *pinfo,
     voip_packet_info_t *stat_info = NULL;
 
     rtsp_stat_info = wmem_new(wmem_packet_scope(), rtsp_info_value_t);
-    rtsp_stat_info->framenum = pinfo->fd->num;
+    rtsp_stat_info->framenum = pinfo->num;
     rtsp_stat_info->response_code = 0;
     rtsp_stat_info->request_method = NULL;
     rtsp_stat_info->request_uri = NULL;
@@ -725,16 +830,14 @@ dissect_rtspmessage(tvbuff_t *tvb, int offset, packet_info *pinfo,
      * is not longer than what's in the buffer, so the
      * "tvb_get_ptr()" call won't throw an exception.
      */
-    first_linelen = tvb_find_line_end(tvb, offset,
-        tvb_ensure_length_remaining(tvb, offset), &next_offset,
-        FALSE);
+    first_linelen = tvb_find_line_end(tvb, offset, -1, &next_offset, FALSE);
 
     /*
      * Is the first line a request or response?
      */
     line = tvb_get_ptr(tvb, offset, first_linelen);
     is_request_or_reply = is_rtsp_request_or_reply(line, first_linelen,
-        &rtsp_type);
+        &rtsp_type_packet);
     if (is_request_or_reply) {
         /*
          * Yes, it's a request or response.
@@ -757,7 +860,11 @@ dissect_rtspmessage(tvbuff_t *tvb, int offset, packet_info *pinfo,
      * of a server closing the connection indicating the end of
      * a reply body.
      *
-     * We assume that an absent content length in a request means
+     * To support pipelining, we check if line behind blank line
+     * looks like RTSP header. If so, we process rest of packet with
+     * RTSP loop.
+     *
+     * If no, we assume that an absent content length in a request means
      * that we don't have a body, and that an absent content length
      * in a reply means that the reply body runs to the end of
      * the connection.  If the first line is neither, we assume
@@ -772,20 +879,19 @@ dissect_rtspmessage(tvbuff_t *tvb, int offset, packet_info *pinfo,
      * which would require subdissectors to deal intelligently
      * with empty segments.
      */
-    if (rtsp_type == RTSP_REQUEST)
+    if (rtsp_type_packet == RTSP_REQUEST)
         body_requires_content_len = TRUE;
     else
         body_requires_content_len = FALSE;
 
     line = tvb_get_ptr(tvb, offset, first_linelen);
     if (is_request_or_reply) {
-        if ( rtsp_type == RTSP_REPLY ) {
+        if ( rtsp_type_packet == RTSP_REPLY ) {
             frame_label = wmem_strdup_printf(wmem_packet_scope(),
-                  "Reply: %s", format_text(line, first_linelen));
+                  "Reply: %s", format_text(wmem_packet_scope(), line, first_linelen));
         }
         else {
-            frame_label = wmem_strdup(wmem_packet_scope(),
-                  format_text(line, first_linelen));
+            frame_label = format_text(wmem_packet_scope(), line, first_linelen);
         }
     }
 
@@ -801,14 +907,14 @@ dissect_rtspmessage(tvbuff_t *tvb, int offset, packet_info *pinfo,
         * "tvb_get_ptr()" call won't throw an exception.
         */
     if (is_request_or_reply)
-        if ( rtsp_type == RTSP_REPLY ) {
+        if ( rtsp_type_packet == RTSP_REPLY ) {
             col_set_str(pinfo->cinfo, COL_INFO, "Reply: ");
             col_append_str(pinfo->cinfo, COL_INFO,
-                format_text(line, first_linelen));
+                format_text(wmem_packet_scope(), line, first_linelen));
         }
         else {
             col_add_str(pinfo->cinfo, COL_INFO,
-                format_text(line, first_linelen));
+                format_text(wmem_packet_scope(), line, first_linelen));
         }
 
     else
@@ -816,9 +922,9 @@ dissect_rtspmessage(tvbuff_t *tvb, int offset, packet_info *pinfo,
 
     orig_offset = offset;
     if (tree) {
-        ti = proto_tree_add_item(tree, proto_rtsp, tvb, offset, -1,
-            ENC_NA);
-        rtsp_tree = proto_item_add_subtree(ti, ett_rtsp);
+        ti_top = proto_tree_add_item(tree, proto_rtsp, tvb, offset, -1,
+                                     ENC_NA);
+        rtsp_tree = proto_item_add_subtree(ti_top, ett_rtsp);
     }
 
     /*
@@ -830,7 +936,7 @@ dissect_rtspmessage(tvbuff_t *tvb, int offset, packet_info *pinfo,
      * Process the packet data, a line at a time.
      */
     saw_req_resp_or_header = FALSE; /* haven't seen anything yet */
-    while (tvb_reported_length_remaining(tvb, offset) != 0) {
+    while (tvb_offset_exists(tvb, offset)) {
         /*
          * We haven't yet concluded that this is a header.
          */
@@ -839,9 +945,7 @@ dissect_rtspmessage(tvbuff_t *tvb, int offset, packet_info *pinfo,
         /*
          * Find the end of the line.
          */
-        linelen = tvb_find_line_end(tvb, offset,
-            tvb_ensure_length_remaining(tvb, offset), &next_offset,
-            FALSE);
+        linelen = tvb_find_line_end(tvb, offset, -1, &next_offset, FALSE);
         if (linelen < 0)
             return -1;
         line_end_offset = offset + linelen;
@@ -860,7 +964,7 @@ dissect_rtspmessage(tvbuff_t *tvb, int offset, packet_info *pinfo,
         /*
          * OK, does it look like an RTSP request or response?
          */
-        is_request_or_reply = is_rtsp_request_or_reply(line, linelen, &rtsp_type);
+        is_request_or_reply = is_rtsp_request_or_reply(line, linelen, &rtsp_type_line);
         if (is_request_or_reply)
             goto is_rtsp;
 
@@ -879,14 +983,14 @@ dissect_rtspmessage(tvbuff_t *tvb, int offset, packet_info *pinfo,
             c = *linep++;
 
             /*
-             * This must be a CHAR, and not a CTL, to be part of a token;
-             * that means it must be printable ASCII.
+             * This must be a CHAR, and must not be a CTL, to be part
+             * of a token; that means it must be printable ASCII.
              *
              * XXX - what about leading LWS on continuation
              * lines of a header?
              */
             if (!g_ascii_isprint(c))
-                break;  /* not ASCII, thus not a CHAR, or a CTL */
+                break;
 
             switch (c) {
 
@@ -987,7 +1091,7 @@ dissect_rtspmessage(tvbuff_t *tvb, int offset, packet_info *pinfo,
         saw_req_resp_or_header = TRUE;
         if (rtsp_tree) {
 
-            switch (rtsp_type)
+            switch (rtsp_type_line)
             {
                 case RTSP_REQUEST:
                     process_rtsp_request(tvb, offset, line, linelen, next_offset, rtsp_tree);
@@ -1025,10 +1129,11 @@ dissect_rtspmessage(tvbuff_t *tvb, int offset, packet_info *pinfo,
 
             if (HDR_MATCHES(rtsp_transport))
             {
-                proto_tree_add_string(rtsp_tree, hf_rtsp_transport, tvb,
-                                      offset, linelen,
-                                      tvb_format_text(tvb, value_offset,
-                                                      value_len));
+                proto_item *ti;
+                ti = proto_tree_add_string(rtsp_tree, hf_rtsp_transport, tvb,
+                                           offset, linelen,
+                                           tvb_format_text(tvb, value_offset,
+                                                           value_len));
 
                 /*
                  * Based on the port numbers specified
@@ -1036,7 +1141,7 @@ dissect_rtspmessage(tvbuff_t *tvb, int offset, packet_info *pinfo,
                  * a conversation that will be dissected
                  * with the appropriate dissector.
                  */
-                rtsp_create_conversation(pinfo, line, linelen, rdt_feature_level);
+                rtsp_create_conversation(pinfo, ti, line, linelen, rdt_feature_level, rtsp_type_packet);
             } else if (HDR_MATCHES(rtsp_content_type))
             {
                 proto_tree_add_string(rtsp_tree, hf_rtsp_content_type,
@@ -1059,10 +1164,15 @@ dissect_rtspmessage(tvbuff_t *tvb, int offset, packet_info *pinfo,
 
             } else if (HDR_MATCHES(rtsp_content_length))
             {
-                proto_tree_add_uint(rtsp_tree, hf_rtsp_content_length,
-                                    tvb, offset, linelen,
-                                    atoi(tvb_format_text(tvb, value_offset,
-                                                         value_len)));
+                guint32 clength;
+                gboolean clength_valid;
+                proto_item* pi;
+                clength_valid = ws_strtou32(tvb_format_text(tvb, value_offset, value_len),
+                    NULL, &clength);
+                pi = proto_tree_add_uint(rtsp_tree, hf_rtsp_content_length,
+                                    tvb, offset, linelen, clength);
+                if (!clength_valid)
+                    expert_add_info(pinfo, pi, &ei_rtsp_content_length_invalid);
 
                 /*
                  * Only the amount specified by the
@@ -1085,6 +1195,7 @@ dissect_rtspmessage(tvbuff_t *tvb, int offset, packet_info *pinfo,
                  */
                 if (colon_offset != -1)
                 {
+                    proto_item *ti;
                     /* Put the value into the protocol tree */
                     ti = proto_tree_add_string(rtsp_tree, hf_rtsp_X_Vig_Msisdn,tvb,
                                                offset, linelen ,
@@ -1102,12 +1213,14 @@ dissect_rtspmessage(tvbuff_t *tvb, int offset, packet_info *pinfo,
                 }
             } else if (HDR_MATCHES(rtsp_rdt_feature_level))
             {
-                rdt_feature_level = atoi(tvb_format_text(tvb, value_offset,
-                                                         value_len));
-                proto_tree_add_uint(rtsp_tree, hf_rtsp_rdtfeaturelevel,
-                                    tvb, offset, linelen,
-                                    atoi(tvb_format_text(tvb, value_offset,
-                                                         value_len)));
+                gboolean rdt_feature_level_valid;
+                proto_item* pi;
+                rdt_feature_level_valid = ws_strtou32(tvb_format_text(tvb, value_offset, value_len),
+                    NULL, &rdt_feature_level);
+                pi = proto_tree_add_uint(rtsp_tree, hf_rtsp_rdtfeaturelevel,
+                tvb, offset, linelen, rdt_feature_level);
+                if (!rdt_feature_level_valid)
+                    expert_add_info(pinfo, pi, &ei_rtsp_rdtfeaturelevel_invalid);
             }
             else
             {
@@ -1115,7 +1228,7 @@ dissect_rtspmessage(tvbuff_t *tvb, int offset, packet_info *pinfo,
                 proto_tree_add_format_text(rtsp_tree, tvb, offset, next_offset - offset);
             }
         }
-        else if (rtsp_type == RTSP_NOT_FIRST_LINE)
+        else if (rtsp_type_line == RTSP_NOT_FIRST_LINE)
         {
             /* Catch-all for all other lines... Show line as text.
                TODO: should these be shown as errors? */
@@ -1147,7 +1260,7 @@ dissect_rtspmessage(tvbuff_t *tvb, int offset, packet_info *pinfo,
      * was supplied), the amount of data to be processed is the amount
      * of data remaining in the frame.
      */
-    datalen = tvb_length_remaining(tvb, offset);
+    datalen = tvb_captured_length_remaining(tvb, offset);
     reported_datalen = tvb_reported_length_remaining(tvb, offset);
     if (content_length != -1) {
         /*
@@ -1204,44 +1317,56 @@ dissect_rtspmessage(tvbuff_t *tvb, int offset, packet_info *pinfo,
          * which, if no content length was specified,
          * is -1, i.e. "to the end of the frame.
          */
-        new_tvb = tvb_new_subset(tvb, offset, datalen,
+        new_tvb = tvb_new_subset_length_caplen(tvb, offset, datalen,
                 reported_datalen);
 
-        if (media_type_str_lower_case &&
-            dissector_try_string(media_type_dissector_table,
-                media_type_str_lower_case,
-                new_tvb, pinfo, rtsp_tree, NULL)){
+        /*
+         * Check if next line is RTSP message - pipelining
+         * If yes, stop processing and start next loop
+         * If no, process rest of packet with dissectors
+         */
+        first_linelen = tvb_find_line_end(new_tvb, 0, -1, &next_offset, FALSE);
+        line = tvb_get_ptr(new_tvb, 0, first_linelen);
+        is_request_or_reply = is_rtsp_request_or_reply(line, first_linelen,
+            &rtsp_type_packet);
 
-        }else {
-            /*
-             * Fix up the top-level item so that it doesn't
-             * include the SDP stuff.
-             */
-            if (ti != NULL)
-                proto_item_set_len(ti, offset);
+        if (!is_request_or_reply){
+            if (media_type_str_lower_case &&
+                dissector_try_string(media_type_dissector_table,
+                    media_type_str_lower_case,
+                    new_tvb, pinfo, rtsp_tree, NULL)){
 
-            if (tvb_get_guint8(tvb, offset) == RTSP_FRAMEHDR) {
+            } else {
                 /*
-                 * This is interleaved stuff; don't
-                 * treat it as raw data - set "datalen"
-                 * to 0, so we won't skip the offset
-                 * past it, which will cause our
-                 * caller to process that stuff itself.
+                 * Fix up the top-level item so that it doesn't
+                 * include the SDP stuff.
                  */
-                datalen = 0;
-            } else {
-                proto_tree_add_text(rtsp_tree, tvb, offset,
-                    datalen, "Data (%d bytes)",
-                    reported_datalen);
+                if (ti_top != NULL)
+                    proto_item_set_len(ti_top, offset);
+
+                if (tvb_get_guint8(tvb, offset) == RTSP_FRAMEHDR) {
+                    /*
+                     * This is interleaved stuff; don't
+                     * treat it as raw data - set "datalen"
+                     * to 0, so we won't skip the offset
+                     * past it, which will cause our
+                     * caller to process that stuff itself.
+                     */
+                    datalen = 0;
+                } else {
+                    proto_tree_add_bytes_format(rtsp_tree, hf_rtsp_data, tvb, offset,
+                        datalen, NULL, "Data (%d bytes)",
+                        reported_datalen);
+                }
             }
-        }
 
-        /*
-         * We've processed "datalen" bytes worth of data
-         * (which may be no data at all); advance the
-         * offset past whatever data we've processed.
-         */
-        offset += datalen;
+            /*
+             * We've processed "datalen" bytes worth of data
+             * (which may be no data at all); advance the
+             * offset past whatever data we've processed.
+             */
+            offset += datalen;
+        }
     }
 
     tap_queue_packet(rtsp_tap, pinfo, rtsp_stat_info);
@@ -1350,13 +1475,20 @@ process_rtsp_reply(tvbuff_t *tvb, int offset, const guchar *data,
                         (gint) (status - status_start), status_i);
 }
 
-static void
-dissect_rtsp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+static int
+dissect_rtsp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
 {
     int offset = 0;
     int len;
 
     while (tvb_reported_length_remaining(tvb, offset) != 0) {
+        /*
+         * Add separator between multiple messages in column info text
+         */
+        if (offset > 0) {
+                col_set_str(pinfo->cinfo, COL_INFO, ", ");
+                col_set_fence(pinfo->cinfo, COL_INFO);
+        }
         len = (tvb_get_guint8(tvb, offset) == RTSP_FRAMEHDR)
             ? dissect_rtspinterleaved(tvb, offset, pinfo, tree)
             : dissect_rtspmessage(tvb, offset, pinfo, tree);
@@ -1366,11 +1498,12 @@ dissect_rtsp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 
         /*
          * OK, we've set the Protocol and Info columns for the
-         * first RTSP message; make the columns non-writable,
-         * so that we don't change it for subsequent RTSP messages.
+         * first RTSP message; set fence so changes are kept for
+         * subsequent RTSP messages.
          */
-        col_set_writable(pinfo->cinfo, FALSE);
+        col_set_fence(pinfo->cinfo, COL_INFO);
     }
+    return tvb_captured_length(tvb);
 }
 
 void
@@ -1424,30 +1557,50 @@ proto_register_rtsp(void)
         { &hf_rtsp_length,
             { "Length", "rtsp.length", FT_UINT16, BASE_DEC, NULL, 0x0,
             NULL, HFILL }},
+        { &hf_rtsp_data,
+            { "Data", "rtsp.data", FT_BYTES, BASE_NONE, NULL, 0x0,
+            NULL, HFILL }},
     };
+
+    static ei_register_info ei[] = {
+        { &ei_rtsp_unknown_transport_type,
+          { "rtsp.unknown_transport_type", PI_UNDECODED, PI_WARN, "Unknown transport type",  EXPFILL }},
+        { &ei_rtsp_bad_server_port,
+          { "rtsp.bad_server_port", PI_UNDECODED, PI_WARN, "Bad server_port",  EXPFILL }},
+        { &ei_rtsp_bad_client_port,
+          { "rtsp.bad_client_port", PI_UNDECODED, PI_WARN, "Bad client port",  EXPFILL }},
+        { &ei_rtsp_bad_interleaved_channel,
+          { "rtsp.bad_interleaved_channel", PI_UNDECODED, PI_WARN, "Bad interleaved_channel",  EXPFILL }},
+        { &ei_rtsp_content_length_invalid,
+          { "rtsp.content-length.invalid", PI_MALFORMED, PI_ERROR, "Invalid content length", EXPFILL }},
+        { &ei_rtsp_rdtfeaturelevel_invalid,
+          { "rtsp.rdt-feature-level.invalid", PI_MALFORMED, PI_ERROR, "Invalid RDTFeatureLevel", EXPFILL }},
+        { &ei_rtsp_bad_server_ip_address,
+          { "rtsp.bad_client_ip_address", PI_MALFORMED, PI_ERROR, "Bad server IP address", EXPFILL }},
+        { &ei_rtsp_bad_client_ip_address,
+          { "rtsp.bad_client_ip_address", PI_MALFORMED, PI_ERROR, "Bad client IP address", EXPFILL }}
+    };
+
     module_t *rtsp_module;
+    expert_module_t *expert_rtsp;
 
-    proto_rtsp = proto_register_protocol("Real Time Streaming Protocol",
-        "RTSP", "rtsp");
+    proto_rtsp = proto_register_protocol("Real Time Streaming Protocol", "RTSP", "rtsp");
 
     proto_register_field_array(proto_rtsp, hf, array_length(hf));
     proto_register_subtree_array(ett, array_length(ett));
 
+    expert_rtsp = expert_register_protocol(proto_rtsp);
+    expert_register_field_array(expert_rtsp, ei, array_length(ei));
+
     /* Make this dissector findable by name */
-    register_dissector("rtsp", dissect_rtsp, proto_rtsp);
+    rtsp_handle = register_dissector("rtsp", dissect_rtsp, proto_rtsp);
 
     /* Register our configuration options, particularly our ports */
 
-    rtsp_module = prefs_register_protocol(proto_rtsp, proto_reg_handoff_rtsp);
+    rtsp_module = prefs_register_protocol(proto_rtsp, NULL);
 
     prefs_register_obsolete_preference(rtsp_module, "tcp.alternate_port");
-    prefs_register_obsolete_preference(rtsp_module, "tcp.port");
 
-    range_convert_str(&global_rtsp_tcp_port_range, RTSP_TCP_PORT_RANGE, 65535);
-    rtsp_tcp_port_range = range_empty();
-    prefs_register_range_preference(rtsp_module, "tcp.port_range", "RTSP TCP Ports",
-                                    "RTSP TCP Ports range",
-                                    &global_rtsp_tcp_port_range, 65535);
     prefs_register_bool_preference(rtsp_module, "desegment_headers",
         "Reassemble RTSP headers spanning multiple TCP segments",
         "Whether the RTSP dissector should reassemble headers "
@@ -1455,13 +1608,19 @@ proto_register_rtsp(void)
         " To use this option, you must also enable \"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
         &rtsp_desegment_headers);
     prefs_register_bool_preference(rtsp_module, "desegment_body",
-        "Trust the \"Content-length:\" header and\ndesegment RTSP "
-        "bodies\nspanning multiple TCP segments",
+        "Trust the \"Content-length:\" header when desegmenting",
         "Whether the RTSP dissector should use the "
         "\"Content-length:\" value to desegment the body "
         "of a request spanning multiple TCP segments",
         &rtsp_desegment_body);
 
+    /*
+     * Heuristic dissectors SHOULD register themselves in
+     * this table using the standard heur_dissector_add()
+     * function.
+     */
+    heur_subdissector_list = register_heur_dissector_list("rtsp", proto_rtsp);
+
     /*
      * Register for tapping
      */
@@ -1471,25 +1630,15 @@ proto_register_rtsp(void)
 void
 proto_reg_handoff_rtsp(void)
 {
-    static dissector_handle_t rtsp_handle;
-    static gboolean rtsp_prefs_initialized = FALSE;
-
-    if (!rtsp_prefs_initialized) {
-        rtsp_handle = find_dissector("rtsp");
-        rtp_handle = find_dissector("rtp");
-        rtcp_handle = find_dissector("rtcp");
-        rdt_handle = find_dissector("rdt");
-        media_type_dissector_table = find_dissector_table("media_type");
-        voip_tap = find_tap_id("voip");
-        rtsp_prefs_initialized = TRUE;
-    }
-    else {
-        dissector_delete_uint_range("tcp.port", rtsp_tcp_port_range, rtsp_handle);
-        g_free(rtsp_tcp_port_range);
-    }
+    rtp_handle = find_dissector_add_dependency("rtp", proto_rtsp);
+    rtp_rfc4571_handle = find_dissector_add_dependency("rtp.rfc4571", proto_rtsp);
+    rtcp_handle = find_dissector_add_dependency("rtcp", proto_rtsp);
+    rdt_handle = find_dissector_add_dependency("rdt", proto_rtsp);
+    media_type_dissector_table = find_dissector_table("media_type");
+    voip_tap = find_tap_id("voip");
+
     /* Set our port number for future use */
-    rtsp_tcp_port_range = range_copy(global_rtsp_tcp_port_range);
-    dissector_add_uint_range("tcp.port", rtsp_tcp_port_range, rtsp_handle);
+    dissector_add_uint_range_with_preference("tcp.port", RTSP_TCP_PORT_RANGE, rtsp_handle);
 
     /* XXX: Do the following only once ?? */
     stats_tree_register("rtsp","rtsp","RTSP/Packet Counter", 0, rtsp_stats_tree_packet, rtsp_stats_tree_init, NULL );
@@ -1502,7 +1651,7 @@ proto_reg_handoff_rtsp(void)
  * Local variables:
  * c-basic-offset: 4
  * tab-width: 8
- * indent-tabs-mode: nil
+ * indent-tabs-mode: space
  * End:
  *
  * vi: set shiftwidth=4 tabstop=8 expandtab: