/* packet-sip.c
* Routines for the Session Initiation Protocol (SIP) dissection.
* RFC 2543
- *
+ *
* TODO: Pay attention to Content-Type: It might not always be SDP.
* Add hf_* fields for filtering support.
* Add sip msg body dissection based on Content-Type for:
* check for other
*
* Copyright 2000, Heikki Vatiainen <hessu@cs.tut.fi>
- * Copyright 2001, Jean-Francois Mule <jfm@clarent.com>
+ * Copyright 2001, Jean-Francois Mule <jfm@cablelabs.com>
*
- * $Id: packet-sip.c,v 1.20 2001/12/10 00:25:34 guy Exp $
+ * $Id: packet-sip.c,v 1.33 2002/10/02 18:51:10 gerald Exp $
*
* Ethereal - Network traffic analyzer
* By Gerald Combs <gerald@ethereal.com>
* Copyright 1998 Gerald Combs
*
* Copied from packet-cops.c
- *
+ *
* 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 <string.h>
#include <glib.h>
-#include "packet.h"
+#include <epan/packet.h>
#define TCP_PORT_SIP 5060
#define UDP_PORT_SIP 5060
static const char *sip_methods[] = {
"<Invalid method>", /* Pad so that the real methods start at index 1 */
- "INVITE",
"ACK",
- "OPTIONS",
"BYE",
"CANCEL",
- "REGISTER",
+ "DO",
"INFO",
+ "INVITE",
+ "MESSAGE",
+ "NOTIFY",
+ "OPTIONS",
+ "PRACK",
+ "QAUTH",
"REFER",
- "SUBSCRIBE",
- "NOTIFY"
+ "REGISTER",
+ "SPRACK",
+ "SUBSCRIBE"
};
-static gboolean sip_is_request(tvbuff_t *tvb, guint32 offset);
+static gboolean sip_is_request(tvbuff_t *tvb, gint eol);
+static gboolean sip_is_known_request(tvbuff_t *tvb, guint32 offset);
static gint sip_get_msg_offset(tvbuff_t *tvb, guint32 offset);
-
+
static dissector_handle_t sdp_handle;
static dissector_handle_t data_handle;
-#define SIP2_HDR "SIP/2.0 "
+#define SIP2_HDR "SIP/2.0"
#define SIP2_HDR_LEN (strlen (SIP2_HDR))
/* Code to actually dissect the packets */
guint32 offset;
gint eol, next_offset, msg_offset;
tvbuff_t *next_tvb;
- gboolean is_request;
-
- /*
- * Note that "tvb_strneql()" doesn't throw exceptions, so
- * "sip_is_request()" won't throw an exception.
- *
- * 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 s below won't throw exceptions.
- */
+ gboolean is_request, is_known_request;
+ char *req_descr;
+
+ /*
+ * Note that "tvb_strneql()" doesn't throw exceptions, so
+ * "sip_is_request()" won't throw an exception.
+ *
+ * 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 s below won't throw exceptions.
+ */
offset = 0;
- eol = tvb_find_line_end(tvb, 0, -1, &next_offset);
- is_request = sip_is_request(tvb, 0);
- /* XXX - Is this case-sensitive? RFC 2543 didn't explicitly say. */
- if (tvb_strneql(tvb, 0, SIP2_HDR, SIP2_HDR_LEN) != 0 && ! is_request)
- goto bad;
-
- if (check_col(pinfo->cinfo, COL_PROTOCOL))
+ eol = tvb_find_line_end(tvb, 0, -1, &next_offset, FALSE);
+ /* XXX - Check for a valid status message as well. */
+ is_request = sip_is_request(tvb, eol);
+ is_known_request = sip_is_known_request(tvb, 0);
+ /* XXX - Is this case-sensitive? RFC 2543 didn't explicitly say. */
+ if (tvb_strneql(tvb, 0, SIP2_HDR, SIP2_HDR_LEN) != 0 && ! is_request)
+ goto bad;
+
+ if (check_col(pinfo->cinfo, COL_PROTOCOL))
col_set_str(pinfo->cinfo, COL_PROTOCOL, "SIP");
-
+ req_descr = is_known_request ? "Request" : "Unknown request";
if (check_col(pinfo->cinfo, COL_INFO))
col_add_fstr(pinfo->cinfo, COL_INFO, "%s: %s",
- is_request ? "Request" : "Status",
- is_request ?
- tvb_format_text(tvb, 0, eol - SIP2_HDR_LEN) :
- tvb_format_text(tvb, SIP2_HDR_LEN, eol - SIP2_HDR_LEN));
+ is_request ? req_descr : "Status",
+ is_request ?
+ tvb_format_text(tvb, 0, eol - SIP2_HDR_LEN - 1) :
+ tvb_format_text(tvb, SIP2_HDR_LEN + 1, eol - SIP2_HDR_LEN - 1));
+
msg_offset = sip_get_msg_offset(tvb, offset);
- if (msg_offset < 0) goto bad;
+ if (msg_offset < 0) {
+ /*
+ * XXX - this may just mean that the entire SIP message
+ * didn't fit in this TCP segment.
+ */
+ goto bad;
+ }
if (tree) {
proto_item *ti, *th;
proto_tree *sip_tree, *hdr_tree;
- ti = proto_tree_add_item(tree, proto_sip, tvb, 0, tvb_length(tvb), FALSE);
+ ti = proto_tree_add_item(tree, proto_sip, tvb, 0, -1, FALSE);
sip_tree = proto_item_add_subtree(ti, ett_sip);
- proto_tree_add_text(sip_tree, tvb, 0, next_offset, "%s-Line: %s",
- is_request ? "Request" : "Status",
+ proto_tree_add_text(sip_tree, tvb, 0, next_offset, "%s line: %s",
+ is_request ? req_descr : "Status",
tvb_format_text(tvb, 0, eol));
offset = next_offset;
/* - 2 since we have a CRLF separating the message-body */
while (msg_offset - 2 > (int) offset) {
- eol = tvb_find_line_end(tvb, offset, -1, &next_offset);
+ eol = tvb_find_line_end(tvb, offset, -1, &next_offset,
+ FALSE);
proto_tree_add_text(hdr_tree, tvb, offset, next_offset - offset, "%s",
tvb_format_text(tvb, offset, eol));
offset = next_offset;
offset += 2; /* Skip the CRLF mentioned above */
}
- if (tvb_length_remaining(tvb, msg_offset) > 0) {
- next_tvb = tvb_new_subset(tvb, offset, -1, -1);
+ if (tvb_offset_exists(tvb, msg_offset)) {
+ next_tvb = tvb_new_subset(tvb, msg_offset, -1, -1);
call_dissector(sdp_handle, next_tvb, pinfo, tree);
}
return;
}
+static gboolean
+dissect_sip_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+ gint eol, next_offset;
+
+ /*
+ * This is a heuristic dissector, which means we get all the
+ * UDP and TCP traffic not sent to a known dissector and not
+ * claimed by a heuristic dissector called before us!
+ * So we first check if the frame is really meant for us.
+ */
+
+ /*
+ * Check for a response.
+ * First, make sure we have enough data to do the check.
+ */
+ if (!tvb_bytes_exist(tvb, 0, SIP2_HDR_LEN)) {
+ /*
+ * We don't.
+ */
+ return FALSE;
+ }
+
+ /*
+ * Now see if we have a response header; they begin with
+ * "SIP/2.0".
+ */
+ if (tvb_strneql(tvb, 0, SIP2_HDR, SIP2_HDR_LEN) != 0) {
+ /*
+ * We don't, so this isn't a response; check for a request.
+ * They *end* with "SIP/2.0".
+ */
+ eol = tvb_find_line_end(tvb, 0, -1, &next_offset, FALSE);
+ if (eol <= (gint)SIP2_HDR_LEN) {
+ /*
+ * The line isn't long enough to end with "SIP/2.0".
+ */
+ return FALSE;
+ }
+ if (!tvb_bytes_exist(tvb, eol - SIP2_HDR_LEN, SIP2_HDR_LEN)) {
+ /*
+ * We don't have enough of the data in the line
+ * to check.
+ */
+ return FALSE;
+ }
+
+ if (tvb_strneql(tvb, eol - SIP2_HDR_LEN, SIP2_HDR, SIP2_HDR_LEN - 1) != 0) {
+ /*
+ * Not a request, either.
+ */
+ return FALSE;
+ }
+ }
+
+ /*
+ * The message seems to be a valid SIP message!
+ */
+ dissect_sip(tvb, pinfo, tree);
+
+ return TRUE;
+}
+
/* Returns the offset to the start of the optional message-body, or
- * -1 for an error.
+ * -1 if not found.
*/
static gint sip_get_msg_offset(tvbuff_t *tvb, guint32 offset)
{
gint eol;
- while ((eol = tvb_find_guint8(tvb, offset, tvb_length_remaining(tvb, offset), '\r')) > 0) {
- if (tvb_get_guint8(tvb, eol + 1) == '\n' &&
- tvb_get_guint8(tvb, eol + 2) == '\r' &&
- tvb_get_guint8(tvb, eol + 3) == '\n')
- return eol + 4;
- offset = eol + 2;
+ while ((eol = tvb_find_guint8(tvb, offset, -1, '\r')) > 0
+ && tvb_bytes_exist(tvb, eol, 4)) {
+ if (tvb_get_guint8(tvb, eol + 1) == '\n' &&
+ tvb_get_guint8(tvb, eol + 2) == '\r' &&
+ tvb_get_guint8(tvb, eol + 3) == '\n')
+ return eol + 4;
+ offset = eol + 2;
}
return -1;
}
-
-static gboolean sip_is_request(tvbuff_t *tvb, guint32 offset)
+
+/* From section 4.1 of RFC 2543:
+ *
+ * Request-Line = Method SP Request-URI SP SIP-Version CRLF
+ */
+
+static gboolean sip_is_request(tvbuff_t *tvb, gint eol)
+{
+ gint meth_len, req_len, req_colon_pos;
+ guint8 req_start, ver_start, ver_len;
+
+ meth_len = tvb_find_guint8(tvb, 0, -1, ' ');
+ req_start = meth_len + 1;
+ req_len = tvb_find_guint8(tvb, req_start, -1, ' ') - meth_len - 1;
+ req_colon_pos = tvb_find_guint8(tvb, req_start + 1, -1, ':');
+ ver_start = meth_len + req_len + 2;
+ ver_len = eol - req_len - meth_len - 2; /*CRLF, plus two spaces */
+
+ /* Do we have:
+ * A method of at least one character?
+ * A URI consisting of at least three characters?
+ * A version string length matching that of SIP2_HDR?
+ */
+ if (meth_len <= 0 || req_len <= 3 || ver_len != SIP2_HDR_LEN)
+ return FALSE;
+
+ /* Does our method have a colon character? */
+ if (req_colon_pos < 0 || req_colon_pos > ver_start)
+ return FALSE;
+ /* XXX - Check for a proper URI prefix? */
+
+ /* Do we have a proper version string? */
+ if (tvb_strneql(tvb, ver_start, SIP2_HDR, SIP2_HDR_LEN))
+ return TRUE;
+
+ return TRUE;
+}
+
+static gboolean sip_is_known_request(tvbuff_t *tvb, guint32 offset)
{
- u_int i;
+ guint8 i, meth_len;
+
+ meth_len = tvb_find_guint8(tvb, 0, -1, ' ');
for (i = 1; i < array_length(sip_methods); i++) {
- if (tvb_strneql(tvb, offset, sip_methods[i], strlen(sip_methods[i])) == 0)
+ if ((meth_len == strlen(sip_methods[i])) && tvb_strneql(tvb, offset, sip_methods[i], strlen(sip_methods[i])) == 0)
return TRUE;
}
/* Register the protocol with Ethereal */
void proto_register_sip(void)
-{
+{
/* Setup list of header fields */
static hf_register_info hf[] = {
/* Register the protocol name and description */
proto_sip = proto_register_protocol("Session Initiation Protocol",
- "SIP", "sip");
+ "SIP", "sip");
/* Required function calls to register the header fields and subtrees used */
proto_register_field_array(proto_sip, hf, array_length(hf));
void
proto_reg_handoff_sip(void)
{
- dissector_handle_t sip_handle;
+ dissector_handle_t sip_handle;
- sip_handle = create_dissector_handle(dissect_sip, proto_sip);
+ sip_handle = create_dissector_handle(dissect_sip, proto_sip);
dissector_add("tcp.port", TCP_PORT_SIP, sip_handle);
dissector_add("udp.port", UDP_PORT_SIP, sip_handle);
- /*
- * Get a handle for the SDP dissector.
- */
- sdp_handle = find_dissector("sdp");
- data_handle = find_dissector("data");
+ heur_dissector_add( "udp", dissect_sip_heur, proto_sip );
+ heur_dissector_add( "tcp", dissect_sip_heur, proto_sip );
+
+ /*
+ * Get a handle for the SDP dissector.
+ */
+ sdp_handle = find_dissector("sdp");
+ data_handle = find_dissector("data");
}