Use "gfloat" and "gdouble", rather than "float" and "double", as the
[obnox/wireshark/wip.git] / packet-rtsp.c
index b2d6de0266ca9f40d2cccecb31553433eef15194..7d9eda7f5200d89133b9aa1cfdb3afbad476ae85 100644 (file)
@@ -4,12 +4,11 @@
  * Jason Lango <jal@netapp.com>
  * Liberally copied from packet-http.c, by Guy Harris <guy@alum.mit.edu>
  *
- * $Id: packet-rtsp.c,v 1.24 2000/11/12 21:23:53 guy Exp $
+ * $Id: packet-rtsp.c,v 1.47 2002/01/21 07:36:41 guy Exp $
  *
  * Ethereal - Network traffic analyzer
- * By Gerald Combs <gerald@zing.org>
+ * By Gerald Combs <gerald@ethereal.com>
  * 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
@@ -24,8 +23,6 @@
  * 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
- *
- *
  */
 
 #include "config.h"
 
 #include <string.h>
 #include <ctype.h>
+#include <stdlib.h>
 
 #include <glib.h>
-#include "packet.h"
-#include "packet-sdp.h"
+#include <epan/packet.h>
 #include "packet-rtp.h"
 #include "packet-rtcp.h"
-#include "conversation.h"
-#include "strutil.h"
+#include <epan/conversation.h>
+#include <epan/strutil.h>
 
 static int proto_rtsp = -1;
 static gint ett_rtsp = -1;
 
+static gint ett_rtspframe = -1;
+
 static int hf_rtsp_method = -1;
 static int hf_rtsp_url = -1;
 static int hf_rtsp_status = -1;
 
+static dissector_handle_t sdp_handle;
+static dissector_handle_t rtp_handle;
+static dissector_handle_t rtcp_handle;
+
+static GMemChunk *rtsp_vals = NULL;
+#define rtsp_hash_init_count 20
+
 #define TCP_PORT_RTSP                  554
 
+/*
+ * Takes an array of bytes, assumed to contain a null-terminated
+ * string, as an argument, and returns the length of the string -
+ * i.e., the size of the array, minus 1 for the null terminator.
+ */
+#define STRLEN_CONST(str)      (sizeof (str) - 1)
+
+#define RTSP_FRAMEHDR  ('$')
+
+typedef struct {
+       dissector_handle_t              dissector;
+} rtsp_interleaved_t;
+
+#define RTSP_MAX_INTERLEAVED           (8)
+
+/*
+ * Careful about dynamically allocating memory in this structure (say
+ * for dynamically increasing the size of the 'interleaved' array) -
+ * the containing structure is garbage collected and contained
+ * pointers will not be freed.
+ */
+typedef struct {
+       rtsp_interleaved_t              interleaved[RTSP_MAX_INTERLEAVED];
+} rtsp_conversation_data_t;
+
+static int
+dissect_rtspinterleaved(tvbuff_t *tvb, int offset, packet_info *pinfo,
+       proto_tree *tree)
+{
+       proto_tree      *rtspframe_tree;
+       proto_item      *ti;
+       int             orig_offset;
+       guint8          rf_start;       /* always RTSP_FRAMEHDR */
+       guint8          rf_chan;        /* interleaved channel id */
+       guint16         rf_len;         /* packet length */
+       gint            framelen;
+       tvbuff_t        *next_tvb;
+       conversation_t  *conv;
+       rtsp_conversation_data_t        *data;
+       dissector_handle_t              dissector;
+
+       orig_offset = offset;
+       rf_start = tvb_get_guint8(tvb, offset);
+       rf_chan = tvb_get_guint8(tvb, offset+1);
+       rf_len = tvb_get_ntohs(tvb, offset+2);
+
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_add_fstr(pinfo->cinfo, COL_INFO, 
+                       "Interleaved channel 0x%02x, %u bytes",
+                       rf_chan, rf_len);
+
+       if (tree == NULL) {
+               /*
+                * We're not building a full protocol tree; all we care
+                * about is setting the column info.
+                */
+               return -1;
+       }
+
+       ti = proto_tree_add_protocol_format(tree, proto_rtsp, tvb, offset, 4,
+               "RTSP Interleaved Frame, Channel: 0x%02x, %u bytes",
+               rf_chan, rf_len);
+       rtspframe_tree = proto_item_add_subtree(ti, ett_rtspframe);
+
+       proto_tree_add_text(rtspframe_tree, tvb, offset, 1,
+               "Magic: 0x%02x",
+               rf_start);
+       offset += 1;
+
+       proto_tree_add_text(rtspframe_tree, tvb, offset, 1,
+               "Channel: 0x%02x",
+               rf_chan);
+       offset += 1;
+
+       proto_tree_add_text(rtspframe_tree, tvb, offset, 2,
+               "Length: %u bytes",
+               rf_len);
+       offset += 2;
+
+       /*
+        * We set the actual length of the tvbuff for the interleaved
+        * stuff to the minimum of what's left in the tvbuff and the
+        * length in the header.
+        *
+        * XXX - what if there's nothing left in the tvbuff?
+        * We'd want a BoundsError exception to be thrown, so
+        * that a Short Frame would be reported.
+        */
+       framelen = tvb_length_remaining(tvb, offset);
+       if (framelen > rf_len)
+               framelen = rf_len;
+       next_tvb = tvb_new_subset(tvb, offset, framelen, rf_len);
+
+       conv = find_conversation(&pinfo->src, &pinfo->dst, pinfo->ptype,
+               pinfo->srcport, pinfo->destport, 0);
+
+       if (conv &&
+           (data = conversation_get_proto_data(conv, proto_rtsp)) &&
+           rf_chan < RTSP_MAX_INTERLEAVED &&
+           (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);
+       }
+
+       offset += rf_len;
+
+       return offset - orig_offset;
+}
+
 static void process_rtsp_request(tvbuff_t *tvb, int offset, const u_char *data,
-       int linelen, proto_tree *tree);
+       size_t linelen, proto_tree *tree);
 
 static void process_rtsp_reply(tvbuff_t *tvb, int offset, const u_char *data,
-       int linelen, proto_tree *tree);
+       size_t linelen, proto_tree *tree);
 
 typedef enum {
        RTSP_REQUEST,
@@ -75,9 +192,9 @@ static const char *rtsp_methods[] = {
 #define RTSP_NMETHODS  (sizeof rtsp_methods / sizeof rtsp_methods[0])
 
 static rtsp_type_t
-is_rtsp_request_or_reply(const u_char *line, int linelen)
+is_rtsp_request_or_reply(const u_char *line, size_t linelen)
 {
-       int             ii;
+       unsigned        ii;
 
        /* Is this an RTSP reply? */
        if (linelen >= 5 && strncasecmp("RTSP/", line, 5) == 0) {
@@ -101,113 +218,207 @@ is_rtsp_request_or_reply(const u_char *line, int linelen)
        return NOT_RTSP;
 }
 
+static const char rtsp_content_type[] = "Content-Type:";
+
 static int
-is_content_sdp(const u_char *line, int linelen)
+is_content_sdp(const u_char *line, size_t linelen)
 {
-       const char      *hdr = "Content-Type:";
-       size_t          hdrlen = strlen(hdr);
-       const char      *type = "application/sdp";
-       size_t          typelen = strlen(type);
-
-       if (linelen < hdrlen || strncasecmp(hdr, line, hdrlen))
-               return 0;
+       static const char type[] = "application/sdp";
+       size_t          typelen = STRLEN_CONST(type);
 
-       line += hdrlen;
-       linelen -= hdrlen;
+       line += STRLEN_CONST(rtsp_content_type);
+       linelen -= STRLEN_CONST(rtsp_content_type);
        while (linelen > 0 && (*line == ' ' || *line == '\t')) {
                line++;
                linelen--;
        }
 
        if (linelen < typelen || strncasecmp(type, line, typelen))
-               return 0;
+               return FALSE;
 
        line += typelen;
        linelen -= typelen;
        if (linelen > 0 && !isspace(*line))
-               return 0;
+               return FALSE;
 
-       return 1;
+       return TRUE;
 }
 
 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/avp";
+static const char rtsp_rtp[] = "rtp/";
+static const char rtsp_inter[] = "interleaved=";
 
 static void
-rtsp_create_conversation(const u_char *trans_begin, const u_char *trans_end)
+rtsp_create_conversation(packet_info *pinfo, const u_char *line_begin,
+       size_t line_len)
 {
        conversation_t  *conv;
-       u_char          tbuf[256];
+       u_char          buf[256];
        u_char          *tmp;
-       int             c_data_port, c_mon_port;
-       int             s_data_port, s_mon_port;
+       u_int           c_data_port, c_mon_port;
+       u_int           s_data_port, s_mon_port;
+       address         null_addr;
 
-       strncpy(tbuf, trans_begin, trans_end - trans_begin);
-       tbuf[sizeof(tbuf)-1] = 0;
+       if (line_len > sizeof(buf) - 1) {
+               /*
+                * Don't overflow the buffer.
+                */
+               line_len = sizeof(buf) - 1;
+       }
+       memcpy(buf, line_begin, line_len);
+       buf[line_len] = '\0';
 
-       tmp = tbuf + strlen(rtsp_transport);
+       tmp = buf + STRLEN_CONST(rtsp_transport);
        while (*tmp && isspace(*tmp))
                tmp++;
-       if (strncasecmp(tmp, rtsp_rtp, strlen(rtsp_rtp)) != 0)
+       if (strncasecmp(tmp, rtsp_rtp, strlen(rtsp_rtp)) != 0) {
+               g_warning("Frame %u: rtsp: unknown transport", pinfo->fd->num);
                return;
+       }
 
        c_data_port = c_mon_port = 0;
        s_data_port = s_mon_port = 0;
-       if ((tmp = strstr(tbuf, rtsp_sps))) {
+       if ((tmp = strstr(buf, rtsp_sps))) {
                tmp += strlen(rtsp_sps);
-               if (sscanf(tmp, "%u-%u", &s_data_port, &s_mon_port) < 1)
-                       g_warning("rtsp: failed to parse 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);
+                       return;
+               }
        }
-       if ((tmp = strstr(tbuf, rtsp_cps))) {
+       if ((tmp = strstr(buf, rtsp_cps))) {
                tmp += strlen(rtsp_cps);
-               if (sscanf(tmp, "%u-%u", &c_data_port, &c_mon_port) < 1)
-                       g_warning("rtsp: failed to parse client_port");
+               if (sscanf(tmp, "%u-%u", &c_data_port, &c_mon_port) < 1) {
+                       g_warning("Frame %u: rtsp: bad client_port",
+                               pinfo->fd->num);
+                       return;
+               }
        }
-       if (!c_data_port || !s_data_port)
+       if (!c_data_port) {
+               rtsp_conversation_data_t        *data;
+               u_int                           s_data_chan, s_mon_chan;
+               int                             i;
+
+               /*
+                * Deal with RTSP TCP-interleaved conversations.
+                */
+               if ((tmp = strstr(buf, rtsp_inter)) == NULL) {
+                       /*
+                        * No interleaved or server_port - probably a
+                        * SETUP request, rather than reply.
+                        */
+                       return;
+               }
+               tmp += strlen(rtsp_inter);
+               i = sscanf(tmp, "%u-%u", &s_data_chan, &s_mon_chan);
+               if (i < 1) {
+                       g_warning("Frame %u: rtsp: bad interleaved",
+                               pinfo->fd->num);
+                       return;
+               }
+               conv = find_conversation(&pinfo->src, &pinfo->dst, pinfo->ptype,
+                       pinfo->srcport, pinfo->destport, 0);
+               if (!conv) {
+                       conv = conversation_new(&pinfo->src, &pinfo->dst,
+                               pinfo->ptype, pinfo->srcport, pinfo->destport,
+                               0);
+               }
+               data = conversation_get_proto_data(conv, proto_rtsp);
+               if (!data) {
+                       data = g_mem_chunk_alloc(rtsp_vals);
+                       conversation_add_proto_data(conv, proto_rtsp, data);
+               }
+               if (s_data_chan < RTSP_MAX_INTERLEAVED) {
+                       data->interleaved[s_data_chan].dissector =
+                               rtp_handle;
+               }
+               if (i > 1 && s_mon_chan < RTSP_MAX_INTERLEAVED) {
+                       data->interleaved[s_mon_chan].dissector =
+                               rtcp_handle;
+               }
                return;
+       }
 
-       conv = conversation_new(&pi.src, &pi.dst, PT_UDP, s_data_port,
-               c_data_port, 0, 0);
-       conversation_set_dissector(conv, dissect_rtp);
+       /*
+        * We only want to match on the destination address, not the
+        * source address, because the server might send back a packet
+        * from an address other than the address to which its client
+        * sent the packet, so we construct a conversation with no
+        * second address.
+        */
+       SET_ADDRESS(&null_addr, pinfo->src.type, 0, NULL);
 
-       if (!c_mon_port || !s_mon_port)
+       conv = conversation_new(&pinfo->dst, &null_addr, PT_UDP, c_data_port,
+               s_data_port, NO_ADDR2 | (!s_data_port ? NO_PORT2 : 0));
+       conversation_set_dissector(conv, rtp_handle);
+
+       if (!c_mon_port)
                return;
 
-       conv = conversation_new(&pi.src, &pi.dst, PT_UDP, s_mon_port,
-               c_mon_port, 0, 0);
-       conversation_set_dissector(conv, dissect_rtcp);
+       conv = conversation_new(&pinfo->dst, &null_addr, PT_UDP, c_mon_port,
+               s_mon_port, NO_ADDR2 | (!s_mon_port ? NO_PORT2 : 0));
+       conversation_set_dissector(conv, rtcp_handle);
 }
 
-static void
-dissect_rtsp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+static const char rtsp_content_length[] = "Content-Length:";
+
+static int
+rtsp_get_content_length(const u_char *line_begin, size_t line_len)
+{
+       u_char          buf[256];
+       u_char          *tmp;
+       long            content_length;
+       char            *p;
+       u_char          *up;
+
+       if (line_len > sizeof(buf) - 1) {
+               /*
+                * Don't overflow the buffer.
+                */
+               line_len = sizeof(buf) - 1;
+       }
+       memcpy(buf, line_begin, line_len);
+       buf[line_len] = '\0';
+
+       tmp = buf + STRLEN_CONST(rtsp_content_length);
+       while (*tmp && isspace(*tmp))
+               tmp++;
+       content_length = strtol(tmp, &p, 10);
+       up = p;
+       if (up == tmp || (*up != '\0' && !isspace(*up)))
+               return -1;      /* not a valid number */
+       return content_length;
+}
+
+static int
+dissect_rtspmessage(tvbuff_t *tvb, int offset, packet_info *pinfo,
+       proto_tree *tree)
 {
        proto_tree      *rtsp_tree;
        proto_item      *ti = NULL;
-       gint            offset = 0;
        const u_char    *line;
        gint            next_offset;
        const u_char    *linep, *lineend;
-       int             linelen;
+       int             orig_offset;
+       size_t          linelen;
        u_char          c;
+       gboolean        is_mime_header;
        int             is_sdp = FALSE;
        int             datalen;
+       int             content_length;
+       int             reported_datalen;
 
-       CHECK_DISPLAY_AS_DATA(proto_rtsp, tvb, pinfo, tree);
-
-       pinfo->current_proto = "RTSP";
-
+       orig_offset = offset;
        rtsp_tree = NULL;
        if (tree) {
-               ti = proto_tree_add_item(tree, proto_rtsp, tvb, offset,
-                       tvb_length_remaining(tvb, offset), FALSE);
+               ti = proto_tree_add_item(tree, proto_rtsp, tvb, offset, -1,
+                   FALSE);
                rtsp_tree = proto_item_add_subtree(ti, ett_rtsp);
        }
 
-       if (check_col(pinfo->fd, COL_PROTOCOL))
-               col_add_str(pinfo->fd, COL_PROTOCOL, "RTSP");
-       if (check_col(pinfo->fd, COL_INFO)) {
+       if (check_col(pinfo->cinfo, COL_INFO)) {
                /*
                 * Put the first line from the buffer into the summary
                 * if it's an RTSP request or reply (but leave out the
@@ -220,20 +431,30 @@ dissect_rtsp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 
                case RTSP_REQUEST:
                case RTSP_REPLY:
-                       col_add_str(pinfo->fd, COL_INFO,
+                       col_add_str(pinfo->cinfo, COL_INFO,
                            format_text(line, linelen));
                        break;
 
                default:
-                       col_add_str(pinfo->fd, COL_INFO, "Continuation");
+                       col_set_str(pinfo->cinfo, COL_INFO, "Continuation");
                        break;
                }
        }
 
+       /*
+        * We haven't yet seen a Content-Length header.
+        */
+       content_length = -1;
+
        /*
         * Process the packet data, a line at a time.
         */
-       while (tvb_length_remaining(tvb, offset)) {
+       while (tvb_offset_exists(tvb, offset)) {
+               /*
+                * We haven't yet concluded that this is a MIME header.
+                */
+               is_mime_header = FALSE;
+
                /*
                 * Find the end of the line.
                 */
@@ -313,8 +534,15 @@ dissect_rtsp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
                                 * This ends the token; we consider
                                 * this to be a MIME header.
                                 */
-                               if (is_content_sdp(line, linelen))
-                                       is_sdp = TRUE;
+                               is_mime_header = TRUE;
+                               goto is_rtsp;
+
+                       case ' ':
+                       case '\t':
+                               /*
+                                * LWS (RFC-2616, 4.2); continue the previous
+                                * header.
+                                */
                                goto is_rtsp;
                        }
                }
@@ -335,16 +563,78 @@ dissect_rtsp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
                            next_offset - offset, "%s",
                            tvb_format_text(tvb, offset, next_offset - offset));
                }
-               if (linelen > strlen(rtsp_transport) &&
-                       strncasecmp(line, rtsp_transport,
-                               strlen(rtsp_transport)) == 0)
-                       rtsp_create_conversation(line, line + linelen);
+               if (is_mime_header) {
+                       /*
+                        * Process some MIME headers specially.
+                        */
+#define MIME_HDR_MATCHES(header) \
+       (linelen > STRLEN_CONST(header) && \
+        strncasecmp(line, (header), STRLEN_CONST(header)) == 0)
+
+                       if (MIME_HDR_MATCHES(rtsp_transport)) {
+                               /*
+                                * Based on the port numbers specified
+                                * in the Transport: header, set up
+                                * a conversation that will be dissected
+                                * with the appropriate dissector.
+                                */
+                               rtsp_create_conversation(pinfo, line, linelen);
+                       } else if (MIME_HDR_MATCHES(rtsp_content_type)) {
+                               /*
+                                * If the Content-Type: header says this
+                                * is SDP, dissect the payload as SDP.
+                                */
+                               if (is_content_sdp(line, linelen))
+                                       is_sdp = TRUE;
+                       } else if (MIME_HDR_MATCHES(rtsp_content_length)) {
+                               /*
+                                * Only the amount specified by the
+                                * Content-Length: header should be treated
+                                * as payload.
+                                */
+                               content_length = rtsp_get_content_length(line,
+                                   linelen);
+                       }
+               }
                offset = next_offset;
        }
 
+       /*
+        * If a content length was supplied, the amount of data to be
+        * processed as RTSP payload is the minimum of the content
+        * length and the amount of data remaining in the frame.
+        *
+        * If no content length was supplied, the amount of data to be
+        * processed is the amount of data remaining in the frame.
+        */
        datalen = tvb_length_remaining(tvb, offset);
-       if (is_sdp) {
-               if (datalen > 0) {
+       if (content_length != -1) {
+               if (datalen > content_length)
+                       datalen = content_length;
+
+               /*
+                * XXX - for now, if the content length is greater
+                * than the amount of data left in this frame (not
+                * the amount of *captured* data left in the frame
+                * minus the current offset, but the amount of *actual*
+                * data that was reported to be in the frame minus
+                * the current offset), limit it to the amount
+                * of data left in this frame.
+                *
+                * If we ever handle data that crosses frame
+                * boundaries, we'll need to remember the actual
+                * content length.
+                */
+               reported_datalen = tvb_reported_length_remaining(tvb, offset);
+               if (content_length > reported_datalen)
+                       content_length = reported_datalen;
+       }
+
+       if (datalen > 0) {
+               /*
+                * There's stuff left over; process it.
+                */
+               if (is_sdp) {
                        tvbuff_t *new_tvb;
 
                        /*
@@ -355,26 +645,56 @@ dissect_rtsp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
                                proto_item_set_len(ti, offset);
 
                        /*
-                        * Now creat a tvbuff for the SDP stuff and
+                        * Now create a tvbuff for the SDP stuff and
                         * dissect it.
+                        *
+                        * The amount of data to be processed that's
+                        * available in the tvbuff is "datalen", which
+                        * is the minimum of the amount of data left in
+                        * the tvbuff and any specified content length.
+                        *
+                        * The amount of data to be processed that's in
+                        * this frame, regardless of whether it was
+                        * captured or not, is "content_length",
+                        * which, if no content length was specified,
+                        * is -1, i.e. "to the end of the frame.
                         */
-                       new_tvb = tvb_new_subset(tvb, offset, -1, -1);
-                       dissect_sdp(new_tvb, pinfo, tree);
-               }
-       } else {
-               if (datalen > 0) {
-                       proto_tree_add_text(rtsp_tree, tvb, offset, datalen,
-                           "Data (%d bytes)", datalen);
+                       new_tvb = tvb_new_subset(tvb, offset, datalen,
+                           content_length);
+                       call_dissector(sdp_handle, new_tvb, pinfo, tree);
+               } else {
+                       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_text(rtsp_tree, tvb, offset,
+                                   datalen, "Data (%d bytes)", 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, so they
+                * don't process it.
+                */
+               offset += datalen;
        }
+       return offset - orig_offset;
 }
 
 static void
 process_rtsp_request(tvbuff_t *tvb, int offset, const u_char *data,
-       int linelen, proto_tree *tree)
+       size_t linelen, proto_tree *tree)
 {
        const u_char    *lineend = data + linelen;
-       int             ii;
+       unsigned        ii;
        const u_char    *url;
        const u_char    *url_start;
        u_char          *tmp_url;
@@ -417,7 +737,7 @@ process_rtsp_request(tvbuff_t *tvb, int offset, const u_char *data,
 
 static void
 process_rtsp_reply(tvbuff_t *tvb, int offset, const u_char *data,
-       int linelen, proto_tree *tree)
+       size_t linelen, proto_tree *tree)
 {
        const u_char    *lineend = data + linelen;
        const u_char    *status = data;
@@ -438,29 +758,82 @@ process_rtsp_reply(tvbuff_t *tvb, int offset, const u_char *data,
                status - status_start, status_i);
 }
 
+static void
+dissect_rtsp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+       int             offset = 0;
+       int             len;
+
+       if (check_col(pinfo->cinfo, COL_PROTOCOL))
+               col_set_str(pinfo->cinfo, COL_PROTOCOL, "RTSP");
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_clear(pinfo->cinfo, COL_INFO);
+
+       while (tvb_offset_exists(tvb, offset)) {
+               len = (tvb_get_guint8(tvb, offset) == RTSP_FRAMEHDR)
+                       ? dissect_rtspinterleaved(tvb, offset, pinfo, tree)
+                       : dissect_rtspmessage(tvb, offset, pinfo, tree);
+               if (len == -1)
+                       break;
+               offset += len;
+
+               /*
+                * 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.
+                */
+               col_set_writable(pinfo->cinfo, FALSE);
+       }
+}
+
+static void
+rtsp_init(void)
+{
+/* Routine to initialize rtsp protocol before each capture or filter pass. */
+/* Release any memory if needed.  Then setup the memory chunks.                */
+
+       if (rtsp_vals)
+               g_mem_chunk_destroy(rtsp_vals);
+
+       rtsp_vals = g_mem_chunk_new("rtsp_vals",
+               sizeof(rtsp_conversation_data_t),
+               rtsp_hash_init_count * sizeof(rtsp_conversation_data_t),
+               G_ALLOC_AND_FREE);
+}
+
 void
 proto_register_rtsp(void)
 {
        static gint *ett[] = {
+               &ett_rtspframe,
                &ett_rtsp,
        };
        static hf_register_info hf[] = {
        { &hf_rtsp_method,
-       { "Method", "rtsp.method", FT_STRING, BASE_NONE, NULL, 0 }},
+       { "Method", "rtsp.method", FT_STRING, BASE_NONE, NULL, 0, "", HFILL }},
        { &hf_rtsp_url,
-       { "URL", "rtsp.url", FT_STRING, BASE_NONE, NULL, 0 }},
+       { "URL", "rtsp.url", FT_STRING, BASE_NONE, NULL, 0, "", HFILL }},
        { &hf_rtsp_status,
-       { "Status", "rtsp.status", FT_UINT32, BASE_DEC, NULL, 0 }},
+       { "Status", "rtsp.status", FT_UINT32, BASE_DEC, NULL, 0, "", HFILL }},
        };
 
         proto_rtsp = proto_register_protocol("Real Time Streaming Protocol",
-               "rtsp");
+               "RTSP", "rtsp");
        proto_register_field_array(proto_rtsp, hf, array_length(hf));
        proto_register_subtree_array(ett, array_length(ett));
+
+       register_init_routine(rtsp_init);       /* register re-init routine */
 }
 
 void
 proto_reg_handoff_rtsp(void)
 {
-       dissector_add("tcp.port", TCP_PORT_RTSP, dissect_rtsp);
+       dissector_handle_t rtsp_handle;
+
+       rtsp_handle = create_dissector_handle(dissect_rtsp, proto_rtsp);
+       dissector_add("tcp.port", TCP_PORT_RTSP, rtsp_handle);
+
+       sdp_handle = find_dissector("sdp");
+       rtp_handle = find_dissector("rtp");
+       rtcp_handle = find_dissector("rtcp");
 }