There's no need to allocate and fill in an array of sub-authorities and
[obnox/wireshark/wip.git] / packet-rtsp.c
index bd7015d80bb879506cbbf37c931c4fcfa8a006f1..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.35 2001/01/13 02:28:27 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 <stdlib.h>
 
 #include <glib.h>
-#include "packet.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;
@@ -54,6 +51,13 @@ 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
 
 /*
@@ -65,6 +69,22 @@ static int hf_rtsp_status = -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)
@@ -77,14 +97,17 @@ dissect_rtspinterleaved(tvbuff_t *tvb, int offset, packet_info *pinfo,
        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->fd, COL_INFO))
-               col_add_fstr(pinfo->fd, COL_INFO, 
+       if (check_col(pinfo->cinfo, COL_INFO))
+               col_add_fstr(pinfo->cinfo, COL_INFO, 
                        "Interleaved channel 0x%02x, %u bytes",
                        rf_chan, rf_len);
 
@@ -129,17 +152,30 @@ dissect_rtspinterleaved(tvbuff_t *tvb, int offset, packet_info *pinfo,
        if (framelen > rf_len)
                framelen = rf_len;
        next_tvb = tvb_new_subset(tvb, offset, framelen, rf_len);
-       dissect_data(next_tvb, 0, pinfo, tree);
+
+       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,
@@ -155,12 +191,10 @@ static const char *rtsp_methods[] = {
 
 #define RTSP_NMETHODS  (sizeof rtsp_methods / sizeof rtsp_methods[0])
 
-static dissector_handle_t sdp_handle;
-
 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) {
@@ -187,7 +221,7 @@ is_rtsp_request_or_reply(const u_char *line, int linelen)
 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)
 {
        static const char type[] = "application/sdp";
        size_t          typelen = STRLEN_CONST(type);
@@ -213,16 +247,19 @@ is_content_sdp(const u_char *line, int linelen)
 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 *line_begin, int line_len)
+rtsp_create_conversation(packet_info *pinfo, const u_char *line_begin,
+       size_t line_len)
 {
        conversation_t  *conv;
        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;
 
        if (line_len > sizeof(buf) - 1) {
                /*
@@ -236,40 +273,99 @@ rtsp_create_conversation(const u_char *line_begin, int line_len)
        tmp = buf + STRLEN_CONST(rtsp_transport);
        while (*tmp && isspace(*tmp))
                tmp++;
-       if (strncasecmp(tmp, rtsp_rtp, strlen(rtsp_rtp)) != 0)
-               return; /* we don't know this transport */
+       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(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(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);
+
+       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 || !s_mon_port)
+       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 const char rtsp_content_length[] = "Content-Length:";
 
 static int
-rtsp_get_content_length(const u_char *line_begin, int line_len)
+rtsp_get_content_length(const u_char *line_begin, size_t line_len)
 {
        u_char          buf[256];
        u_char          *tmp;
@@ -305,7 +401,8 @@ dissect_rtspmessage(tvbuff_t *tvb, int offset, packet_info *pinfo,
        const u_char    *line;
        gint            next_offset;
        const u_char    *linep, *lineend;
-       int             orig_offset, linelen;
+       int             orig_offset;
+       size_t          linelen;
        u_char          c;
        gboolean        is_mime_header;
        int             is_sdp = FALSE;
@@ -316,12 +413,12 @@ dissect_rtspmessage(tvbuff_t *tvb, int offset, packet_info *pinfo,
        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_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
@@ -334,12 +431,12 @@ dissect_rtspmessage(tvbuff_t *tvb, int offset, packet_info *pinfo,
 
                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_set_str(pinfo->fd, COL_INFO, "Continuation");
+                       col_set_str(pinfo->cinfo, COL_INFO, "Continuation");
                        break;
                }
        }
@@ -439,6 +536,14 @@ dissect_rtspmessage(tvbuff_t *tvb, int offset, packet_info *pinfo,
                                 */
                                is_mime_header = TRUE;
                                goto is_rtsp;
+
+                       case ' ':
+                       case '\t':
+                               /*
+                                * LWS (RFC-2616, 4.2); continue the previous
+                                * header.
+                                */
+                               goto is_rtsp;
                        }
                }
 
@@ -473,7 +578,7 @@ dissect_rtspmessage(tvbuff_t *tvb, int offset, packet_info *pinfo,
                                 * a conversation that will be dissected
                                 * with the appropriate dissector.
                                 */
-                               rtsp_create_conversation(line, linelen);
+                               rtsp_create_conversation(pinfo, line, linelen);
                        } else if (MIME_HDR_MATCHES(rtsp_content_type)) {
                                /*
                                 * If the Content-Type: header says this
@@ -586,10 +691,10 @@ dissect_rtspmessage(tvbuff_t *tvb, int offset, packet_info *pinfo,
 
 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;
@@ -632,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;
@@ -659,12 +764,10 @@ dissect_rtsp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
        int             offset = 0;
        int             len;
 
-       CHECK_DISPLAY_AS_DATA(proto_rtsp, tvb, pinfo, tree);
-
-       pinfo->current_proto = "RTSP";
-
-       if (check_col(pinfo->fd, COL_PROTOCOL))
-               col_set_str(pinfo->fd, COL_PROTOCOL, "RTSP");
+       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)
@@ -679,10 +782,25 @@ dissect_rtsp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
                 * first RTSP message; make the columns non-writable,
                 * so that we don't change it for subsequent RTSP messages.
                 */
-               col_set_writable(pinfo->fd, FALSE);
+               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)
 {
@@ -692,26 +810,30 @@ proto_register_rtsp(void)
        };
        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");
        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, proto_rtsp);
+       dissector_handle_t rtsp_handle;
+
+       rtsp_handle = create_dissector_handle(dissect_rtsp, proto_rtsp);
+       dissector_add("tcp.port", TCP_PORT_RTSP, rtsp_handle);
 
-       /*
-        * Get a handle for the SDP dissector.
-        */
        sdp_handle = find_dissector("sdp");
+       rtp_handle = find_dissector("rtp");
+       rtcp_handle = find_dissector("rtcp");
 }