* Jason Lango <jal@netapp.com>
* Liberally copied from packet-http.c, by Guy Harris <guy@alum.mit.edu>
*
- * $Id: packet-rtsp.c,v 1.17 2000/08/21 18:36:33 guy Exp $
+ * $Id: packet-rtsp.c,v 1.50 2002/08/28 21:00:30 jmayer 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
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- *
- *
*/
#include "config.h"
-#ifdef HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
#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 <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
-static int process_rtsp_request_or_reply(const u_char *data, int offset,
- int linelen, proto_tree *tree);
+/*
+ * 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
-is_content_sdp(const u_char *line, int linelen)
+dissect_rtspinterleaved(tvbuff_t *tvb, int offset, packet_info *pinfo,
+ proto_tree *tree)
{
- const char *hdr = "Content-Type:";
- size_t hdrlen = strlen(hdr);
- const char *type = "application/sdp";
- size_t typelen = strlen(type);
+ 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;
- if (linelen < hdrlen || strncasecmp(hdr, line, hdrlen))
- return 0;
+ 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;
+ }
- line += hdrlen;
- linelen -= hdrlen;
+ 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 guchar *data,
+ size_t linelen, proto_tree *tree);
+
+static void process_rtsp_reply(tvbuff_t *tvb, int offset, const guchar *data,
+ size_t linelen, proto_tree *tree);
+
+typedef enum {
+ RTSP_REQUEST,
+ RTSP_REPLY,
+ NOT_RTSP
+} rtsp_type_t;
+
+static const char *rtsp_methods[] = {
+ "DESCRIBE", "ANNOUNCE", "GET_PARAMETER", "OPTIONS",
+ "PAUSE", "PLAY", "RECORD", "REDIRECT", "SETUP",
+ "SET_PARAMETER", "TEARDOWN"
+};
+
+#define RTSP_NMETHODS (sizeof rtsp_methods / sizeof rtsp_methods[0])
+
+static rtsp_type_t
+is_rtsp_request_or_reply(const guchar *line, size_t linelen)
+{
+ unsigned ii;
+
+ /* Is this an RTSP reply? */
+ if (linelen >= 5 && strncasecmp("RTSP/", line, 5) == 0) {
+ /*
+ * Yes.
+ */
+ return RTSP_REPLY;
+ }
+
+ /*
+ * Is this an RTSP request?
+ * Check whether the line begins with one of the RTSP request
+ * methods.
+ */
+ for (ii = 0; ii < RTSP_NMETHODS; ii++) {
+ size_t len = strlen(rtsp_methods[ii]);
+ if (linelen >= len &&
+ strncasecmp(rtsp_methods[ii], line, len) == 0)
+ return RTSP_REQUEST;
+ }
+ return NOT_RTSP;
+}
+
+static const char rtsp_content_type[] = "Content-Type:";
+
+static int
+is_content_sdp(const guchar *line, size_t linelen)
+{
+ static const char type[] = "application/sdp";
+ size_t typelen = STRLEN_CONST(type);
+
+ 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 guchar *line_begin,
+ size_t line_len)
{
conversation_t *conv;
- u_char tbuf[256];
- u_char *tmp;
- int c_data_port, c_mon_port;
- int s_data_port, s_mon_port;
+ guchar buf[256];
+ guchar *tmp;
+ guint c_data_port, c_mon_port;
+ guint 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;
+ guint 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;
+ }
+
+ /*
+ * 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(&pi.src, &pi.dst, PT_UDP, s_data_port,
- c_data_port, 0);
- old_conversation_set_dissector(conv, dissect_rtp);
+ 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);
- old_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(const u_char *pd, int offset, frame_data *fd,
- proto_tree *tree)
+static const char rtsp_content_length[] = "Content-Length:";
+
+static int
+rtsp_get_content_length(const guchar *line_begin, size_t line_len)
{
- proto_tree *rtsp_tree;
- proto_item *ti;
- const u_char *data, *dataend;
- const u_char *linep, *lineend, *eol;
- int linelen;
- u_char c;
- int is_sdp = 0;
- int end_offset;
+ guchar buf[256];
+ guchar *tmp;
+ long content_length;
+ char *p;
+ guchar *up;
- OLD_CHECK_DISPLAY_AS_DATA(proto_rtsp, pd, offset, fd, tree);
+ 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';
- data = &pd[offset];
- dataend = data + END_OF_FRAME;
- end_offset = offset + END_OF_FRAME;
+ 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;
+ const guchar *line;
+ gint next_offset;
+ const guchar *linep, *lineend;
+ int orig_offset;
+ size_t linelen;
+ guchar c;
+ gboolean is_mime_header;
+ int is_sdp = FALSE;
+ int datalen;
+ int content_length;
+ int reported_datalen;
+
+ orig_offset = offset;
rtsp_tree = NULL;
if (tree) {
- ti = proto_tree_add_item(tree, proto_rtsp, NullTVB, offset,
- END_OF_FRAME, 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(fd, COL_PROTOCOL))
- col_add_str(fd, COL_PROTOCOL, "RTSP");
- if (check_col(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. Otherwise, just call
- * it a continuation.
+ * if it's an RTSP request or reply (but leave out the
+ * line terminator).
+ * Otherwise, just call it a continuation.
*/
- lineend = find_line_end(data, dataend, &eol);
- linelen = lineend - data;
- if (process_rtsp_request_or_reply(data, offset, linelen,
- rtsp_tree))
- col_add_str(fd, COL_INFO, format_text(data, linelen));
- else
- col_add_str(fd, COL_INFO, "Continuation");
+ linelen = tvb_find_line_end(tvb, offset, -1, &next_offset,
+ FALSE);
+ line = tvb_get_ptr(tvb, offset, linelen);
+ switch (is_rtsp_request_or_reply(line, linelen)) {
+
+ case RTSP_REQUEST:
+ case RTSP_REPLY:
+ col_add_str(pinfo->cinfo, COL_INFO,
+ format_text(line, linelen));
+ break;
+
+ default:
+ col_set_str(pinfo->cinfo, COL_INFO, "Continuation");
+ break;
+ }
}
- if (offset >= end_offset)
- goto bad_len;
- while (data < dataend) {
+ /*
+ * We haven't yet seen a Content-Length header.
+ */
+ content_length = -1;
+
+ /*
+ * Process the packet data, a line at a time.
+ */
+ 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.
*/
- lineend = find_line_end(data, dataend, &eol);
- linelen = lineend - data;
+ linelen = tvb_find_line_end(tvb, offset, -1, &next_offset,
+ FALSE);
+
+ /*
+ * Get a buffer that refers to the line.
+ */
+ line = tvb_get_ptr(tvb, offset, linelen);
+ lineend = line + linelen;
/*
* OK, does it look like an RTSP request or
* response?
*/
- if (process_rtsp_request_or_reply(data, offset, linelen,
- rtsp_tree))
+ switch (is_rtsp_request_or_reply(line, linelen)) {
+
+ case RTSP_REQUEST:
+ if (rtsp_tree != NULL)
+ process_rtsp_request(tvb, offset, line, linelen,
+ rtsp_tree);
+ goto is_rtsp;
+
+ case RTSP_REPLY:
+ if (rtsp_tree != NULL)
+ process_rtsp_reply(tvb, offset, line, linelen,
+ rtsp_tree);
goto is_rtsp;
+ case NOT_RTSP:
+ break;
+ }
+
/*
* No. Does it look like a blank line (as would
* appear at the end of an RTSP request)?
*/
- if (linelen == 1) {
- if (*data == '\n')
- goto is_rtsp;
- }
- if (linelen == 2) {
- if (strncmp(data, "\r\n", 2) == 0 ||
- strncmp(data, "\n\r", 2) == 0)
- goto is_rtsp;
- }
+ if (linelen == 0)
+ goto is_rtsp; /* Yes. */
/*
* No. Does it look like a MIME header?
*/
- linep = data;
+ linep = line;
while (linep < lineend) {
c = *linep++;
if (!isprint(c))
* This ends the token; we consider
* this to be a MIME header.
*/
- if (is_content_sdp(data, linelen))
- is_sdp = 1;
+ is_mime_header = TRUE;
+ goto is_rtsp;
+
+ case ' ':
+ case '\t':
+ /*
+ * LWS (RFC-2616, 4.2); continue the previous
+ * header.
+ */
goto is_rtsp;
}
}
* Put this line.
*/
if (rtsp_tree) {
- proto_tree_add_text(rtsp_tree, NullTVB, offset, linelen, "%s",
- format_text(data, linelen));
+ proto_tree_add_text(rtsp_tree, tvb, offset,
+ next_offset - offset, "%s",
+ tvb_format_text(tvb, offset, next_offset - offset));
}
- if (linelen > strlen(rtsp_transport) &&
- strncasecmp(data, rtsp_transport,
- strlen(rtsp_transport)) == 0)
- rtsp_create_conversation(data, data + linelen);
- offset += linelen;
- data = lineend;
+ 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 (is_sdp) {
- dissect_sdp(pd, offset, fd, tree);
- if (check_col(fd, COL_PROTOCOL))
- col_add_str(fd, COL_PROTOCOL, "RTSP/SDP");
- }
- else if (data < dataend) {
- proto_tree_add_text(rtsp_tree, NullTVB, offset, END_OF_FRAME,
- "Data (%d bytes)", END_OF_FRAME);
+ /*
+ * 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 (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;
}
- return;
-bad_len:
- proto_tree_add_text(rtsp_tree, NullTVB, end_offset, 0,
- "Unexpected end of packet");
-}
+ if (datalen > 0) {
+ /*
+ * There's stuff left over; process it.
+ */
+ if (is_sdp) {
+ tvbuff_t *new_tvb;
+
+ /*
+ * Fix up the top-level item so that it doesn't
+ * include the SDP stuff.
+ */
+ if (ti != NULL)
+ proto_item_set_len(ti, offset);
+
+ /*
+ * 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, 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);
+ }
+ }
-const char *rtsp_methods[] = {
- "DESCRIBE", "ANNOUNCE", "GET_PARAMETER", "OPTIONS",
- "PAUSE", "PLAY", "RECORD", "REDIRECT", "SETUP",
- "SET_PARAMETER", "TEARDOWN"
-};
-const int rtsp_nmethods = sizeof(rtsp_methods) / sizeof(*rtsp_methods);
+ /*
+ * 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 int
-process_rtsp_request_or_reply(const u_char *data, int offset, int linelen,
- proto_tree *tree)
+static void
+process_rtsp_request(tvbuff_t *tvb, int offset, const guchar *data,
+ size_t linelen, proto_tree *tree)
{
- int ii;
- const u_char *lineend = data + linelen;
-
- /* Reply */
- if (linelen >= 5 && !strncasecmp("RTSP/", data, 5)) {
- if (tree) {
- /* status code */
- const u_char *status = data;
- const u_char *status_start;
- unsigned int status_i = 0;
- while (status < lineend && !isspace(*status))
- status++;
- while (status < lineend && isspace(*status))
- status++;
- status_start = status;
- while (status < lineend && isdigit(*status))
- status_i = status_i * 10 + *status++ - '0';
- proto_tree_add_uint_hidden(tree, hf_rtsp_status, NullTVB,
- offset + (status_start - data),
- status - status_start, status_i);
- }
- return TRUE;
- }
+ const guchar *lineend = data + linelen;
+ unsigned ii;
+ const guchar *url;
+ const guchar *url_start;
+ guchar *tmp_url;
/* Request Methods */
- for (ii = 0; ii < rtsp_nmethods; ii++) {
+ for (ii = 0; ii < RTSP_NMETHODS; ii++) {
size_t len = strlen(rtsp_methods[ii]);
if (linelen >= len && !strncasecmp(rtsp_methods[ii], data, len))
break;
}
- if (ii == rtsp_nmethods)
- return FALSE;
+ if (ii == RTSP_NMETHODS) {
+ /*
+ * We got here because "is_rtsp_request_or_reply()" returned
+ * RTSP_REQUEST, so we know one of the request methods
+ * matched, so we "can't get here".
+ */
+ g_assert_not_reached();
+ }
- if (tree) {
- const u_char *url;
- const u_char *url_start;
- u_char *tmp_url;
-
- /* method name */
- proto_tree_add_string_hidden(tree, hf_rtsp_method, NullTVB, offset,
- strlen(rtsp_methods[ii]), rtsp_methods[ii]);
-
- /* URL */
- url = data;
- while (url < lineend && !isspace(*url))
- url++;
- while (url < lineend && isspace(*url))
- url++;
- url_start = url;
- while (url < lineend && !isspace(*url))
- url++;
- tmp_url = g_malloc(url - url_start + 1);
- memcpy(tmp_url, url_start, url - url_start);
- tmp_url[url - url_start] = 0;
- proto_tree_add_string_hidden(tree, hf_rtsp_url, NullTVB,
- offset + (url_start - data), url - url_start, tmp_url);
- g_free(tmp_url);
+ /* Method name */
+ proto_tree_add_string_hidden(tree, hf_rtsp_method, tvb, offset,
+ strlen(rtsp_methods[ii]), rtsp_methods[ii]);
+
+ /* URL */
+ url = data;
+ while (url < lineend && !isspace(*url))
+ url++;
+ while (url < lineend && isspace(*url))
+ url++;
+ url_start = url;
+ while (url < lineend && !isspace(*url))
+ url++;
+ tmp_url = g_malloc(url - url_start + 1);
+ memcpy(tmp_url, url_start, url - url_start);
+ tmp_url[url - url_start] = 0;
+ proto_tree_add_string_hidden(tree, hf_rtsp_url, tvb,
+ offset + (url_start - data), url - url_start, tmp_url);
+ g_free(tmp_url);
+}
+
+static void
+process_rtsp_reply(tvbuff_t *tvb, int offset, const guchar *data,
+ size_t linelen, proto_tree *tree)
+{
+ const guchar *lineend = data + linelen;
+ const guchar *status = data;
+ const guchar *status_start;
+ unsigned int status_i;
+
+ /* status code */
+ while (status < lineend && !isspace(*status))
+ status++;
+ while (status < lineend && isspace(*status))
+ status++;
+ status_start = status;
+ status_i = 0;
+ while (status < lineend && isdigit(*status))
+ status_i = status_i * 10 + *status++ - '0';
+ proto_tree_add_uint_hidden(tree, hf_rtsp_status, tvb,
+ offset + (status_start - 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);
}
- return TRUE;
+}
+
+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)
{
- old_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");
}