aboutsummaryrefslogtreecommitdiffstats
path: root/epan/dissectors/packet-sip.c
diff options
context:
space:
mode:
Diffstat (limited to 'epan/dissectors/packet-sip.c')
-rw-r--r--epan/dissectors/packet-sip.c130
1 files changed, 120 insertions, 10 deletions
diff --git a/epan/dissectors/packet-sip.c b/epan/dissectors/packet-sip.c
index b7a78a7211..0ac259fc4f 100644
--- a/epan/dissectors/packet-sip.c
+++ b/epan/dissectors/packet-sip.c
@@ -220,6 +220,7 @@ static gint ett_sip_tc_uri = -1;
static expert_field ei_sip_unrecognized_header = EI_INIT;
static expert_field ei_sip_header_not_terminated = EI_INIT;
+static expert_field ei_sip_odd_register_response = EI_INIT;
static expert_field ei_sip_sipsec_malformed = EI_INIT;
/* PUBLISH method added as per http://www.ietf.org/internet-drafts/draft-ietf-sip-publish-01.txt */
@@ -1519,7 +1520,8 @@ display_sip_uri (tvbuff_t *tvb, proto_tree *sip_element_tree, uri_offset_info* u
* * contact-param = (name-addr / addr-spec) *(SEMI contact-params)
*/
static gint
-dissect_sip_contact_item(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gint start_offset, gint line_end_offset)
+dissect_sip_contact_item(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gint start_offset, gint line_end_offset,
+ guchar* contacts_expires_0, guchar* contacts_expires_unknown)
{
gchar c;
gint current_offset;
@@ -1527,6 +1529,8 @@ dissect_sip_contact_item(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gi
gint contact_params_start_offset = -1;
/*gint contact_param_end_offset = -1;*/
uri_offset_info uri_offsets;
+ gboolean end_of_hdr = FALSE;
+ gboolean has_expires_param = FALSE;
/* skip Spaces and Tabs */
start_offset = tvb_skip_wsp(tvb, start_offset, line_end_offset - start_offset);
@@ -1547,11 +1551,23 @@ dissect_sip_contact_item(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gi
}
display_sip_uri(tvb, tree, &uri_offsets, &sip_contact_uri);
+ /* check if there's a comma before a ';', in which case we stop parsing this item at the comma */
+ queried_offset = tvb_find_guint8(tvb, uri_offsets.uri_end, line_end_offset - uri_offsets.uri_end, ',');
+
/* Check if we have contact parameters, the uri should be followed by a ';' */
contact_params_start_offset = tvb_find_guint8(tvb, uri_offsets.uri_end, line_end_offset - uri_offsets.uri_end, ';');
/* check if contact-params is present */
- if(contact_params_start_offset == -1)
+ if(contact_params_start_offset == -1) {
+ /* no expires param */
+ (*contacts_expires_unknown)++;
return line_end_offset;
+ }
+
+ if (queried_offset != -1 && queried_offset < contact_params_start_offset) {
+ /* no expires param */
+ (*contacts_expires_unknown)++;
+ return queried_offset;
+ }
/* Move current offset to the start of the first param */
contact_params_start_offset++;
@@ -1568,6 +1584,11 @@ dissect_sip_contact_item(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gi
queried_offset++;
c = tvb_get_guint8(tvb, queried_offset);
switch (c) {
+ /* prevent tree from displaying the '\r\n' as part of the param */
+ case '\r':
+ case '\n':
+ end_of_hdr = TRUE;
+ /* fall through */
case ',':
case ';':
case '"':
@@ -1599,15 +1620,56 @@ dissect_sip_contact_item(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gi
}
proto_tree_add_item(tree, hf_sip_contact_param, tvb, contact_params_start_offset ,
current_offset - contact_params_start_offset, ENC_ASCII|ENC_NA);
+
+ /* need to check for an 'expires' parameter
+ * TODO: this should be done in a common way for all headers,
+ * but To/From/etc do their own right now so doing the same here
+ */
+ /* also, this is a bad way of checking param names, but it's what To/From
+ * etc, do. But legally "exPiRes = value" is also legit.
+ */
+ if (tvb_strncaseeql(tvb, contact_params_start_offset, "expires=", 8) == 0) {
+ /* if the expires param value is 0, then it's de-registering
+ * this assumes the message is a REGISTER request/response, but these
+ * contacts_expires_0/contacts_expires_unknown variables only get used then,
+ * so that's ok
+ */
+ if (atoi(tvb_get_string_enc(wmem_packet_scope(), tvb, contact_params_start_offset+8,
+ current_offset - (contact_params_start_offset+8), ENC_UTF_8)) == 0) {
+ (*contacts_expires_0)++;
+ /* it is actually unusual - arguably invalid - for a SIP REGISTER
+ * 200 OK _response_ to contain Contacts with expires=0.
+ */
+ if (stat_info && stat_info->response_code > 199 && stat_info->response_code < 300) {
+ proto_tree_add_expert_format(tree, pinfo, &ei_sip_odd_register_response,
+ tvb, contact_params_start_offset, current_offset - contact_params_start_offset,
+ "SIP REGISTER %d response contains Contact with expires=0",
+ stat_info->response_code);
+ }
+ } else {
+ has_expires_param = TRUE;
+ }
+ }
+
/* In case there are more parameters, point to the start of it */
contact_params_start_offset = current_offset+1;
queried_offset = contact_params_start_offset;
+ if (end_of_hdr) {
+ /* '\r' or '\n' found, stop parsing and also set current offset to end
+ * so the return value indicates we reached line end
+ */
+ current_offset = line_end_offset;
+ }
if (c == ',') {
/* comma separator found, stop parsing of current contact-param here */
break;
}
}
+ if (!has_expires_param) {
+ (*contacts_expires_unknown)++;
+ }
+
return current_offset;
}
@@ -2262,7 +2324,7 @@ dissect_sip_common(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tr
*sip_element_tree = NULL, *message_body_tree = NULL, *cseq_tree = NULL,
*via_tree = NULL, *reason_tree = NULL, *rack_tree = NULL,
*route_tree = NULL, *security_client_tree = NULL;
- guchar contacts = 0, contact_is_star = 0, expires_is_0 = 0;
+ guchar contacts = 0, contact_is_star = 0, expires_is_0 = 0, contacts_expires_0 = 0, contacts_expires_unknown = 0;
guint32 cseq_number = 0;
guchar cseq_number_set = 0;
char cseq_method[MAX_CSEQ_METHOD_SIZE] = "";
@@ -2342,7 +2404,9 @@ dissect_sip_common(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tr
}
}
- /* Initialise stat info for passing to tap */
+ /* Initialise stat info for passing to tap
+ * Note: this isn't _only_ for taps - internal code here uses it too
+ */
stat_info = wmem_new0(wmem_packet_scope(), sip_info_value_t);
col_set_str(pinfo->cinfo, COL_PROTOCOL, "SIP");
@@ -3050,7 +3114,8 @@ dissect_sip_common(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tr
if(hdr_tree) {
comma_offset = value_offset;
- while((comma_offset = dissect_sip_contact_item(tvb, pinfo, sip_element_tree, comma_offset, next_offset)) != -1)
+ while((comma_offset = dissect_sip_contact_item(tvb, pinfo, sip_element_tree, comma_offset,
+ next_offset, &contacts_expires_0, &contacts_expires_unknown)) != -1)
{
contacts++;
if(comma_offset == next_offset)
@@ -3300,29 +3365,73 @@ dissect_sip_common(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tr
}
/* Add to info column interesting things learned from header fields. */
+
+ /* for either REGISTER requests or responses, any contacts without expires
+ * parameter use the Expires header's value
+ */
+ if (expires_is_0) {
+ /* this may add nothing, but that's ok */
+ contacts_expires_0 += contacts_expires_unknown;
+ }
+
/* Registration requests */
if (current_method_idx == SIP_METHOD_REGISTER)
{
+ /* TODO: what if there's a *-contact but also non-*-contacts?
+ * we should create expert info for that someday I guess
+ */
if (contact_is_star && expires_is_0)
{
- col_append_str(pinfo->cinfo, COL_INFO, " (remove all bindings)");
+ col_append_str(pinfo->cinfo, COL_INFO, " (remove all bindings)");
+ }
+ else
+ if (contacts_expires_0 > 0)
+ {
+ col_append_fstr(pinfo->cinfo, COL_INFO, " (remove %d binding%s)",
+ contacts_expires_0, contacts_expires_0 == 1 ? "":"s");
+ if (contacts > contacts_expires_0) {
+ col_append_fstr(pinfo->cinfo, COL_INFO, " (add %d binding%s)",
+ contacts - contacts_expires_0,
+ (contacts - contacts_expires_0 == 1) ? "":"s");
+ }
}
else
if (!contacts)
{
- col_append_str(pinfo->cinfo, COL_INFO, " (fetch bindings)");
+ col_append_str(pinfo->cinfo, COL_INFO, " (fetch bindings)");
+ }
+ else
+ {
+ col_append_fstr(pinfo->cinfo, COL_INFO, " (%d binding%s)",
+ contacts, contacts == 1 ? "":"s");
}
}
- /* Registration responses */
- if (line_type == STATUS_LINE && (strcmp(cseq_method, "REGISTER") == 0))
+ /* Registration responses - this info only makes sense in 2xx responses */
+ if (line_type == STATUS_LINE && (strcmp(cseq_method, "REGISTER") == 0) &&
+ stat_info && stat_info->response_code > 199 && stat_info->response_code < 300)
{
- col_append_fstr(pinfo->cinfo, COL_INFO, " (%d bindings)", contacts);
+ if (contacts_expires_0 > 0) {
+ col_append_fstr(pinfo->cinfo, COL_INFO, " (removed %d binding%s)",
+ contacts_expires_0, contacts_expires_0 == 1 ? "":"s");
+ if (contacts > contacts_expires_0) {
+ col_append_fstr(pinfo->cinfo, COL_INFO, " (%d binding%s kept)",
+ contacts - contacts_expires_0,
+ (contacts - contacts_expires_0 == 1) ? "":"s");
+ }
+ } else {
+ col_append_fstr(pinfo->cinfo, COL_INFO, " (%d binding%s)",
+ contacts, contacts == 1 ? "":"s");
+ }
}
/* We've finished writing to the info col for this SIP message
* Set fence in case there is more than one (SIP)message in the frame
*/
+ /* XXX: this produces ugly output, since usually there's only one SIP
+ * message in a frame yet this '|' gets added at the end. Need a better
+ * way to do this.
+ */
col_append_str(pinfo->cinfo, COL_INFO, " | ");
col_set_fence(pinfo->cinfo, COL_INFO);
@@ -5390,6 +5499,7 @@ void proto_register_sip(void)
static ei_register_info ei[] = {
{ &ei_sip_unrecognized_header, { "sip.unrecognized_header", PI_UNDECODED, PI_NOTE, "Unrecognised SIP header", EXPFILL }},
{ &ei_sip_header_not_terminated, { "sip.header_not_terminated", PI_MALFORMED, PI_WARN, "Header not terminated by empty line (CRLF)", EXPFILL }},
+ { &ei_sip_odd_register_response, { "sip.response.unsual", PI_RESPONSE_CODE, PI_WARN, "SIP Response is unusual", EXPFILL }},
{ &ei_sip_sipsec_malformed, { "sip.sec_mechanism.malformed", PI_MALFORMED, PI_WARN, "SIP Security-mechanism header malformed", EXPFILL }},
};