* 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
* 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;
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
/*
#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)
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);
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,
#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) {
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);
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) {
/*
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;
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;
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
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;
}
}
*/
is_mime_header = TRUE;
goto is_rtsp;
+
+ case ' ':
+ case '\t':
+ /*
+ * LWS (RFC-2616, 4.2); continue the previous
+ * header.
+ */
+ goto is_rtsp;
}
}
* 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
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;
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;
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)
* 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)
{
};
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");
}