diff options
Diffstat (limited to 'epan/dissectors/packet-rtsp.c')
-rw-r--r-- | epan/dissectors/packet-rtsp.c | 1217 |
1 files changed, 1217 insertions, 0 deletions
diff --git a/epan/dissectors/packet-rtsp.c b/epan/dissectors/packet-rtsp.c new file mode 100644 index 0000000000..1a1453494e --- /dev/null +++ b/epan/dissectors/packet-rtsp.c @@ -0,0 +1,1217 @@ +/* packet-rtsp.c + * Routines for RTSP packet disassembly (RFC 2326) + * + * Jason Lango <jal@netapp.com> + * Liberally copied from packet-http.c, by Guy Harris <guy@alum.mit.edu> + * + * $Id$ + * + * Ethereal - Network traffic analyzer + * 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. + * + * References: + * RTSP is defined in RFC 2326, http://www.ietf.org/rfc/rfc2326.txt?number=2326 + * http://www.iana.org/assignments/rsvp-parameters + */ + +#include "config.h" + +#include <string.h> +#include <ctype.h> +#include <stdlib.h> + +#include "prefs.h" + +#include <glib.h> +#include <epan/packet.h> +#include "req_resp_hdrs.h" +#include "packet-rtp.h" +#include "packet-rtcp.h" +#include <epan/conversation.h> +#include <epan/strutil.h> +#include "packet-e164.h" + +static int proto_rtsp = -1; + +static gint ett_rtsp = -1; +static gint ett_rtspframe = -1; +static gint ett_rtsp_method = -1; + +static int hf_rtsp_method = -1; +static int hf_rtsp_url = -1; +static int hf_rtsp_status = -1; +static int hf_rtsp_session = -1; +static int hf_rtsp_X_Vig_Msisdn = -1; + +static dissector_handle_t sdp_handle; +static dissector_handle_t rtp_handle; +static dissector_handle_t rtcp_handle; + +void proto_reg_handoff_rtsp(void); + +static GMemChunk *rtsp_vals = NULL; +#define rtsp_hash_init_count 20 + +/* + * desegmentation of RTSP headers + * (when we are over TCP or another protocol providing the desegmentation API) + */ +static gboolean rtsp_desegment_headers = FALSE; + +/* + * desegmentation of RTSP bodies + * (when we are over TCP or another protocol providing the desegmentation API) + * TODO let the user filter on content-type the bodies he wants desegmented + */ +static gboolean rtsp_desegment_body = FALSE; + +/* http://www.iana.org/assignments/port-numberslists two rtsp ports */ +#define TCP_PORT_RTSP 554 +#define TCP_ALTERNATE_PORT_RTSP 8554 +static guint global_rtsp_tcp_port = TCP_PORT_RTSP; +static guint global_rtsp_tcp_alternate_port = TCP_ALTERNATE_PORT_RTSP; +/* +* Variables to allow for proper deletion of dissector registration when +* the user changes port from the gui. +*/ +static guint tcp_port = 0; +static guint tcp_alternate_port = 0; + +/* + * 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) +{ + guint length_remaining; + proto_item *ti; + proto_tree *rtspframe_tree = NULL; + int orig_offset; + guint8 rf_start; /* always RTSP_FRAMEHDR */ + guint8 rf_chan; /* interleaved channel id */ + guint16 rf_len; /* packet length */ + tvbuff_t *next_tvb; + conversation_t *conv; + rtsp_conversation_data_t *data; + dissector_handle_t dissector; + + /* + * This will throw an exception if we don't have any data left. + * That's what we want. (See "tcp_dissect_pdus()", which is + * similar.) + */ + length_remaining = tvb_ensure_length_remaining(tvb, offset); + + /* + * Can we do reassembly? + */ + if (rtsp_desegment_headers && pinfo->can_desegment) { + /* + * Yes - would an RTSP multiplexed header starting at + * this offset be split across segment boundaries? + */ + if (length_remaining < 4) { + /* + * Yes. Tell the TCP dissector where the data + * for this message starts in the data it handed + * us, and how many more bytes we need, and return. + */ + pinfo->desegment_offset = offset; + pinfo->desegment_len = 4 - length_remaining; + return -1; + } + } + + /* + * Get the "$", channel, and length from the header. + */ + 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); + + /* + * Can we do reassembly? + */ + if (rtsp_desegment_body && pinfo->can_desegment) { + /* + * Yes - is the header + encapsulated packet split + * across segment boundaries? + */ + if (length_remaining < 4U + rf_len) { + /* + * Yes. Tell the TCP dissector where the data + * for this message starts in the data it handed + * us, and how many more bytes we need, and return. + */ + pinfo->desegment_offset = offset; + pinfo->desegment_len = 4U + rf_len - length_remaining; + return -1; + } + } + + 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) { + 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; + + if (tree != NULL) { + proto_tree_add_text(rtspframe_tree, tvb, offset, 1, + "Channel: 0x%02x", + rf_chan); + } + offset += 1; + + if (tree != NULL) { + 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. + */ + if (length_remaining > rf_len) + length_remaining = rf_len; + next_tvb = tvb_new_subset(tvb, offset, length_remaining, 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 gboolean +is_rtsp_request_or_reply(const guchar *line, size_t linelen, rtsp_type_t *type) +{ + unsigned ii; + + /* Is this an RTSP reply? */ + if (linelen >= 5 && strncasecmp("RTSP/", line, 5) == 0) { + /* + * Yes. + */ + *type = RTSP_REPLY; + return TRUE; + } + + /* + * 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 && + (len == linelen || isspace(line[len]))) { + *type = RTSP_REQUEST; + return TRUE; + } + } + *type = NOT_RTSP; + return FALSE; +} + +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 FALSE; + + line += typelen; + linelen -= typelen; + if (linelen > 0 && !isspace(*line)) + return FALSE; + + 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/"; +static const char rtsp_inter[] = "interleaved="; + +static void +rtsp_create_conversation(packet_info *pinfo, const guchar *line_begin, + size_t line_len) +{ + conversation_t *conv; + guchar buf[256]; + guchar *tmp; + guint c_data_port, c_mon_port; + guint s_data_port, s_mon_port; + address null_addr; + + 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_transport); + while (*tmp && isspace(*tmp)) + tmp++; + 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("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("Frame %u: rtsp: bad client_port", + pinfo->fd->num); + return; + } + } + 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); + + rtp_add_address(pinfo, (char *)pinfo->dst.data, c_data_port, s_data_port, + "RTSP", pinfo->fd->num); + + if (!c_mon_port) + return; + + rtcp_add_address(pinfo, (char *)pinfo->dst.data, c_mon_port, s_mon_port, + "RTSP", pinfo->fd->num); +} + +static const char rtsp_content_length[] = "Content-Length:"; + +static int +rtsp_get_content_length(const guchar *line_begin, size_t line_len) +{ + guchar buf[256]; + guchar *tmp; + long content_length; + char *p; + guchar *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 const char rtsp_Session[] = "Session:"; +static const char rtsp_X_Vig_Msisdn[] = "X-Vig-Msisdn"; + +static int +dissect_rtspmessage(tvbuff_t *tvb, int offset, packet_info *pinfo, + proto_tree *tree) +{ + proto_tree *rtsp_tree = NULL; + proto_tree *sub_tree = NULL; + proto_item *ti = NULL; + const guchar *line; + gint next_offset; + const guchar *linep, *lineend; + int orig_offset; + int first_linelen, linelen; + int line_end_offset; + int colon_offset; + gboolean is_request_or_reply; + gboolean body_requires_content_len; + gboolean saw_req_resp_or_header; + guchar c; + rtsp_type_t rtsp_type; + gboolean is_header; + int is_sdp = FALSE; + int datalen; + int content_length; + int reported_datalen; + int value_offset; + int value_len; + e164_info_t e164_info; + /* + * Is this a request or response? + * + * Note that "tvb_find_line_end()" will return a value that + * is not longer than what's in the buffer, so the + * "tvb_get_ptr()" call won't throw an exception. + */ + first_linelen = tvb_find_line_end(tvb, offset, + tvb_ensure_length_remaining(tvb, offset), &next_offset, + FALSE); + /* + * Is the first line a request or response? + */ + line = tvb_get_ptr(tvb, offset, first_linelen); + is_request_or_reply = is_rtsp_request_or_reply(line, first_linelen, + &rtsp_type); + if (is_request_or_reply) { + /* + * Yes, it's a request or response. + * Do header desegmentation if we've been told to, + * and do body desegmentation if we've been told to and + * we find a Content-Length header. + */ + if (!req_resp_hdrs_do_reassembly(tvb, pinfo, + rtsp_desegment_headers, rtsp_desegment_body)) { + /* + * More data needed for desegmentation. + */ + return -1; + } + } + + /* + * RFC 2326 says that a content length must be specified + * in requests that have a body, although section 4.4 speaks + * of a server closing the connection indicating the end of + * a reply body. + * + * We assume that an absent content length in a request means + * that we don't have a body, and that an absent content length + * in a reply means that the reply body runs to the end of + * the connection. If the first line is neither, we assume + * that whatever follows a blank line should be treated as a + * body; there's not much else we can do, as we're jumping + * into the message in the middle. + * + * XXX - if there was no Content-Length entity header, we should + * accumulate all data until the end of the connection. + * That'd require that the TCP dissector call subdissectors + * for all frames with FIN, even if they contain no data, + * which would require subdissectors to deal intelligently + * with empty segments. + */ + if (rtsp_type == RTSP_REQUEST) + body_requires_content_len = TRUE; + else + body_requires_content_len = FALSE; + + if (check_col(pinfo->cinfo, COL_PROTOCOL)) + col_set_str(pinfo->cinfo, COL_PROTOCOL, "RTSP"); + 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 + * line terminator). + * Otherwise, just call it a continuation. + * + * Note that "tvb_find_line_end()" will return a value that + * is not longer than what's in the buffer, so the + * "tvb_get_ptr()" call won't throw an exception. + */ + line = tvb_get_ptr(tvb, offset, first_linelen); + if (is_request_or_reply) + if ( rtsp_type == RTSP_REPLY ) { + col_add_str(pinfo->cinfo, COL_INFO, "Reply: "); + col_append_str(pinfo->cinfo, COL_INFO, + format_text(line, first_linelen)); + } + else + col_add_str(pinfo->cinfo, COL_INFO, + format_text(line, first_linelen)); + + else + col_set_str(pinfo->cinfo, COL_INFO, "Continuation"); + + } + + orig_offset = offset; + if (tree) { + ti = proto_tree_add_item(tree, proto_rtsp, tvb, offset, -1, + FALSE); + rtsp_tree = proto_item_add_subtree(ti, ett_rtsp); + } + + /* + * We haven't yet seen a Content-Length header. + */ + content_length = -1; + + /* + * Process the packet data, a line at a time. + */ + saw_req_resp_or_header = FALSE; /* haven't seen anything yet */ + while (tvb_reported_length_remaining(tvb, offset) != 0) { + /* + * We haven't yet concluded that this is a header. + */ + is_header = FALSE; + + /* + * Find the end of the line. + */ + linelen = tvb_find_line_end(tvb, offset, + tvb_ensure_length_remaining(tvb, offset), &next_offset, + FALSE); + if (linelen < 0) + return -1; + line_end_offset = offset + linelen; + /* + * colon_offset may be -1 + */ + colon_offset = tvb_find_guint8(tvb, offset, linelen, ':'); + + + /* + * 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? + */ + is_request_or_reply = is_rtsp_request_or_reply(line, linelen, + &rtsp_type); + if (is_request_or_reply) + goto is_rtsp; + + /* + * No. Does it look like a blank line (as would appear + * at the end of an RTSP request)? + */ + if (linelen == 0) + goto is_rtsp; /* Yes. */ + + /* + * No. Does it look like a header? + */ + linep = line; + while (linep < lineend) { + c = *linep++; + + /* + * This must be a CHAR to be part of a token; that + * means it must be ASCII. + */ + if (!isascii(c)) + break; /* not ASCII, thus not a CHAR */ + + /* + * This mustn't be a CTL to be part of a token. + * + * XXX - what about leading LWS on continuation + * lines of a header? + */ + if (iscntrl(c)) + break; /* CTL, not part of a header */ + + switch (c) { + + case '(': + case ')': + case '<': + case '>': + case '@': + case ',': + case ';': + case '\\': + case '"': + case '/': + case '[': + case ']': + case '?': + case '=': + case '{': + case '}': + /* + * It's a tspecial, so it's not + * part of a token, so it's not + * a field name for the beginning + * of a header. + */ + goto not_rtsp; + + case ':': + /* + * This ends the token; we consider + * this to be a header. + */ + is_header = TRUE; + goto is_rtsp; + + case ' ': + case '\t': + /* + * LWS (RFC-2616, 4.2); continue the previous + * header. + */ + goto is_rtsp; + } + } + + /* + * We haven't seen the colon, but everything else looks + * OK for a header line. + * + * If we've already seen an RTSP request or response + * line, or a header line, and we're at the end of + * the tvbuff, we assume this is an incomplete header + * line. (We quit this loop after seeing a blank line, + * so if we've seen a request or response line, or a + * header line, this is probably more of the request + * or response we're presumably seeing. There is some + * risk of false positives, but the same applies for + * full request or response lines or header lines, + * although that's less likely.) + * + * We throw an exception in that case, by checking for + * the existence of the next byte after the last one + * in the line. If it exists, "tvb_ensure_bytes_exist()" + * throws no exception, and we fall through to the + * "not RTSP" case. If it doesn't exist, + * "tvb_ensure_bytes_exist()" will throw the appropriate + * exception. + */ + if (saw_req_resp_or_header) + tvb_ensure_bytes_exist(tvb, offset, linelen + 1); + + not_rtsp: + /* + * We don't consider this part of an RTSP request or + * reply, so we don't display it. + */ + break; + + is_rtsp: + /* + * Process this line. + */ + if (linelen == 0) { + /* + * This is a blank line, which means that + * whatever follows it isn't part of this + * request or reply. + */ + proto_tree_add_text(rtsp_tree, tvb, offset, + next_offset - offset, "%s", + tvb_format_text(tvb, offset, next_offset - offset)); + offset = next_offset; + break; + } + + /* + * Not a blank line - either a request, a reply, or a header + * line. + */ + saw_req_resp_or_header = TRUE; + if (rtsp_tree) { + ti = proto_tree_add_text(rtsp_tree, tvb, offset, + next_offset - offset, "%s", + tvb_format_text(tvb, offset, next_offset - offset)); + + sub_tree = proto_item_add_subtree(ti, ett_rtsp_method); + + switch (rtsp_type) { + + case RTSP_REQUEST: + process_rtsp_request(tvb, offset, line, linelen, + sub_tree); + break; + + case RTSP_REPLY: + process_rtsp_reply(tvb, offset, line, linelen, + sub_tree); + break; + + case NOT_RTSP: + break; + } + } + if (is_header) { + /* + * Process some headers specially. + */ +#define HDR_MATCHES(header) \ + (linelen > STRLEN_CONST(header) && \ + strncasecmp(line, (header), STRLEN_CONST(header)) == 0) + + if (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 (HDR_MATCHES(rtsp_content_type)) { + /* + * If the Content-Type: header says this + * is SDP, dissect the payload as SDP. + * + * XXX - we should just do the same + * sort of header processing + * that HTTP does, and use the + * "media_type" dissector table on + * the content type. + * + * We should use those for Transport: + * and Content-Length: as well (and + * should process Content-Length: in + * HTTP). + */ + if (is_content_sdp(line, linelen)) + is_sdp = TRUE; + } else if (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); + + } else if (HDR_MATCHES(rtsp_Session)) { + /* + * Extract the session string + + */ + + if ( colon_offset != -1 ){ + /* + * Skip whitespace after the colon. + * (Code from SIP dissector ) + */ + value_offset = colon_offset + 1; + while (value_offset < line_end_offset + && ((c = tvb_get_guint8(tvb, + value_offset)) == ' ' + || c == '\t')) + value_offset++; + /* + * Put the value into the protocol tree + */ + value_len = line_end_offset - value_offset; + proto_tree_add_string(sub_tree, hf_rtsp_session,tvb, + value_offset, value_len , + tvb_format_text(tvb, value_offset, value_len)); + } + + } else if (HDR_MATCHES(rtsp_X_Vig_Msisdn)) { + /* + * Extract the X_Vig_Msisdn string + */ + if ( colon_offset != -1 ){ + /* + * Skip whitespace after the colon. + * (Code from SIP dissector ) + */ + value_offset = colon_offset + 1; + while (value_offset < line_end_offset + && ((c = tvb_get_guint8(tvb, + value_offset)) == ' ' + || c == '\t')) + value_offset++; + /* + * Put the value into the protocol tree + */ + value_len = line_end_offset - value_offset; + proto_tree_add_string(sub_tree, hf_rtsp_X_Vig_Msisdn,tvb, + value_offset, value_len , + tvb_format_text(tvb, value_offset, value_len)); + + e164_info.e164_number_type = CALLING_PARTY_NUMBER; + e164_info.nature_of_address = 0; + + e164_info.E164_number_str = tvb_get_string(tvb, value_offset, + value_len); + e164_info.E164_number_length = value_len; + dissect_e164_number(tvb, sub_tree, value_offset, + value_len, e164_info); + g_free(e164_info.E164_number_str); + + + } + } + + } + 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 (or if a bad 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); + reported_datalen = tvb_reported_length_remaining(tvb, offset); + if (content_length != -1) { + /* + * Content length specified; display only that amount + * as payload. + */ + if (datalen > content_length) + datalen = content_length; + + /* + * XXX - limit the reported length in the tvbuff we'll + * hand to a subdissector to be no greater than the + * content length. + * + * We really need both unreassembled and "how long it'd + * be if it were reassembled" lengths for tvbuffs, so + * that we throw the appropriate exceptions for + * "not enough data captured" (running past the length), + * "packet needed reassembly" (within the length but + * running past the unreassembled length), and + * "packet is malformed" (running past the reassembled + * length). + */ + if (reported_datalen > content_length) + reported_datalen = content_length; + } else { + /* + * No content length specified; if this message doesn't + * have a body if no content length is specified, process + * nothing as payload. + */ + if (body_requires_content_len) + datalen = 0; + } + + 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 "reported_datalen", + * 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, + reported_datalen); + 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)", + reported_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. + */ + offset += datalen; + } + return offset - orig_offset; +} + +static void +process_rtsp_request(tvbuff_t *tvb, int offset, const guchar *data, + size_t linelen, proto_tree *tree) +{ + 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++) { + size_t len = strlen(rtsp_methods[ii]); + if (linelen >= len && + strncasecmp(rtsp_methods[ii], data, len) == 0 && + (len == linelen || isspace(data[len]))) + break; + } + 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(); + } + + /* Method name */ + proto_tree_add_string(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(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(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; + + while (tvb_reported_length_remaining(tvb, offset) != 0) { + 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, + &ett_rtsp_method, + }; + static hf_register_info hf[] = { + { &hf_rtsp_method, + { "Method", "rtsp.method", FT_STRING, BASE_NONE, NULL, 0, + "", HFILL }}, + { &hf_rtsp_url, + { "URL", "rtsp.url", FT_STRING, BASE_NONE, NULL, 0, + "", HFILL }}, + { &hf_rtsp_status, + { "Status", "rtsp.status", FT_UINT32, BASE_DEC, NULL, 0, + "", HFILL }}, + { &hf_rtsp_session, + { "Session", "rtsp.session", FT_STRING, BASE_NONE, NULL, 0, + "", HFILL }}, + { &hf_rtsp_X_Vig_Msisdn, + { "X-Vig-Msisdn", "X_Vig_Msisdn", FT_STRING, BASE_NONE, NULL, 0, + "", HFILL }}, + + + }; + module_t *rtsp_module; + + 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 our configuration options, particularly our ports */ + + rtsp_module = prefs_register_protocol(proto_rtsp, proto_reg_handoff_rtsp); + prefs_register_uint_preference(rtsp_module, "tcp.port", + "RTSP TCP Port", + "Set the TCP port for RTSP messages", + 10, &global_rtsp_tcp_port); + prefs_register_uint_preference(rtsp_module, "tcp.alternate_port", + "Alternate RTSP TCP Port", + "Set the alternate TCP port for RTSP messages", + 10, &global_rtsp_tcp_alternate_port); + prefs_register_bool_preference(rtsp_module, "desegment_headers", + "Desegment all RTSP headers\nspanning multiple TCP segments", + "Whether the RTSP dissector should desegment all headers " + "of a request spanning multiple TCP segments", + &rtsp_desegment_headers); + prefs_register_bool_preference(rtsp_module, "desegment_body", + "Trust the \"Content-length:\" header and\ndesegment RTSP " + "bodies\nspanning multiple TCP segments", + "Whether the RTSP dissector should use the " + "\"Content-length:\" value to desegment the body " + "of a request spanning multiple TCP segments", + &rtsp_desegment_body); + + register_init_routine(rtsp_init); /* register re-init routine */ +} + +void +proto_reg_handoff_rtsp(void) +{ + dissector_handle_t rtsp_handle; + static int rtsp_prefs_initialized = FALSE; + + rtsp_handle = create_dissector_handle(dissect_rtsp, proto_rtsp); + + if (!rtsp_prefs_initialized) { + rtsp_prefs_initialized = TRUE; + } + else { + dissector_delete("tcp.port", tcp_port, rtsp_handle); + dissector_delete("tcp.port", tcp_alternate_port, rtsp_handle); + } + /* Set our port number for future use */ + + tcp_port = global_rtsp_tcp_port; + tcp_alternate_port = global_rtsp_tcp_alternate_port; + + dissector_add("tcp.port", tcp_port, rtsp_handle); + dissector_add("tcp.port", tcp_alternate_port, rtsp_handle); + + sdp_handle = find_dissector("sdp"); + rtp_handle = find_dissector("rtp"); + rtcp_handle = find_dissector("rtcp"); +} |