* 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
* 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,
#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) {
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
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.
*/
* 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;
}
}
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;
/*
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;
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;
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");
}