wireshark/epan/dissectors/packet-sip.c

4639 lines
146 KiB
C

/* packet-sip.c
* Routines for the Session Initiation Protocol (SIP) dissection.
* RFCs 3261-3264
*
* TODO:
* hf_ display filters for headers of SIP extension RFCs (ongoing)
* Use hash table for list of headers
* Align SIP methods with recent Internet Drafts or RFCs
*
* Copyright 2000, Heikki Vatiainen <hessu@cs.tut.fi>
* Copyright 2001, Jean-Francois Mule <jfm@cablelabs.com>
* Copyright 2004, Anders Broman <anders.broman@ericsson.com>
*
* $Id$
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* 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.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <epan/prefs.h>
#include <glib.h>
#include <wsutil/str_util.h>
#include <epan/packet.h>
#include <epan/req_resp_hdrs.h>
#include <epan/emem.h>
#include <epan/strutil.h>
#include <wsutil/str_util.h>
#include "packet-isup.h"
#include "packet-sip.h"
#include <epan/tap.h>
#include <epan/expert.h>
#include "packet-tcp.h"
#include "packet-ssl.h"
#define TCP_PORT_SIP 5060
#define UDP_PORT_SIP 5060
#define TLS_PORT_SIP 5061
static gint sip_tap = -1;
static dissector_handle_t sigcomp_handle;
/* Initialize the protocol and registered fields */
static gint proto_sip = -1;
static gint proto_raw_sip = -1;
static gint hf_raw_sip_line = -1;
static gint hf_msg_hdr = -1;
static gint hf_sip_Method = -1;
static gint hf_Request_Line = -1;
static gint hf_sip_ruri = -1;
static gint hf_sip_ruri_user = -1;
static gint hf_sip_ruri_host = -1;
static gint hf_sip_ruri_port = -1;
static gint hf_Status_Code = -1;
static gint hf_Status_Line = -1;
static gint hf_sip_display = -1;
static gint hf_sip_to_addr = -1;
static gint hf_sip_to_user = -1;
static gint hf_sip_to_host = -1;
static gint hf_sip_to_port = -1;
static gint hf_sip_from_addr = -1;
static gint hf_sip_from_user = -1;
static gint hf_sip_from_host = -1;
static gint hf_sip_from_port = -1;
static gint hf_sip_tag = -1;
static gint hf_sip_pai_addr = -1;
static gint hf_sip_pai_user = -1;
static gint hf_sip_pai_host = -1;
static gint hf_sip_pai_port = -1;
static gint hf_sip_pmiss_addr = -1;
static gint hf_sip_pmiss_user = -1;
static gint hf_sip_pmiss_host = -1;
static gint hf_sip_pmiss_port = -1;
static gint hf_sip_ppi_addr = -1;
static gint hf_sip_ppi_user = -1;
static gint hf_sip_ppi_host = -1;
static gint hf_sip_ppi_port = -1;
static gint hf_sip_tc_addr = -1;
static gint hf_sip_tc_user = -1;
static gint hf_sip_tc_host = -1;
static gint hf_sip_tc_port = -1;
static gint hf_sip_tc_turi = -1;
static gint hf_sip_uri = -1;
static gint hf_sip_contact_addr = -1;
static gint hf_sip_contact_item = -1;
static gint hf_sip_resend = -1;
static gint hf_sip_original_frame = -1;
static gint hf_sip_matching_request_frame = -1;
static gint hf_sip_response_time = -1;
static gint hf_sip_release_time = -1;
static gint hf_sip_auth = -1;
static gint hf_sip_auth_scheme = -1;
static gint hf_sip_auth_digest_response = -1;
static gint hf_sip_auth_nc = -1;
static gint hf_sip_auth_username = -1;
static gint hf_sip_auth_realm = -1;
static gint hf_sip_auth_nonce = -1;
static gint hf_sip_auth_algorithm = -1;
static gint hf_sip_auth_opaque = -1;
static gint hf_sip_auth_qop = -1;
static gint hf_sip_auth_cnonce = -1;
static gint hf_sip_auth_uri = -1;
static gint hf_sip_auth_domain = -1;
static gint hf_sip_auth_stale = -1;
static gint hf_sip_auth_auts = -1;
static gint hf_sip_auth_rspauth = -1;
static gint hf_sip_auth_nextnonce = -1;
static gint hf_sip_auth_ik = -1;
static gint hf_sip_auth_ck = -1;
static gint hf_sip_cseq_seq_no = -1;
static gint hf_sip_cseq_method = -1;
static gint hf_sip_via_transport = -1;
static gint hf_sip_via_sent_by_address = -1;
static gint hf_sip_via_sent_by_port = -1;
static gint hf_sip_via_branch = -1;
static gint hf_sip_via_maddr = -1;
static gint hf_sip_via_rport = -1;
static gint hf_sip_via_received = -1;
static gint hf_sip_via_ttl = -1;
static gint hf_sip_via_comp = -1;
static gint hf_sip_via_sigcomp_id = -1;
static gint hf_sip_rack_rseq_no = -1;
static gint hf_sip_rack_cseq_no = -1;
static gint hf_sip_rack_cseq_method = -1;
static gint hf_sip_msg_body = -1;
/* Initialize the subtree pointers */
static gint ett_sip = -1;
static gint ett_sip_reqresp = -1;
static gint ett_sip_hdr = -1;
static gint ett_sip_ext_hdr = -1;
static gint ett_raw_text = -1;
static gint ett_sip_element = -1;
static gint ett_sip_uri = -1;
static gint ett_sip_contact_item = -1;
static gint ett_sip_message_body = -1;
static gint ett_sip_cseq = -1;
static gint ett_sip_via = -1;
static gint ett_sip_reason = -1;
static gint ett_sip_rack = -1;
static gint ett_sip_ruri = -1;
static gint ett_sip_to_uri = -1;
static gint ett_sip_from_uri = -1;
static gint ett_sip_pai_uri = -1;
static gint ett_sip_pmiss_uri = -1;
static gint ett_sip_ppi_uri = -1;
static gint ett_sip_tc_uri = -1;
/* PUBLISH method added as per http://www.ietf.org/internet-drafts/draft-ietf-sip-publish-01.txt */
static const char *sip_methods[] = {
#define SIP_METHOD_INVALID 0
"<Invalid method>", /* Pad so that the real methods start at index 1 */
#define SIP_METHOD_ACK 1
"ACK",
#define SIP_METHOD_BYE 2
"BYE",
#define SIP_METHOD_CANCEL 3
"CANCEL",
#define SIP_METHOD_DO 4
"DO",
#define SIP_METHOD_INFO 5
"INFO",
#define SIP_METHOD_INVITE 6
"INVITE",
#define SIP_METHOD_MESSAGE 7
"MESSAGE",
#define SIP_METHOD_NOTIFY 8
"NOTIFY",
#define SIP_METHOD_OPTIONS 9
"OPTIONS",
#define SIP_METHOD_PRACK 10
"PRACK",
#define SIP_METHOD_QAUTH 11
"QAUTH",
#define SIP_METHOD_REFER 12
"REFER",
#define SIP_METHOD_REGISTER 13
"REGISTER",
#define SIP_METHOD_SPRACK 14
"SPRACK",
#define SIP_METHOD_SUBSCRIBE 15
"SUBSCRIBE",
#define SIP_METHOD_UPDATE 16
"UPDATE",
#define SIP_METHOD_PUBLISH 17
"PUBLISH"
};
/* from RFC 3261
* Updated with info from http://www.iana.org/assignments/sip-parameters
* (last updated 2009-11-11)
* Updated with: http://www.ietf.org/internet-drafts/draft-ietf-sip-resource-priority-05.txt
*/
typedef struct {
const char *name;
const char *compact_name;
} sip_header_t;
static const sip_header_t sip_headers[] = {
{ "Unknown-header", NULL }, /* 0 Pad so that the real headers start at index 1 */
{ "Accept", NULL }, /* 1 */
{ "Accept-Contact", "a" }, /* 2 RFC3841 */
{ "Accept-Encoding", NULL }, /* 3 */
{ "Accept-Language", NULL }, /* 4 */
{ "Accept-Resource-Priority", NULL }, /* 5 RFC4412 */
{ "Alert-Info", NULL },
{ "Allow", NULL },
{ "Allow-Events", "u" }, /* 8 RFC3265 */
{ "Answer-Mode", NULL }, /* 9 RFC5373 */
{ "Authentication-Info", NULL },
{ "Authorization", NULL }, /* 11 */
{ "Call-ID", "i" },
{ "Call-Info", NULL },
{ "Contact", "m" },
{ "Content-Disposition", NULL },
{ "Content-Encoding", "e" }, /* 16 */
{ "Content-Language", NULL },
{ "Content-Length", "l" },
{ "Content-Type", "c" },
{ "CSeq", NULL },
{ "Date", NULL }, /* 21 */
/* Encryption (Deprecated) [RFC3261] */
{ "Error-Info", NULL },
{ "Event", "o" },
{ "Expires", NULL },
/* Flow-Timer [RFC5626]*/
{ "From", "f" }, /* 25 */
/* Hide [RFC3261] (deprecated)*/
{ "History-Info", NULL }, /* 26 RFC4244 */
{ "Identity", "y" }, /* 27 RFC4474 */
{ "Identity-Info", "n" }, /* 28 RFC4474 */
{ "In-Reply-To", NULL }, /* 29 RFC3261 */
{ "Join", NULL }, /* 30 RFC3911 */
{ "Max-Breadth", NULL }, /* 31 RFC5393*/
{ "Max-Forwards", NULL }, /* 32 */
{ "MIME-Version", NULL }, /* 33 */
{ "Min-Expires", NULL }, /* 34 */
{ "Min-SE", NULL }, /* 35 RFC4028 */
{ "Organization", NULL }, /* 36 RFC3261 */
{ "P-Access-Network-Info", NULL }, /* 37 RFC3455 */
{ "P-Answer-State", NULL }, /* 38 RFC4964 */
{ "P-Asserted-Identity", NULL }, /* 39 RFC3325 */
{ "P-Associated-URI", NULL }, /* 40 RFC3455 */
{ "P-Called-Party-ID", NULL }, /* 41 RFC3455 */
{ "P-Charging-Function-Addresses", NULL },/* 42 RFC3455 */
{ "P-Charging-Vector", NULL }, /* 43 RFC3455 */
{ "P-DCS-Trace-Party-ID", NULL }, /* 44 RFC5503 */
{ "P-DCS-OSPS", NULL }, /* 45 RFC5503 */
{ "P-DCS-Billing-Info", NULL }, /* 46 RFC5503 */
{ "P-DCS-LAES", NULL }, /* 47 RFC5503 */
{ "P-DCS-Redirect", NULL }, /* 48 RFC5503 */
{ "P-Early-Media", NULL }, /* 49 RFC5009 */
{ "P-Media-Authorization", NULL }, /* 50 RFC3313 */
{ "P-Preferred-Identity", NULL }, /* 51 RFC3325 */
{ "P-Profile-Key", NULL }, /* 52 RFC5002 */
{ "P-Refused-URI-List", NULL }, /* 53 RFC5318 */
{ "P-Served-User", NULL }, /* 54 RFC5502 */
{ "P-User-Database", NULL }, /* 55 RFC4457 */
{ "P-Visited-Network-ID", NULL }, /* 56 RFC3455 */
{ "Path", NULL }, /* 57 RFC3327 */
{ "Permission-Missing", NULL }, /* 58 RFC5360 */
{ "Priority", NULL }, /* 59 RFC3261 */
{ "Priv-Answer-Mode", NULL }, /* 60 RFC5373 */
{ "Privacy", NULL }, /* 61 RFC3323 */
{ "Proxy-Authenticate", NULL }, /* 62 */
{ "Proxy-Authorization", NULL }, /* 63 */
{ "Proxy-Require", NULL }, /* 64 */
{ "RAck", NULL }, /* 65 RFC3262 */
{ "Reason", NULL }, /* 66 RFC3326 */
{ "Record-Route", NULL }, /* 67 */
{ "Refer-Sub", NULL }, /* 68 RFC4488 */
{ "Refer-To", "r" }, /* 69 RFC3515 */
{ "Referred-By", "b" }, /* 70 RFC3892 */
{ "Reject-Contact", "j" }, /* 71 RFC3841 */
{ "Replaces", NULL }, /* 72 RFC3891 */
{ "Reply-To", NULL }, /* 73 RFC3261 */
{ "Request-Disposition", "d" }, /* 74 RFC3841 */
{ "Require", NULL }, /* 75 RFC3261 */
{ "Resource-Priority", NULL }, /* 76 RFC4412 */
/*
{ "Response-Key (Deprecated) [RFC3261]
*/
{ "Retry-After", NULL }, /* 77 RFC3261 */
{ "Route", NULL }, /* 78 RFC3261 */
{ "RSeq", NULL }, /* 79 RFC3262 */
{ "Security-Client", NULL }, /* 80 RFC3329 */
{ "Security-Server", NULL }, /* 81 RFC3329 */
{ "Security-Verify", NULL }, /* 82 RFC3329 */
{ "Server", NULL }, /* 83 RFC3261 */
{ "Service-Route", NULL }, /* 84 RFC3608 */
{ "Session-Expires", "x" }, /* 85 RFC4028 */
{ "SIP-ETag", NULL }, /* 86 RFC3903 */
{ "SIP-If-Match", NULL }, /* 87 RFC3903 */
{ "Subject", "s" }, /* 88 RFC3261 */
{ "Subscription-State", NULL }, /* 89 RFC3265 */
{ "Supported", "k" }, /* 90 RFC3261 */
{ "Target-Dialog", NULL }, /* 81 RFC4538 */
{ "Timestamp", NULL }, /* 92 RFC3261 */
{ "To", "t" }, /* 93 RFC3261 */
{ "Trigger-Consent", NULL }, /* 94 RFC5360 */
{ "Unsupported", NULL }, /* 95 RFC3261 */
{ "User-Agent", NULL }, /* 96 RFC3261 */
{ "Via", "v" }, /* 97 RFC3261 */
{ "Warning", NULL }, /* 98 RFC3261 */
{ "WWW-Authenticate", NULL }, /* 99 RFC3261 */
};
#define POS_ACCEPT 1
#define POS_ACCEPT_CONTACT 2
#define POS_ACCEPT_ENCODING 3
#define POS_ACCEPT_LANGUAGE 4
#define POS_ACCEPT_RESOURCE_PRIORITY 5
#define POS_ALERT_INFO 6
#define POS_ALLOW 7
#define POS_ALLOW_EVENTS 8
#define POS_ANSWER_MODE 9
#define POS_AUTHENTICATION_INFO 10
#define POS_AUTHORIZATION 11
#define POS_CALL_ID 12
#define POS_CALL_INFO 13
#define POS_CONTACT 14
#define POS_CONTENT_DISPOSITION 15
#define POS_CONTENT_ENCODING 16
#define POS_CONTENT_LANGUAGE 17
#define POS_CONTENT_LENGTH 18
#define POS_CONTENT_TYPE 19
#define POS_CSEQ 20
#define POS_DATE 21
#define POS_ERROR_INFO 22
#define POS_EVENT 23
#define POS_EXPIRES 24
#define POS_FROM 25
#define POS_HISTORY_INFO 26
#define POS_IDENTITY 27
#define POS_IDENTITY_INFO 28
#define POS_IN_REPLY_TO 29
#define POS_JOIN 30
#define POS_MAX_BREADTH 31
#define POS_MAX_FORWARDS 32
#define POS_MIME_VERSION 33
#define POS_MIN_EXPIRES 34
#define POS_MIN_SE 35
#define POS_ORGANIZATION 36
#define POS_P_ACCESS_NETWORK_INFO 37
#define POS_P_ANSWER_STATE 38
#define POS_P_ASSERTED_IDENTITY 39
#define POS_P_ASSOCIATED_URI 40
#define POS_P_CALLED_PARTY_ID 41
#define POS_P_CHARGING_FUNCTION_ADDRESSES 42
#define POS_P_CHARGING_VECTOR 43
#define POS_P_DCS_TRACE_PARTY_ID 44
#define POS_P_DCS_OSPS 45
#define POS_P_DCS_BILLING_INFO 46
#define POS_P_DCS_LAES 47
#define POS_P_DCS_REDIRECT 48
#define POS_P_EARLY_MEDIA 49
#define POS_P_MEDIA_AUTHORIZATION 50
#define POS_P_PREFERRED_IDENTITY 51
#define POS_P_PROFILE_KEY 52
#define POS_P_REFUSED_URI_LST 53
#define POS_P_SERVED_USER 54
#define POS_P_USER_DATABASE 55
#define POS_P_VISITED_NETWORK_ID 56
#define POS_PATH 57
#define POS_PERMISSION_MISSING 58
#define POS_PRIORITY 59
#define POS_PRIV_ANSWER_MODE 60
#define POS_PRIVACY 61
#define POS_PROXY_AUTHENTICATE 62
#define POS_PROXY_AUTHORIZATION 63
#define POS_PROXY_REQUIRE 64
#define POS_RACK 65
#define POS_REASON 66
#define POS_RECORD_ROUTE 67
#define POS_REFER_SUB 68
#define POS_REFER_TO 69
#define POS_REFERED_BY 70
#define POS_REJECT_CONTACT 71
#define POS_REPLACES 72
#define POS_REPLY_TO 73
#define POS_REQUEST_DISPOSITION 74
#define POS_REQUIRE 75
#define POS_RESOURCE_PRIORITY 76
#define POS_RETRY_AFTER 77
#define POS_ROUTE 78
#define POS_RSEQ 79
#define POS_SECURITY_CLIENT 80
#define POS_SECURITY_SERVER 81
#define POS_SECURITY_VERIFY 82
#define POS_SERVER 83
#define POS_SERVICE_ROUTE 84
#define POS_SESSION_EXPIRES 85
#define POS_SIP_ETAG 86
#define POS_SIP_IF_MATCH 87
#define POS_SUBJECT 88
#define POS_SUBSCRIPTION_STATE 89
#define POS_SUPPORTED 90
#define POS_TARGET_DALOG 91
#define POS_TIMESTAMP 92
#define POS_TO 93
#define POS_TRIGGER_CONSENT 94
#define POS_UNSUPPORTED 95
#define POS_USER_AGENT 96
#define POS_VIA 97
#define POS_WARNING 98
#define POS_WWW_AUTHENTICATE 99
static gint hf_header_array[] = {
-1, /* 0"Unknown-header" - Pad so that the real headers start at index 1 */
-1, /* 1"Accept" */
-1, /* 2"Accept-Contact" RFC3841 */
-1, /* 3"Accept-Encoding" */
-1, /* 4"Accept-Language" */
-1, /* 5"Accept-Resource-Priority" RFC4412 */
-1, /* 6"Alert-Info", */
-1, /* 7"Allow", */
-1, /* 8"Allow-Events", RFC3265 */
-1, /* 9"Answer-Mode" RFC5373 */
-1, /* 10"Authentication-Info" */
-1, /* 11"Authorization", */
-1, /* 12"Call-ID", */
-1, /* 13"Call-Info" */
-1, /* 14"Contact", */
-1, /* 15"Content-Disposition", */
-1, /* 16"Content-Encoding", */
-1, /* 17"Content-Language", */
-1, /* 18"Content-Length", */
-1, /* 19"Content-Type", */
-1, /* 20"CSeq", */
-1, /* 21"Date", */
-1, /* 22"Error-Info", */
-1, /* 23"Event", */
-1, /* 24"Expires", */
-1, /* 25"From", */
-1, /* 26"History-Info", RFC4244 */
-1, /* 27"Identity", */
-1, /* 28"Identity-Info", RFC4474 */
-1, /* 29"In-Reply-To", RFC3261 */
-1, /* 30"Join", RFC3911 */
-1, /* 31"Max-Breadth" RFC5393 */
-1, /* 32"Max-Forwards", */
-1, /* 33"MIME-Version", */
-1, /* 34"Min-Expires", */
-1, /* 35"Min-SE", RFC4028 */
-1, /* 36"Organization", */
-1, /* 37"P-Access-Network-Info", RFC3455 */
-1, /* 38"P-Answer-State", RFC4964 */
-1, /* 39"P-Asserted-Identity", RFC3325 */
-1, /* 40"P-Associated-URI", RFC3455 */
-1, /* 41"P-Called-Party-ID", RFC3455 */
-1, /* 42"P-Charging-Function-Addresses", RFC3455 */
-1, /* 43"P-Charging-Vector", RFC3455 */
-1, /* 44"P-DCS-Trace-Party-ID", RFC3603 */
-1, /* 45"P-DCS-OSPS", RFC3603 */
-1, /* 46"P-DCS-Billing-Info", RFC3603 */
-1, /* 47"P-DCS-LAES", RFC3603 */
-1, /* 48"P-DCS-Redirect", RFC3603 */
-1, /* 49"P-Early-Media", */
-1, /* 50"P-Media-Authorization", RFC3313 */
-1, /* 51"P-Preferred-Identity", RFC3325 */
-1, /* 52"P-Profile-Key", */
-1, /* 53"P-Refused-URI-List", RFC5318 */
-1, /* 54"P-Served-User", RFC5502 */
-1, /* 55"P-User-Database RFC4457 */
-1, /* 56"P-Visited-Network-ID", RFC3455 */
-1, /* 57"Path", RFC3327 */
-1, /* 58"Permission-Missing" RFC5360 */
-1, /* 59"Priority" */
-1, /* 60"Priv-Answer-mode" RFC5373 */
-1, /* 61"Privacy", RFC3323 */
-1, /* 62"Proxy-Authenticate", */
-1, /* 63"Proxy-Authorization", */
-1, /* 64"Proxy-Require", */
-1, /* 65"RAck", RFC3262 */
-1, /* 66"Reason", RFC3326 */
-1, /* 67"Record-Route", */
-1, /* 68"Refer-Sub",", RFC4488 */
-1, /* 69"Refer-To", RFC3515 */
-1, /* 70"Referred-By", */
-1, /* 71"Reject-Contact", RFC3841 */
-1, /* 72"Replaces", RFC3891 */
-1, /* 73"Reply-To", RFC3261 */
-1, /* 74"Request-Disposition", RFC3841 */
-1, /* 75"Require", RFC3261 */
-1, /* 76"Resource-Priority", RFC4412 */
-1, /* 77"Retry-After", RFC3261 */
-1, /* 78"Route", RFC3261 */
-1, /* 79"RSeq", RFC3262 */
-1, /* 80"Security-Client", RFC3329 */
-1, /* 81"Security-Server", RFC3329 */
-1, /* 82"Security-Verify", RFC3329 */
-1, /* 83"Server", RFC3261 */
-1, /* 84"Service-Route", RFC3608 */
-1, /* 85"Session-Expires", RFC4028 */
-1, /* 86"SIP-ETag", RFC3903 */
-1, /* 87"SIP-If-Match", RFC3903 */
-1, /* 88"Subject", RFC3261 */
-1, /* 89"Subscription-State", RFC3265 */
-1, /* 90"Supported", RFC3261 */
-1, /* 91"Target-Dialog", RFC4538 */
-1, /* 92"Timestamp", RFC3261 */
-1, /* 93"To", RFC3261 */
-1, /* 94"Trigger-Consent" RFC5380 */
-1, /* 95"Unsupported", RFC3261 */
-1, /* 96"User-Agent", RFC3261 */
-1, /* 97"Via", RFC3261 */
-1, /* 98"Warning", RFC3261 */
-1, /* 99"WWW-Authenticate", RFC3261 */
};
/* Track associations between parameter name and hf item */
typedef struct {
const char *param_name;
const gint *hf_item;
} header_parameter_t;
static header_parameter_t auth_parameters_hf_array[] =
{
{"response", &hf_sip_auth_digest_response},
{"nc", &hf_sip_auth_nc},
{"username", &hf_sip_auth_username},
{"realm", &hf_sip_auth_realm},
{"nonce", &hf_sip_auth_nonce},
{"algorithm", &hf_sip_auth_algorithm},
{"opaque", &hf_sip_auth_opaque},
{"qop", &hf_sip_auth_qop},
{"cnonce", &hf_sip_auth_cnonce},
{"uri", &hf_sip_auth_uri},
{"domain", &hf_sip_auth_domain},
{"stale", &hf_sip_auth_stale},
{"auts", &hf_sip_auth_auts},
{"rspauth", &hf_sip_auth_rspauth},
{"nextnonce", &hf_sip_auth_nextnonce},
{"ik", &hf_sip_auth_ik},
{"ck", &hf_sip_auth_ck}
};
static header_parameter_t via_parameters_hf_array[] =
{
{"branch", &hf_sip_via_branch},
{"maddr", &hf_sip_via_maddr},
{"rport", &hf_sip_via_rport},
{"received", &hf_sip_via_received},
{"ttl", &hf_sip_via_ttl},
{"comp", &hf_sip_via_comp},
{"sigcomp-id", &hf_sip_via_sigcomp_id}
};
typedef struct {
gint *hf_sip_addr;
gint *hf_sip_user;
gint *hf_sip_host;
gint *hf_sip_port;
gint *ett_uri;
} hf_sip_uri_t;
static hf_sip_uri_t sip_pai_uri = {
&hf_sip_pai_addr,
&hf_sip_pai_user,
&hf_sip_pai_host,
&hf_sip_pai_port,
&ett_sip_pai_uri
};
static hf_sip_uri_t sip_ppi_uri = {
&hf_sip_ppi_addr,
&hf_sip_ppi_user,
&hf_sip_ppi_host,
&hf_sip_ppi_port,
&ett_sip_ppi_uri
};
static hf_sip_uri_t sip_pmiss_uri = {
&hf_sip_pmiss_addr,
&hf_sip_pmiss_user,
&hf_sip_pmiss_host,
&hf_sip_pmiss_port,
&ett_sip_pmiss_uri
};
static hf_sip_uri_t sip_tc_uri = {
&hf_sip_tc_addr,
&hf_sip_tc_user,
&hf_sip_tc_host,
&hf_sip_tc_port,
&ett_sip_tc_uri
};
static hf_sip_uri_t sip_to_uri = {
&hf_sip_to_addr,
&hf_sip_to_user,
&hf_sip_to_host,
&hf_sip_to_port,
&ett_sip_to_uri
};
static hf_sip_uri_t sip_from_uri = {
&hf_sip_from_addr,
&hf_sip_from_user,
&hf_sip_from_host,
&hf_sip_from_port,
&ett_sip_from_uri
};
static hf_sip_uri_t sip_req_uri = {
&hf_sip_ruri,
&hf_sip_ruri_user,
&hf_sip_ruri_host,
&hf_sip_ruri_port,
&ett_sip_ruri
};
/*
* Type of line. It's either a SIP Request-Line, a SIP Status-Line, or
* another type of line.
*/
typedef enum {
REQUEST_LINE,
STATUS_LINE,
OTHER_LINE
} line_type_t;
/* Preferences */
static guint sip_tcp_port = TCP_PORT_SIP;
static guint sip_tls_port = TLS_PORT_SIP;
/* global_sip_raw_text determines whether we are going to display */
/* the raw text of the SIP message, much like the MEGACO dissector does. */
static gboolean global_sip_raw_text = FALSE;
/* strict_sip_version determines whether the SIP dissector enforces
* the SIP version to be "SIP/2.0". */
static gboolean strict_sip_version = TRUE;
/*
* desegmentation of SIP headers
* (when we are over TCP or another protocol providing the desegmentation API)
*/
static gboolean sip_desegment_headers = TRUE;
/*
* desegmentation of SIP bodies
* (when we are over TCP or another protocol providing the desegmentation API)
*/
static gboolean sip_desegment_body = TRUE;
/*
* same source port for retransmissions
*/
static gboolean sip_retrans_the_same_sport = TRUE;
/* Extension header subdissectors */
static dissector_table_t ext_hdr_subdissector_table;
/* Forward declaration we need below */
void proto_reg_handoff_sip(void);
static gboolean dissect_sip_common(tvbuff_t *tvb, int offset, packet_info *pinfo,
proto_tree *tree, gboolean is_heur, gboolean use_reassembly);
static line_type_t sip_parse_line(tvbuff_t *tvb, int offset, gint linelen,
guint *token_1_len);
static gboolean sip_is_known_request(tvbuff_t *tvb, int meth_offset,
guint meth_len, guint *meth_idx);
static gint sip_is_known_sip_header(tvbuff_t *tvb, int offset,
guint header_len);
static void dfilter_sip_request_line(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, gint offset,
guint meth_len, gint linelen);
static void dfilter_sip_status_line(tvbuff_t *tvb, proto_tree *tree);
static void tvb_raw_text_add(tvbuff_t *tvb, int offset, int length, proto_tree *tree);
static guint sip_is_packet_resend(packet_info *pinfo,
gchar* cseq_method,
gchar* call_id,
guchar cseq_number_set, guint32 cseq_number,
line_type_t line_type);
static guint sip_find_request(packet_info *pinfo,
gchar* cseq_method,
gchar* call_id,
guchar cseq_number_set, guint32 cseq_number,
guint32 *response_time);
static guint sip_find_invite(packet_info *pinfo,
gchar* cseq_method,
gchar* call_id,
guchar cseq_number_set, guint32 cseq_number,
guint32 *response_time);
/* SIP content type and internet media type used by other dissectors
* are the same. List of media types from IANA at:
* http://www.iana.org/assignments/media-types/index.html */
static dissector_table_t media_type_dissector_table;
static heur_dissector_list_t heur_subdissector_list;
#define SIP2_HDR "SIP/2.0"
#define SIP2_HDR_LEN ((gint)strlen (SIP2_HDR))
/* Store the info needed by the SIP tap for one packet */
static sip_info_value_t *stat_info;
/* The buffer size for the cseq_method name */
#define MAX_CSEQ_METHOD_SIZE 16
/****************************************************************************
* Conversation-type definitions
*
* For each call, keep track of the current cseq number and state of
* transaction, in order to be able to detect retransmissions.
*
* Don't use the conservation mechanism, but instead:
* - store with each dissected packet original frame (if any)
* - maintain a global hash table of
* (call_id, source_addr, dest_addr) -> (cseq, transaction_state, frame)
*
* N.B. This is broken for a couple of reasons:
* - it won't cope properly with overlapping transactions within the
* same dialog
* - request response mapping won't work where the response uses a different
* address pair from the request
*
* TODO: proper transaction matching uses RFC fields (use Max-forwards or
* maybe Via count as extra key to limit view to one hop)
****************************************************************************/
static GHashTable *sip_hash = NULL; /* Hash table */
static GHashTable *sip_headers_hash = NULL; /* Hash table */
/* Types for hash table keys and values */
#define MAX_CALL_ID_SIZE 128
#define MAGIC_SOURCE_PORT 0
/* Conversation-type key */
typedef struct
{
char call_id[MAX_CALL_ID_SIZE];
address source_address;
guint32 source_port;
address dest_address;
guint32 dest_port;
} sip_hash_key;
typedef enum
{
nothing_seen,
request_seen,
provisional_response_seen,
final_response_seen
} transaction_state_t;
/* Current conversation-type value */
typedef struct
{
guint32 cseq;
transaction_state_t transaction_state;
gchar method[MAX_CSEQ_METHOD_SIZE];
nstime_t request_time;
guint32 response_code;
gint frame_number;
} sip_hash_value;
/* Result to be stored in per-packet info */
typedef struct
{
gint original_frame_num;
gint response_request_frame_num;
gint response_time;
} sip_frame_result_value;
/************************/
/* Hash table functions */
/* Equal keys */
static gint sip_equal(gconstpointer v, gconstpointer v2)
{
const sip_hash_key* val1 = v;
const sip_hash_key* val2 = v2;
/* Call id must match */
if (strcmp(val1->call_id, val2->call_id) != 0)
{
return 0;
}
/* Addresses must match */
return (ADDRESSES_EQUAL(&(val1->source_address), &(val2->source_address))) &&
(val1->source_port == val2->source_port) &&
(ADDRESSES_EQUAL(&(val1->dest_address), &(val2->dest_address))) &&
(val1->dest_port == val2->dest_port);
}
#if 0
/*
20010-03-03 Changed to use g_str_hash ()
This function seem to not generate unique enough keys
if load generation tools are used with many thousands users.
Leave the code for a while to se if someone gets poor performance.
*/
/* Compute a hash value for a given key. */
/* Don't try to use addresses here, call-id should be almost unique. */
static guint sip_hash_func(gconstpointer v)
{
gint n;
const sip_hash_key *key = v;
guint value = (guint)strlen(key->call_id);
gint chars_to_use = value / 4;
/* First few characters from the call-id should be enough... */
for (n=0; n < chars_to_use; n++)
{
value += key->call_id[n];
}
return value;
}
#endif
/* Initializes the hash table and the mem_chunk area each time a new
* file is loaded or re-loaded in wireshark */
static void
sip_init_protocol(void)
{
guint i;
gchar *value_copy;
/* Destroy any existing hashes. */
if (sip_hash)
g_hash_table_destroy(sip_hash);
/* Now create them over */
sip_hash = g_hash_table_new(g_str_hash , sip_equal);
/* Create a hastable with the SIP headers it will be used to find the related hf entry (POS_x)
* this is faster than the previously used for loop
* There is no g_hash_table_destroy as the liftime is the same as the lifetime of Wireshark
*/
if(!sip_headers_hash){
sip_headers_hash = g_hash_table_new(g_str_hash , g_str_equal);
for (i = 1; i < array_length(sip_headers); i++){
value_copy = g_strdup (sip_headers[i].name);
g_hash_table_insert(sip_headers_hash, (gpointer)value_copy, GINT_TO_POINTER(i));
}
}
}
/* Structure to collect info about a sip uri */
typedef struct _uri_offset_info
{
gint display_name_start;
gint display_name_end;
gint uri_start;
gint uri_end;
gint uri_parameters_start;
gint uri_parameters_end;
gint name_addr_start;
gint name_addr_end;
gint uri_user_start;
gint uri_user_end;
gint uri_host_start;
gint uri_host_end;
gint uri_host_port_start;
gint uri_host_port_end;
} uri_offset_info;
static void
sip_uri_offset_init(uri_offset_info *uri_offsets){
/* Initialize the uri_offsets */
uri_offsets->display_name_start = -1;
uri_offsets->display_name_end = -1;
uri_offsets->uri_start = -1;
uri_offsets->uri_end = -1;
uri_offsets->uri_parameters_start = -1;
uri_offsets->uri_parameters_end = -1;
uri_offsets->name_addr_start = -1;
uri_offsets->name_addr_end = -1;
uri_offsets->uri_user_start = -1;
uri_offsets->uri_user_end = -1;
uri_offsets->uri_host_start = -1;
uri_offsets->uri_host_end = -1;
uri_offsets->uri_host_port_start = -1;
uri_offsets->uri_host_port_end = -1;
}
/* Code to parse a sip uri.
* Returns Offset end off parsing or -1 for unsuccessful parsing
* - sip_uri_offset_init() must have been called first.
*/
static gint
dissect_sip_uri(tvbuff_t *tvb, packet_info *pinfo _U_, gint start_offset,
gint line_end_offset, uri_offset_info *uri_offsets)
{
gchar c = '\0';
gint current_offset;
gint queried_offset;
gint comma_offset;
gint semicolon_offset;
gint parameter_end_offset;
gboolean in_ipv6 = FALSE;
/* skip Spaces and Tabs */
current_offset = tvb_skip_wsp(tvb, start_offset, line_end_offset - start_offset);
if(current_offset >= line_end_offset) {
/* Nothing to parse */
return -1;
}
/* Set uri start offset in case this was caled directly */
uri_offsets->uri_start = current_offset;
/* Check if it's realy a sip uri ( it might be a tel uri, parse that?) */
queried_offset = tvb_find_guint8(tvb, current_offset, line_end_offset - current_offset, ':');
if (tvb_strneql(tvb, current_offset, "sip", 3) != 0)
return -1;
if(uri_offsets->uri_end == -1)
{
/* name-addr form was NOT used e.g no closing ">" */
/* look for the first ',' or ';' which will mark the end of this URI
* In this case a semicolon indicates a header field parameter, and not an uri parameter.
*/
comma_offset = tvb_find_guint8(tvb, current_offset, line_end_offset - current_offset, ',');
semicolon_offset = tvb_find_guint8(tvb, current_offset, line_end_offset - current_offset, ';');
if (semicolon_offset != -1 && comma_offset != -1)
{
if(semicolon_offset < comma_offset)
{
uri_offsets->uri_end = semicolon_offset - 1;
}
else
{
uri_offsets->uri_end = comma_offset - 1;
}
}
else
{
if (semicolon_offset != -1)
{
uri_offsets->uri_end = semicolon_offset - 1;
}
else if (comma_offset != -1)
{
uri_offsets->uri_end = comma_offset - 1;
} else {
/* If both offsets are equal to -1, we don't have a semicolon or a comma.
* In that case, we assume that the end of the URI is at the line end
*/
uri_offsets->uri_end = line_end_offset - 3; /* remove '\r\n' */
}
}
uri_offsets->name_addr_end = uri_offsets->uri_end;
}
/* Look for URI address parts (user, host, host-port) */
/* Look for '@' within URI */
queried_offset = tvb_find_guint8(tvb, uri_offsets->uri_start, uri_offsets->uri_end - uri_offsets->uri_start, '@');
if(queried_offset == -1)
{
/* no '@' = no user part */
uri_offsets->uri_host_start = tvb_find_guint8(tvb, uri_offsets->uri_start, uri_offsets->uri_end - uri_offsets->uri_start, ':')+1;
}
else
{
/* with '@' = with user part */
uri_offsets->uri_user_start = tvb_find_guint8(tvb, uri_offsets->uri_start, uri_offsets->uri_end - uri_offsets->uri_start, ':')+1;
uri_offsets->uri_user_end = tvb_find_guint8(tvb, uri_offsets->uri_user_start, uri_offsets->uri_end - uri_offsets->uri_start, '@')-1;
uri_offsets->uri_host_start = uri_offsets->uri_user_end + 2;
}
/* find URI-Host end*/
parameter_end_offset = uri_offsets->uri_host_start;
in_ipv6 = (tvb_get_guint8(tvb, parameter_end_offset) == '[');
while (parameter_end_offset < line_end_offset)
{
parameter_end_offset++;
c = tvb_get_guint8(tvb, parameter_end_offset);
switch (c) {
case '>':
case ',':
case ';':
case '?':
case ' ':
case '\r':
goto uri_host_end_found;
case ':':
if (!in_ipv6)
goto uri_host_end_found;
break;
case '[':
in_ipv6 = TRUE;
break;
case ']':
in_ipv6 = FALSE;
break;
default :
break;
}
}
uri_host_end_found:
uri_offsets->uri_host_end = parameter_end_offset - 1;
if (c == ':')
{
uri_offsets->uri_host_port_start = parameter_end_offset + 1;
parameter_end_offset = uri_offsets->uri_host_port_start;
while (parameter_end_offset < line_end_offset)
{
parameter_end_offset++;
c = tvb_get_guint8(tvb, parameter_end_offset);
switch (c) {
case '>':
case ',':
case ';':
case '?':
case ' ':
case '\r':
goto uri_host_port_end_found;
default :
break;
}
}
uri_host_port_end_found:
uri_offsets->uri_host_port_end = parameter_end_offset -1;
}
return uri_offsets->name_addr_end;
}
/*
* token = 1*(alphanum / "-" / "." / "!" / "%" / "*"
* / "_" / "+" / "`" / "'" / "~" )
* LWS = [*WSP CRLF] 1*WSP ; linear whitespace
* name-addr = [ display-name ] LAQUOT addr-spec RAQUOT
* addr-spec = SIP-URI / SIPS-URI / absoluteURI
* display-name = *(token LWS)/ quoted-string
*/
static gint
dissect_sip_name_addr_or_addr_spec(tvbuff_t *tvb, packet_info *pinfo _U_, gint start_offset,
gint line_end_offset, uri_offset_info *uri_offsets)
{
gchar c;
gint i;
gint current_offset;
gint queried_offset;
gint colon_offset;
gboolean uri_without_angle_quotes = FALSE;
/* skip Spaces and Tabs */
current_offset = tvb_skip_wsp(tvb, start_offset, line_end_offset - start_offset);
if(current_offset >= line_end_offset) {
/* Nothing to parse */
return -1;
}
uri_offsets->name_addr_start = current_offset;
/* First look, if we have a display name */
c=tvb_get_guint8(tvb, current_offset);
switch(c)
{
case '"':
/* We have a display name, look for the next unescaped '"' */
uri_offsets->display_name_start = current_offset;
do
{
queried_offset = tvb_find_guint8(tvb, current_offset + 1, line_end_offset - (current_offset + 1), '"');
if(queried_offset == -1)
{
/* malformed URI */
return -1;
}
current_offset = queried_offset;
/* Is it escaped? */
/* count back slashes before '"' */
for(i=1;tvb_get_guint8(tvb, queried_offset - i) == '\\';i++);
i--;
if(i % 2 == 0)
{
/* not escaped */
break;
}
} while (current_offset < line_end_offset);
if(current_offset >= line_end_offset)
{
/* malformed URI */
return -1;
}
uri_offsets->display_name_end = current_offset;
/* find start of the URI */
queried_offset = tvb_find_guint8(tvb, current_offset, line_end_offset - current_offset, '<');
if(queried_offset == -1)
{
/* malformed Uri */
return -1;
}
current_offset = queried_offset + 1;
break;
case '<':
/* We don't have a display name */
current_offset++;
break;
default:
/* We have either an URI without angles or a display name with a limited character set */
/* Look for the right angle quote or colon */
queried_offset = tvb_find_guint8(tvb, current_offset, line_end_offset - current_offset, '<');
colon_offset = tvb_find_guint8(tvb, current_offset, line_end_offset - current_offset, ':');
if(queried_offset != -1 && colon_offset != -1)
{
if(queried_offset < colon_offset)
{
/* we have an URI with angle quotes */
uri_offsets->display_name_start = current_offset;
uri_offsets->display_name_end = queried_offset - 1;
current_offset = queried_offset + 1;
}
else
{
/* we have an URI without angle quotes */
uri_without_angle_quotes = TRUE;
}
}
else
{
if(queried_offset != -1)
{
/* we have an URI with angle quotes */
uri_offsets->display_name_start = current_offset;
uri_offsets->display_name_end = queried_offset - 1;
current_offset = queried_offset + 1;
break;
}
if(colon_offset != -1)
{
/* we have an URI without angle quotes */
uri_without_angle_quotes = TRUE;
break;
}
/* If this point is reached, we can't parse the URI */
return -1;
}
break;
}
/* Start of URI */
uri_offsets->uri_start = current_offset;
if(uri_without_angle_quotes==FALSE){
/* name-addr form was used */
/* look for closing angle quote */
queried_offset = tvb_find_guint8(tvb, current_offset, line_end_offset - current_offset, '>');
if(queried_offset == -1)
{
/* malformed Uri */
return -1;
}
uri_offsets->name_addr_end = queried_offset;
uri_offsets->uri_end = queried_offset - 1;
}
return dissect_sip_uri(tvb, pinfo, current_offset, line_end_offset, uri_offsets);
}
/*
* Code to add dissected SIP URI Information to proto tree
*/
static proto_tree *
display_sip_uri (tvbuff_t *tvb, proto_tree *sip_element_tree, uri_offset_info* uri_offsets, hf_sip_uri_t* uri)
{
proto_item *ti;
proto_tree *uri_item_tree = NULL;
if(uri_offsets->display_name_end != uri_offsets->display_name_start) {
proto_tree_add_item(sip_element_tree, hf_sip_display, tvb, uri_offsets->display_name_start,
uri_offsets->display_name_end - uri_offsets->display_name_start + 1, FALSE);
}
ti = proto_tree_add_item(sip_element_tree, *(uri->hf_sip_addr), tvb, uri_offsets->uri_start, uri_offsets->uri_end - uri_offsets->uri_start + 1, FALSE);
uri_item_tree = proto_item_add_subtree(ti, *(uri->ett_uri));
if(uri_offsets->uri_user_end > uri_offsets->uri_user_start) {
proto_tree_add_item(uri_item_tree, *(uri->hf_sip_user), tvb, uri_offsets->uri_user_start,
uri_offsets->uri_user_end - uri_offsets->uri_user_start + 1, FALSE);
}
proto_tree_add_item(uri_item_tree, *(uri->hf_sip_host), tvb, uri_offsets->uri_host_start,
uri_offsets->uri_host_end - uri_offsets->uri_host_start + 1, FALSE);
if(uri_offsets->uri_host_port_end > uri_offsets->uri_host_port_start) {
proto_tree_add_item(uri_item_tree, *(uri->hf_sip_port), tvb, uri_offsets->uri_host_port_start,
uri_offsets->uri_host_port_end - uri_offsets->uri_host_port_start + 1, FALSE);
}
return uri_item_tree;
}
/* Code to parse a contact header item
* Returns Offset end off parsing or -1 for unsuccessful parsing
*/
static gint
dissect_sip_contact_item(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, gint start_offset, gint line_end_offset)
{
gchar c;
gint i;
proto_item *ti = NULL;
proto_tree *contact_item_tree = NULL, *uri_tree = NULL;
gint current_offset;
gint queried_offset;
gint contact_params_start_offset = -1;
gint contact_item_end_offset = -1;
uri_offset_info uri_offsets;
/* skip Spaces and Tabs */
start_offset = tvb_skip_wsp(tvb, start_offset, line_end_offset - start_offset);
if(start_offset >= line_end_offset) {
/* Nothing to parse */
return -1;
}
/* Initialize the uri_offsets */
sip_uri_offset_init(&uri_offsets);
/* contact-param = (name-addr / addr-spec) *(SEMI contact-params) */
current_offset = dissect_sip_name_addr_or_addr_spec(tvb, pinfo, start_offset, line_end_offset, &uri_offsets);
if(current_offset == -1)
{
/* Parsing failed */
return -1;
}
/* Now look for the end of the contact item */
while (current_offset < line_end_offset)
{
c=tvb_get_guint8(tvb, current_offset);
if(c == ';' && contact_params_start_offset == -1)
{
/* here we start with contact parameters */
contact_params_start_offset = current_offset;
}
if(c == '"')
{
/* look for the next unescaped '"' */
do
{
queried_offset = tvb_find_guint8(tvb, current_offset + 1, line_end_offset - (current_offset + 1), '"');
if(queried_offset == -1)
{
/* malformed Contact header */
return -1;
}
current_offset = queried_offset;
/* Is it escaped?
* Look for uneven number of backslashes before '"' */
for(i=0;tvb_get_guint8(tvb, queried_offset - (i+1) ) == '\\';i++);
i=i%2;
} while (i == 1);
}
if(c == ',')
{
/* end of contact item found. */
contact_item_end_offset = current_offset - 1; /* remove ',' */
break;
}
current_offset++;
}
if(contact_item_end_offset == -1)
contact_item_end_offset = line_end_offset - 3; /* remove '\r\n' */
/* Build the tree, now */
if(tree)
{
ti = proto_tree_add_string(tree, hf_sip_contact_item, tvb, start_offset, contact_item_end_offset - start_offset + 1,
tvb_format_text(tvb, start_offset, contact_item_end_offset - start_offset + 1));
contact_item_tree = proto_item_add_subtree(ti, ett_sip_contact_item);
ti = proto_tree_add_string(contact_item_tree, hf_sip_uri, tvb, uri_offsets.name_addr_start, uri_offsets.name_addr_end - uri_offsets.name_addr_start + 1,
tvb_format_text(tvb, uri_offsets.name_addr_start, uri_offsets.name_addr_end - uri_offsets.name_addr_start + 1));
uri_tree = proto_item_add_subtree(ti, ett_sip_uri);
if(uri_offsets.display_name_start != -1 && uri_offsets.display_name_end != -1)
{
proto_tree_add_string(uri_tree, hf_sip_display, tvb, uri_offsets.display_name_start,
uri_offsets.display_name_end - uri_offsets.display_name_start + 1,
tvb_format_text(tvb, uri_offsets.display_name_start,
uri_offsets.display_name_end - uri_offsets.display_name_start + 1));
}
if(uri_offsets.uri_start != -1 && uri_offsets.uri_end != -1)
{
proto_tree_add_string(uri_tree, hf_sip_contact_addr, tvb, uri_offsets.uri_start,
uri_offsets.uri_end - uri_offsets.uri_start + 1,
tvb_format_text(tvb, uri_offsets.uri_start,
uri_offsets.uri_end - uri_offsets.uri_start + 1));
}
/* Parse URI and Contact header Parameters now */
/* TODO */
}
return current_offset;
}
/* Code to parse an authorization header item
* Returns offset at end of parsing, or -1 for unsuccessful parsing
*/
static gint
dissect_sip_authorization_item(tvbuff_t *tvb, proto_tree *tree, gint start_offset, gint line_end_offset)
{
gchar c;
gint current_offset;
gint equals_offset = 0;
gchar *name;
header_parameter_t *auth_parameter;
guint i = 0;
/* skip Spaces and Tabs */
start_offset = tvb_skip_wsp(tvb, start_offset, line_end_offset - start_offset);
if (start_offset >= line_end_offset)
{
/* Nothing to parse */
return -1;
}
current_offset = start_offset;
/* Now look for the end of the parameter */
while (current_offset < line_end_offset)
{
c = tvb_get_guint8(tvb, current_offset);
if (c == '=')
{
equals_offset = current_offset;
}
if(c == '"')
{
/* look for the next unescaped '"' */
do
{
current_offset = tvb_find_guint8(tvb, current_offset + 1, line_end_offset - (current_offset + 1), '"');
if(current_offset == -1)
{
/* malformed parameter */
return -1;
}
/* Is it escaped?
* Look for uneven number of backslashes before '"' */
for(i=0;tvb_get_guint8(tvb, current_offset - (i+1) ) == '\\';i++);
i=i%2;
} while (i == 1);
current_offset++;
current_offset = tvb_skip_wsp(tvb, current_offset, line_end_offset - current_offset);
continue;
}
if (c == ',')
{
break;
}
current_offset++;
}
if (equals_offset == 0)
{
/* Give up if equals not found */
return -1;
}
/* Extract the parameter name */
name = tvb_get_ephemeral_string(tvb, start_offset, equals_offset-start_offset);
/* Try to add parameter as a filterable item */
for (auth_parameter = &auth_parameters_hf_array[i];
i < array_length(auth_parameters_hf_array);
i++, auth_parameter++)
{
if (g_ascii_strcasecmp(name, auth_parameter->param_name) == 0)
{
proto_tree_add_item(tree, *(auth_parameter->hf_item), tvb,
equals_offset+1, current_offset-equals_offset-1,
FALSE);
break;
}
}
/* If not matched, just add as text... */
if (i == array_length(auth_parameters_hf_array))
{
proto_tree_add_text(tree, tvb, start_offset, current_offset-start_offset,
"%s", tvb_format_text(tvb, start_offset,
current_offset-start_offset));
}
return current_offset;
}
/* Dissect the details of a Reason header */
static void
dissect_sip_reason_header(tvbuff_t *tvb, proto_tree *tree, gint start_offset, gint line_end_offset){
gint current_offset, semi_colon_offset, length;
gchar *param_name = NULL;
guint cause_value;
/* skip Spaces and Tabs */
start_offset = tvb_skip_wsp(tvb, start_offset, line_end_offset - start_offset);
if (start_offset >= line_end_offset)
{
/* Nothing to parse */
return;
}
current_offset = start_offset;
semi_colon_offset = tvb_find_guint8(tvb, current_offset, line_end_offset-current_offset, ';');
length = semi_colon_offset - current_offset;
proto_tree_add_text(tree, tvb, start_offset, length,
"Reason Protocols: %s", tvb_format_text(tvb, start_offset, length));
param_name = tvb_get_ephemeral_string(tvb, start_offset, length);
if (g_ascii_strcasecmp(param_name, "Q.850") == 0){
current_offset = tvb_find_guint8(tvb, semi_colon_offset, line_end_offset-semi_colon_offset, '=')+1;
length = line_end_offset - current_offset;
/* q850_cause_code_vals */
cause_value = atoi(tvb_get_ephemeral_string(tvb, current_offset, length));
proto_tree_add_text(tree, tvb, current_offset, length,
"Cause: %u(0x%x)[%s]", cause_value,cause_value,
val_to_str(cause_value, q850_cause_code_vals, "Unknown (%d)" ));
}
}
/* Dissect the details of a Via header */
static void dissect_sip_via_header(tvbuff_t *tvb, proto_tree *tree, gint start_offset, gint line_end_offset)
{
gint current_offset;
gint transport_start_offset;
gint address_start_offset;
gint semicolon_offset;
guint transport_slash_count;
gboolean transport_name_started;
gboolean colon_seen;
gboolean ipv6_reference;
gboolean ipv6_address;
guchar c;
gchar *param_name = NULL;
current_offset = start_offset;
while (1)
{
/* Reset flags and counters */
transport_start_offset = 0;
semicolon_offset = 0;
transport_name_started = FALSE;
transport_slash_count = 0;
ipv6_reference = FALSE;
ipv6_address = FALSE;
colon_seen = FALSE;
/* skip Spaces and Tabs */
current_offset = tvb_skip_wsp(tvb, current_offset, line_end_offset - current_offset);
if (current_offset >= line_end_offset)
{
/* Nothing more to parse */
return;
}
/* Now look for the end of the SIP/2.0/transport parameter.
There may be spaces between the slashes */
while (current_offset < line_end_offset)
{
c = tvb_get_guint8(tvb, current_offset);
if (c == '/')
{
transport_slash_count++;
}
else
if (!transport_name_started && (transport_slash_count == 2) && isalpha(c))
{
transport_name_started = TRUE;
transport_start_offset = current_offset;
}
else
if (transport_name_started && ((c == ' ') || (c == '\t')))
{
proto_tree_add_item(tree, hf_sip_via_transport, tvb, transport_start_offset,
current_offset - transport_start_offset, FALSE);
break;
}
current_offset++;
}
/* skip Spaces and Tabs */
current_offset = tvb_skip_wsp(tvb, current_offset, line_end_offset - current_offset);
/* Now read the address part */
address_start_offset = current_offset;
while (current_offset < line_end_offset)
{
c = tvb_get_guint8(tvb, current_offset);
if (c == '[') {
ipv6_reference = TRUE;
ipv6_address = TRUE;
}
else if (c == ']')
{
ipv6_reference = FALSE;
}
if (colon_seen || (c == ' ') || (c == '\t') || ((c == ':') && (ipv6_reference == FALSE)) || (c == ';'))
{
break;
}
current_offset++;
}
/* Add address to tree */
if (ipv6_address == TRUE) {
proto_tree_add_item(tree, hf_sip_via_sent_by_address, tvb, address_start_offset + 1,
current_offset - address_start_offset - 2, FALSE);
} else {
proto_tree_add_item(tree, hf_sip_via_sent_by_address, tvb, address_start_offset,
current_offset - address_start_offset, FALSE);
}
/* Transport port number may follow ([space] : [space])*/
current_offset = tvb_skip_wsp(tvb, current_offset, line_end_offset - current_offset);
c = tvb_get_guint8(tvb, current_offset);
if (c == ':')
{
/* Port number will follow any space after : */
gint port_offset;
colon_seen = TRUE;
current_offset++;
/* Skip optional space after colon */
current_offset = tvb_skip_wsp(tvb, current_offset, line_end_offset - current_offset);
port_offset = current_offset;
/* Find digits of port number */
while (current_offset < line_end_offset)
{
c = tvb_get_guint8(tvb, current_offset);
if (!isdigit(c))
{
if (current_offset > port_offset)
{
/* Add address port number to tree */
proto_tree_add_uint(tree, hf_sip_via_sent_by_port, tvb, port_offset,
current_offset - port_offset,
atoi(tvb_get_ephemeral_string(tvb, port_offset,
current_offset - port_offset)));
}
else
{
/* Shouldn't see a colon without a port number given */
return;
}
break;
}
current_offset++;
}
}
/* skip Spaces and Tabs */
current_offset = tvb_skip_wsp(tvb, current_offset, line_end_offset - current_offset);
/* Dissect any parameters found */
while (current_offset < line_end_offset)
{
gboolean equals_found = FALSE;
gboolean found_end_of_parameters = FALSE;
gint parameter_name_end = 0;
gint parameter_value_end;
header_parameter_t *via_parameter;
guint i = 0;
/* Look for the semicolon that signals the start of a parameter */
while (current_offset < line_end_offset)
{
c = tvb_get_guint8(tvb, current_offset);
if (c == ';')
{
semicolon_offset = current_offset;
current_offset++;
break;
}
else
if ((c != ' ') && (c != '\t'))
{
found_end_of_parameters = TRUE;
break;
}
current_offset++;
}
if (found_end_of_parameters)
{
break;
}
if (current_offset == line_end_offset)
{
return;
}
/* Look for end of parameter name */
while (current_offset < line_end_offset)
{
c = tvb_get_guint8(tvb, current_offset);
if (!isalpha(c) && (c != '-'))
{
break;
}
current_offset++;
}
/* Not all params have an = */
if (c == '=')
{
equals_found = TRUE;
}
parameter_name_end = current_offset;
/* Read until end of parameter value */
while (current_offset < line_end_offset)
{
c = tvb_get_guint8(tvb, current_offset);
if ((c == ' ') || (c == '\t') || (c == ';') || (c == ','))
{
break;
}
current_offset++;
}
/* Note parameter name */
parameter_value_end = current_offset;
param_name = tvb_get_ephemeral_string(tvb, semicolon_offset+1,
parameter_name_end - semicolon_offset - 1);
/* Try to add parameter as a filterable item */
for (via_parameter = &via_parameters_hf_array[i];
i < array_length(via_parameters_hf_array);
i++, via_parameter++)
{
if (g_ascii_strcasecmp(param_name, via_parameter->param_name) == 0)
{
if (equals_found)
{
proto_tree_add_item(tree, *(via_parameter->hf_item), tvb,
parameter_name_end+1, current_offset-parameter_name_end-1,
FALSE);
}
else
{
proto_tree_add_item(tree, *(via_parameter->hf_item), tvb,
semicolon_offset+1, current_offset-semicolon_offset-1,
FALSE);
}
break;
}
}
/* If not matched, just add as text... */
if (i == array_length(via_parameters_hf_array))
{
proto_tree_add_text(tree, tvb, semicolon_offset+1, current_offset-semicolon_offset-1,
"%s", tvb_format_text(tvb, semicolon_offset+1,
current_offset-semicolon_offset-1));
}
/* skip Spaces and Tabs */
current_offset = tvb_skip_wsp(tvb, current_offset, line_end_offset - current_offset);
/* There may be a comma, followed by more Via entries... */
if (current_offset < line_end_offset)
{
c = tvb_get_guint8(tvb, current_offset);
if (c == ',')
{
/* Skip it and get out of parameter loop */
current_offset++;
break;
}
}
}
}
}
/* Code to actually dissect the packets */
static int
dissect_sip(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
guint8 octet;
int len;
octet = tvb_get_guint8(tvb,0);
if ((octet & 0xf8) == 0xf8){
call_dissector(sigcomp_handle, tvb, pinfo, tree);
return tvb_length(tvb);
}
len = dissect_sip_common(tvb, 0, pinfo, tree, FALSE, FALSE);
if (len < 0)
return 0; /* not SIP */
else
return len;
}
static void
dissect_sip_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
guint8 octet;
int offset = 0;
int len;
octet = tvb_get_guint8(tvb,0);
if ((octet & 0xf8) == 0xf8){
call_dissector(sigcomp_handle, tvb, pinfo, tree);
return;
}
while (tvb_reported_length_remaining(tvb, offset) != 0) {
len = dissect_sip_common(tvb, offset, pinfo, tree, TRUE, TRUE);
if (len <= 0)
break;
offset += len;
}
}
static gboolean
dissect_sip_tcp_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
int offset = 0;
int len;
gboolean first = TRUE;
while (tvb_reported_length_remaining(tvb, offset) != 0) {
len = dissect_sip_common(tvb, offset, pinfo, tree, !first, TRUE);
if (len == -2) {
if (first) {
/*
* If the first packet doesn't start with
* a valid SIP request or response, don't
* treat this as SIP.
*/
return FALSE;
}
break;
}
if (len == -1)
break; /* need more data */
offset += len;
}
return TRUE;
}
static gboolean
dissect_sip_heur(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
return dissect_sip_common(tvb, 0, pinfo, tree, FALSE, FALSE) > 0;
}
static int
dissect_sip_common(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree,
gboolean dissect_other_as_continuation, gboolean use_reassembly)
{
int orig_offset;
gint next_offset, linelen;
int content_length, datalen, reported_datalen;
line_type_t line_type;
tvbuff_t *next_tvb;
gboolean is_known_request;
gboolean found_match = FALSE;
const char *descr;
guint token_1_len = 0;
guint current_method_idx = SIP_METHOD_INVALID;
proto_item *ts = NULL, *ti_a = NULL, *th = NULL, *sip_element_item = NULL;
proto_tree *sip_tree = NULL, *reqresp_tree = NULL , *hdr_tree = NULL,
*sip_element_tree = NULL, *message_body_tree = NULL, *cseq_tree = NULL,
*via_tree = NULL, *reason_tree = NULL, *rack_tree = NULL;
guchar contacts = 0, contact_is_star = 0, expires_is_0 = 0;
guint32 cseq_number = 0;
guchar cseq_number_set = 0;
char cseq_method[MAX_CSEQ_METHOD_SIZE] = "";
char call_id[MAX_CALL_ID_SIZE] = "";
gchar *media_type_str_lower_case = NULL;
char *content_type_parameter_str = NULL;
guint resend_for_packet = 0;
guint request_for_response = 0;
guint32 response_time = 0;
int strlen_to_copy;
/*
* 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()" calls below won't throw exceptions.
*
* Note that "tvb_strneql()" doesn't throw exceptions, so
* "sip_parse_line()" won't throw an exception.
*/
orig_offset = offset;
linelen = tvb_find_line_end(tvb, offset, -1, &next_offset, FALSE);
if (tvb_strnlen(tvb, offset, linelen) > -1)
{
/*
* There's a NULL in the line,
* this may be SIP within another protocol.
* This heuristic still needs to improve.
*/
return -2;
}
line_type = sip_parse_line(tvb, offset, linelen, &token_1_len);
if (line_type == OTHER_LINE) {
/*
* This is neither a SIP request nor response.
*/
if (!dissect_other_as_continuation) {
/*
* We were asked to reject this.
*/
return -2;
}
/*
* Just dissect it as a continuation.
*/
} else if (use_reassembly) {
/*
* 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, offset, pinfo,
sip_desegment_headers, sip_desegment_body)) {
/*
* More data needed for desegmentation.
*/
return -1;
}
}
/* Initialise stat info for passing to tap */
stat_info = ep_alloc0(sizeof(sip_info_value_t));
col_set_str(pinfo->cinfo, COL_PROTOCOL, "SIP");
switch (line_type) {
case REQUEST_LINE:
is_known_request = sip_is_known_request(tvb, offset, token_1_len, &current_method_idx);
descr = is_known_request ? "Request" : "Unknown request";
col_add_fstr(pinfo->cinfo, COL_INFO, "%s: %s",
descr,
tvb_format_text(tvb, offset, linelen - SIP2_HDR_LEN - 1));
break;
case STATUS_LINE:
descr = "Status";
col_add_fstr(pinfo->cinfo, COL_INFO, "Status: %s",
tvb_format_text(tvb, offset + SIP2_HDR_LEN + 1, linelen - SIP2_HDR_LEN - 1));
stat_info->reason_phrase = tvb_get_ephemeral_string(tvb, offset + SIP2_HDR_LEN + 5, linelen - (SIP2_HDR_LEN + 5));
break;
case OTHER_LINE:
default: /* Squelch compiler complaints */
descr = "Continuation";
col_set_str(pinfo->cinfo, COL_INFO, "Continuation");
break;
}
if (tree) {
ts = proto_tree_add_item(tree, proto_sip, tvb, offset, -1, FALSE);
sip_tree = proto_item_add_subtree(ts, ett_sip);
}
switch (line_type) {
case REQUEST_LINE:
if (sip_tree) {
ti_a = proto_tree_add_string(sip_tree, hf_Request_Line, tvb, offset, next_offset-offset,
tvb_format_text(tvb, offset, linelen));
reqresp_tree = proto_item_add_subtree(ti_a, ett_sip_reqresp);
}
dfilter_sip_request_line(tvb, reqresp_tree, pinfo, offset, token_1_len, linelen);
break;
case STATUS_LINE:
if (sip_tree) {
ti_a = proto_tree_add_string(sip_tree, hf_Status_Line, tvb, offset, next_offset-offset,
tvb_format_text(tvb, offset, linelen));
reqresp_tree = proto_item_add_subtree(ti_a, ett_sip_reqresp);
}
dfilter_sip_status_line(tvb, reqresp_tree);
break;
case OTHER_LINE:
if (sip_tree) {
ti_a = proto_tree_add_text(sip_tree, tvb, offset, next_offset,
"%s line: %s", descr,
tvb_format_text(tvb, offset, linelen));
reqresp_tree = proto_item_add_subtree(ti_a, ett_sip_reqresp);
proto_tree_add_text(sip_tree, tvb, offset, -1,
"Continuation data");
}
return tvb_length_remaining(tvb, offset);
}
offset = next_offset;
if (sip_tree) {
th = proto_tree_add_item(sip_tree, hf_msg_hdr, tvb, offset,
tvb_length_remaining(tvb, offset), FALSE);
proto_item_set_text(th, "Message Header");
hdr_tree = proto_item_add_subtree(th, ett_sip_hdr);
}
/*
* Process the headers - if we're not building a protocol tree,
* we just do this to find the blank line separating the
* headers from the message body.
*/
next_offset = offset;
content_length = -1;
while (tvb_reported_length_remaining(tvb, offset) > 0) {
gint line_end_offset;
gint colon_offset;
gint semi_colon_offset;
gint parameter_offset;
gint parameter_end_offset;
gint parameter_len;
gint content_type_len, content_type_parameter_str_len;
gint header_len;
gchar *header_name;
dissector_handle_t ext_hdr_handle;
gint hf_index;
gint value_offset;
gint sub_value_offset;
gint comma_offset;
guchar c;
gint value_len;
char *value;
gboolean is_no_header_termination = FALSE;
proto_item *cause;
proto_tree *pai_uri_item_tree = NULL;
proto_tree *pmiss_uri_item_tree = NULL;
proto_tree *ppi_uri_item_tree = NULL;
proto_tree *tc_uri_item_tree = NULL;
uri_offset_info uri_offsets;
linelen = tvb_find_line_end(tvb, offset, -1, &next_offset, FALSE);
if (linelen == 0) {
/*
* This is a blank line separating the
* message header from the message body.
*/
offset = next_offset;
break;
}
line_end_offset = offset + linelen;
if(tvb_reported_length_remaining(tvb, next_offset) == 0){
is_no_header_termination = TRUE;
}else{
while ((c = tvb_get_guint8(tvb, next_offset)) == ' ' || c == '\t')
{
/*
* This line end is not a header seperator.
* It just extends the header with another line.
* Look for next line end:
*/
linelen += (next_offset - line_end_offset);
linelen += tvb_find_line_end(tvb, next_offset, -1, &next_offset, FALSE);
line_end_offset = offset + linelen;
}
}
colon_offset = tvb_find_guint8(tvb, offset, linelen, ':');
if (colon_offset == -1) {
/*
* Malformed header - no colon after the name.
*/
if(hdr_tree) {
proto_tree_add_text(hdr_tree, tvb, offset,
next_offset - offset, "%s",
tvb_format_text(tvb, offset, linelen));
}
} else {
header_len = colon_offset - offset;
hf_index = sip_is_known_sip_header(tvb, offset, header_len);
/*
* Skip whitespace after the colon.
*/
value_offset = tvb_skip_wsp(tvb, colon_offset + 1, line_end_offset - (colon_offset + 1));
/*
* Fetch the value.
*/
value_len = (gint) (line_end_offset - value_offset);
value = tvb_get_ephemeral_string(tvb, value_offset, value_len);
if (hf_index == -1) {
proto_item *ti_c = proto_tree_add_text(hdr_tree, tvb,
offset, next_offset - offset, "%s",
tvb_format_text(tvb, offset, linelen));
header_name = (gchar*)tvb_get_ephemeral_string(tvb, offset, header_len);
ascii_strdown_inplace(header_name);
ext_hdr_handle = dissector_get_string_handle(ext_hdr_subdissector_table, header_name);
if (ext_hdr_handle != NULL) {
tvbuff_t *next_tvb2;
next_tvb2 = tvb_new_subset(tvb, value_offset, value_len, value_len);
dissector_try_string(ext_hdr_subdissector_table, header_name, next_tvb2, pinfo, proto_item_add_subtree(ti_c, ett_sip_ext_hdr));
} else {
expert_add_info_format(pinfo, ti_c,
PI_UNDECODED, PI_NOTE,
"Unrecognised SIP header (%s)",
tvb_format_text(tvb, offset, header_len));
}
} else {
/*
* Add it to the protocol tree,
* but display the line as is.
*/
switch ( hf_index ) {
case POS_TO :
if(hdr_tree) {
sip_element_item = proto_tree_add_string_format(hdr_tree,
hf_header_array[hf_index], tvb,
offset, next_offset - offset,
value, "%s",
tvb_format_text(tvb, offset, linelen));
sip_element_tree = proto_item_add_subtree( sip_element_item,
ett_sip_element);
/* To = ( "To" / "t" ) HCOLON ( name-addr
* / addr-spec ) *( SEMI to-param )
*/
}
sip_uri_offset_init(&uri_offsets);
if((dissect_sip_name_addr_or_addr_spec(tvb, pinfo, value_offset, line_end_offset+2, &uri_offsets)) != -1){
display_sip_uri(tvb, sip_element_tree, &uri_offsets, &sip_to_uri);
if((uri_offsets.name_addr_start != -1) && (uri_offsets.name_addr_end != -1)){
stat_info->tap_to_addr=tvb_get_ephemeral_string(tvb, uri_offsets.name_addr_start,
uri_offsets.name_addr_end - uri_offsets.name_addr_start);
}
offset = uri_offsets.name_addr_end +1;
}
if(hdr_tree) {
/* Find parameter tag if present.
* TODO make this generic to find any interesting parameter
* use the same method as for SIP headers ?
*/
parameter_offset = offset;
while (parameter_offset < line_end_offset
&& (tvb_strneql(tvb, parameter_offset, "tag=", 4) != 0))
parameter_offset++;
if ( parameter_offset < line_end_offset ){ /* Tag found */
parameter_offset = parameter_offset + 4;
parameter_end_offset = tvb_find_guint8(tvb, parameter_offset,
(line_end_offset - parameter_offset), ';');
if ( parameter_end_offset == -1)
parameter_end_offset = line_end_offset;
parameter_len = parameter_end_offset - parameter_offset;
proto_tree_add_item(sip_element_tree, hf_sip_tag, tvb, parameter_offset,
parameter_len, FALSE);
/* Tag indicates in-dialog messages, in case we have a INVITE, SUBSCRIBE or REFER, mark it */
switch (current_method_idx) {
case SIP_METHOD_INVITE:
case SIP_METHOD_SUBSCRIBE:
case SIP_METHOD_REFER:
col_append_str(pinfo->cinfo, COL_INFO, ", in-dialog");
break;
}
}
} /* if hdr_tree */
break;
case POS_FROM :
if(hdr_tree) {
sip_element_item = proto_tree_add_string_format(hdr_tree,
hf_header_array[hf_index], tvb,
offset, next_offset - offset,
value, "%s",
tvb_format_text(tvb, offset, linelen));
sip_element_tree = proto_item_add_subtree( sip_element_item, ett_sip_element);
}
/*
* From = ( "From" / "f" ) HCOLON from-spec
* from-spec = ( name-addr / addr-spec )
* *( SEMI from-param )
*/
sip_uri_offset_init(&uri_offsets);
if((dissect_sip_name_addr_or_addr_spec(tvb, pinfo, value_offset, line_end_offset+2, &uri_offsets)) != -1){
display_sip_uri(tvb, sip_element_tree, &uri_offsets, &sip_from_uri);
if((uri_offsets.name_addr_start != -1) && (uri_offsets.name_addr_end != -1)){
stat_info->tap_from_addr=tvb_get_ephemeral_string(tvb, uri_offsets.name_addr_start,
uri_offsets.name_addr_end - uri_offsets.name_addr_start);
}
offset = uri_offsets.name_addr_end +1;
}
if(hdr_tree) {
/* Find parameter tag if present.
* TODO make this generic to find any interesting parameter
* use the same method as for SIP headers ?
*/
parameter_offset = offset;
while (parameter_offset < line_end_offset
&& (tvb_strneql(tvb, parameter_offset, "tag=", 4) != 0))
parameter_offset++;
if ( parameter_offset < line_end_offset ){ /* Tag found */
parameter_offset = parameter_offset + 4;
parameter_end_offset = tvb_find_guint8(tvb, parameter_offset,
(line_end_offset - parameter_offset), ';');
if ( parameter_end_offset == -1)
parameter_end_offset = line_end_offset;
parameter_len = parameter_end_offset - parameter_offset;
proto_tree_add_item(sip_element_tree, hf_sip_tag, tvb, parameter_offset,
parameter_len, FALSE);
}
}/* hdr_tree */
break;
case POS_P_ASSERTED_IDENTITY :
if(hdr_tree)
{
sip_element_item = proto_tree_add_string_format(hdr_tree,
hf_header_array[hf_index], tvb,
offset, next_offset - offset,
value, "%s",
tvb_format_text(tvb, offset, linelen));
sip_element_tree = proto_item_add_subtree( sip_element_item,
ett_sip_element);
/*
* PAssertedID = "P-Asserted-Identity" HCOLON PAssertedID-value
* *(COMMA PAssertedID-value)
* PAssertedID-value = name-addr / addr-spec
*
* Initialize the uri_offsets
*/
sip_uri_offset_init(&uri_offsets);
if((dissect_sip_name_addr_or_addr_spec(tvb, pinfo, value_offset, line_end_offset+2, &uri_offsets)) != -1)
pai_uri_item_tree = display_sip_uri(tvb, sip_element_tree, &uri_offsets, &sip_pai_uri);
}
break;
case POS_P_CHARGING_FUNCTION_ADDRESSES:
if(hdr_tree)
{
sip_element_item = proto_tree_add_string_format(hdr_tree,
hf_header_array[hf_index], tvb,
offset, next_offset - offset,
value, "%s",
tvb_format_text(tvb, offset, linelen));
sip_element_tree = proto_item_add_subtree( sip_element_item,
ett_sip_element);
}
break;
case POS_P_PREFERRED_IDENTITY :
if(hdr_tree)
{
sip_element_item = proto_tree_add_string_format(hdr_tree,
hf_header_array[hf_index], tvb,
offset, next_offset - offset,
value, "%s",
tvb_format_text(tvb, offset, linelen));
sip_element_tree = proto_item_add_subtree( sip_element_item,
ett_sip_element);
/*
* PPreferredID = "P-Preferred-Identity" HCOLON PPreferredID-value
* *(COMMA PPreferredID-value)
* PPreferredID-value = name-addr / addr-spec
*
* Initialize the uri_offsets
*/
sip_uri_offset_init(&uri_offsets);
if((dissect_sip_name_addr_or_addr_spec(tvb, pinfo, value_offset, line_end_offset+2, &uri_offsets)) != -1)
ppi_uri_item_tree = display_sip_uri(tvb, sip_element_tree, &uri_offsets, &sip_ppi_uri);
}
break;
case POS_PERMISSION_MISSING :
if(hdr_tree)
{
sip_element_item = proto_tree_add_string_format(hdr_tree,
hf_header_array[hf_index], tvb,
offset, next_offset - offset,
value, "%s",
tvb_format_text(tvb, offset, linelen));
sip_element_tree = proto_item_add_subtree( sip_element_item,
ett_sip_element);
/*
* Permission-Missing = "Permission-Missing" HCOLON per-miss-spec
* *( COMMA per-miss-spec )
* per-miss-spec = ( name-addr / addr-spec )
* *( SEMI generic-param )
* Initialize the uri_offsets
*/
sip_uri_offset_init(&uri_offsets);
if((dissect_sip_name_addr_or_addr_spec(tvb, pinfo, value_offset, line_end_offset+2, &uri_offsets)) != -1)
pmiss_uri_item_tree = display_sip_uri(tvb, sip_element_tree, &uri_offsets, &sip_pmiss_uri);
}
break;
case POS_TRIGGER_CONSENT :
if(hdr_tree)
{
sip_element_item = proto_tree_add_string_format(hdr_tree,
hf_header_array[hf_index], tvb,
offset, next_offset - offset,
value, "%s",
tvb_format_text(tvb, offset, linelen));
sip_element_tree = proto_item_add_subtree( sip_element_item,
ett_sip_element);
}
/*
* Trigger-Consent = "Trigger-Consent" HCOLON trigger-cons-spec
* *( COMMA trigger-cons-spec )
* trigger-cons-spec = ( SIP-URI / SIPS-URI )
* *( SEMI trigger-param )
* trigger-param = target-uri / generic-param
* target-uri = "target-uri" EQUAL
* LDQUOT *( qdtext / quoted-pair ) RDQUOT
* Initialize the uri_offsets
*/
sip_uri_offset_init(&uri_offsets);
if((dissect_sip_uri(tvb, pinfo, value_offset, line_end_offset+2, &uri_offsets)) != -1) {
tc_uri_item_tree = display_sip_uri(tvb, sip_element_tree, &uri_offsets, &sip_tc_uri);
if (line_end_offset > uri_offsets.uri_end) {
gint hparam_offset = uri_offsets.uri_end + 1;
/* Is there a header parameter */
if (tvb_find_guint8(tvb, hparam_offset, 1,';')) {
while ((hparam_offset != -1 && hparam_offset < line_end_offset) ) {
/* Is this a target-uri ? */
hparam_offset = hparam_offset + 1;
if (tvb_strncaseeql(tvb, hparam_offset, "target-uri=\"", 12) == 0) {
gint turi_start_offset = hparam_offset + 12;
gint turi_end_offset = tvb_find_guint8(tvb, turi_start_offset, -1,'\"');
if (turi_end_offset != -1)
proto_tree_add_item(tc_uri_item_tree, hf_sip_tc_turi, tvb, turi_start_offset,(turi_end_offset - turi_start_offset),FALSE);
else
break; /* malformed */
}
hparam_offset = tvb_find_guint8(tvb, hparam_offset, -1,';');
}
}
}
}
break;
case POS_CSEQ :
/* Store the sequence number */
cseq_number = atoi(value);
cseq_number_set = 1;
stat_info->tap_cseq_number=cseq_number;
/* Add CSeq tree */
if (hdr_tree) {
sip_element_item = proto_tree_add_string_format(hdr_tree,
hf_header_array[hf_index], tvb,
offset, next_offset - offset,
value, "%s",
tvb_format_text(tvb, offset, linelen));
cseq_tree = proto_item_add_subtree(sip_element_item, ett_sip_cseq);
}
/* Walk past number and spaces characters to get to start
of method name */
for (sub_value_offset=0; sub_value_offset < (gint)strlen(value); sub_value_offset++)
{
if (!isdigit((guchar)value[sub_value_offset]))
{
proto_tree_add_uint(cseq_tree, hf_sip_cseq_seq_no,
tvb, value_offset, sub_value_offset,
cseq_number);
break;
}
}
for (; sub_value_offset < (gint)strlen(value); sub_value_offset++)
{
if (isalpha((guchar)value[sub_value_offset]))
{
/* Have reached start of method name */
break;
}
}
if (sub_value_offset == (gint)strlen(value))
{
/* Didn't find method name */
THROW(ReportedBoundsError);
return offset - orig_offset;
}
/* Extract method name from value */
strlen_to_copy = (int)strlen(value)-sub_value_offset;
if (strlen_to_copy > MAX_CSEQ_METHOD_SIZE) {
/* Note the error in the protocol tree */
if (hdr_tree) {
proto_tree_add_string_format(hdr_tree,
hf_header_array[hf_index], tvb,
offset, next_offset - offset,
value+sub_value_offset, "%s String too big: %d bytes",
sip_headers[POS_CSEQ].name,
strlen_to_copy);
}
THROW(ReportedBoundsError);
return offset - orig_offset;
}
else {
g_strlcpy(cseq_method, value+sub_value_offset, MAX_CSEQ_METHOD_SIZE);
/* Add CSeq method to the tree */
if (cseq_tree)
{
proto_tree_add_item(cseq_tree, hf_sip_cseq_method, tvb,
value_offset + sub_value_offset, strlen_to_copy, FALSE);
}
}
break;
case POS_RACK :
{
int cseq_no_offset;
int cseq_method_offset;
/* Add RAck tree */
if (hdr_tree) {
sip_element_item = proto_tree_add_string_format(hdr_tree,
hf_header_array[hf_index], tvb,
offset, next_offset - offset,
value, "%s",
tvb_format_text(tvb, offset, linelen));
rack_tree = proto_item_add_subtree(sip_element_item, ett_sip_rack);
}
/* RSeq number */
for (sub_value_offset=0; sub_value_offset < (gint)strlen(value); sub_value_offset++)
{
if (!isdigit((guchar)value[sub_value_offset]))
{
proto_tree_add_uint(rack_tree, hf_sip_rack_rseq_no,
tvb, value_offset, sub_value_offset,
atoi(value));
break;
}
}
/* Get to start of CSeq number */
for ( ; sub_value_offset < (gint)strlen(value); sub_value_offset++)
{
if (value[sub_value_offset] != ' ' &&
value[sub_value_offset] != '\t')
{
break;
}
}
cseq_no_offset = sub_value_offset;
/* CSeq number */
for ( ; sub_value_offset < (gint)strlen(value); sub_value_offset++)
{
if (!isdigit((guchar)value[sub_value_offset]))
{
proto_tree_add_uint(rack_tree, hf_sip_rack_cseq_no,
tvb, value_offset+cseq_no_offset,
sub_value_offset-cseq_no_offset,
atoi(value+cseq_no_offset));
break;
}
}
/* Get to start of CSeq method name */
for ( ; sub_value_offset < (gint)strlen(value); sub_value_offset++)
{
if (isalpha((guchar)value[sub_value_offset]))
{
/* Have reached start of method name */
break;
}
}
cseq_method_offset = sub_value_offset;
if (sub_value_offset == (gint)strlen(value))
{
/* Didn't find method name */
THROW(ReportedBoundsError);
return offset - orig_offset;
}
/* Add CSeq method to the tree */
if (cseq_tree)
{
proto_tree_add_item(rack_tree, hf_sip_rack_cseq_method, tvb,
value_offset + sub_value_offset,
(int)strlen(value)-sub_value_offset, FALSE);
}
break;
}
case POS_CALL_ID :
/* Store the Call-id */
g_strlcpy(call_id, value, MAX_CALL_ID_SIZE);
stat_info->tap_call_id = ep_strdup(call_id);
/* Add 'Call-id' string item to tree */
if(hdr_tree) {
sip_element_item = proto_tree_add_string_format(hdr_tree,
hf_header_array[hf_index], tvb,
offset, next_offset - offset,
value, "%s",
tvb_format_text(tvb, offset, linelen));
}
break;
case POS_EXPIRES :
if (strcmp(value, "0") == 0)
{
expires_is_0 = 1;
}
/* Add 'Expires' string item to tree */
if(hdr_tree) {
sip_element_item = proto_tree_add_uint(hdr_tree,
hf_header_array[hf_index], tvb,
offset, next_offset - offset,
atoi(value));
}
break;
/*
* Content-Type is the same as Internet
* media type used by other dissectors,
* appropriate dissector found by
* lookup in "media_type" dissector table.
*/
case POS_CONTENT_TYPE :
if(hdr_tree) {
sip_element_item = proto_tree_add_string_format(hdr_tree,
hf_header_array[hf_index], tvb,
offset, next_offset - offset,
value, "%s",
tvb_format_text(tvb, offset, linelen));
}
content_type_len = value_len;
semi_colon_offset = tvb_find_guint8(tvb, value_offset, value_len, ';');
/* Content-Type = ( "Content-Type" / "c" ) HCOLON media-type
* media-type = m-type SLASH m-subtype *(SEMI m-parameter)
* SEMI = SWS ";" SWS ; semicolon
* LWS = [*WSP CRLF] 1*WSP ; linear whitespace
* SWS = [LWS] ; sep whitespace
*/
if ( semi_colon_offset != -1) {
gint content_type_end;
/*
* Skip whitespace after the semicolon.
*/
parameter_offset = tvb_skip_wsp(tvb, semi_colon_offset +1, value_offset + value_len - (semi_colon_offset +1));
content_type_end = tvb_skip_wsp_return(tvb, semi_colon_offset-1);
content_type_len = content_type_end - value_offset;
content_type_parameter_str_len = value_offset + value_len - parameter_offset;
content_type_parameter_str = tvb_get_ephemeral_string(tvb, parameter_offset,
content_type_parameter_str_len);
}
media_type_str_lower_case = ascii_strdown_inplace(
(gchar *)tvb_get_ephemeral_string(tvb, value_offset, content_type_len));
/* Debug code
proto_tree_add_text(hdr_tree, tvb, value_offset,content_type_len,
"media_type_str(lower cased)=%s",media_type_str_lower_case);
*/
break;
case POS_CONTENT_LENGTH :
content_length = atoi(value);
if(hdr_tree) {
sip_element_item = proto_tree_add_uint_format(hdr_tree,
hf_header_array[hf_index], tvb,
offset, next_offset - offset,
content_length, "%s",
tvb_format_text(tvb, offset, linelen));
}
break;
case POS_MAX_BREADTH :
case POS_MAX_FORWARDS :
case POS_RSEQ :
if(hdr_tree) {
sip_element_item = proto_tree_add_uint(hdr_tree,
hf_header_array[hf_index], tvb,
offset, next_offset - offset,
atoi(value));
}
break;
case POS_CONTACT :
if(hdr_tree) {
sip_element_item = proto_tree_add_string_format(hdr_tree,
hf_header_array[hf_index], tvb,
offset, next_offset - offset,
value, "%s",
tvb_format_text(tvb, offset, linelen));
sip_element_tree = proto_item_add_subtree( sip_element_item,
ett_sip_element);
}
if (strcmp(value, "*") == 0)
{
contact_is_star = 1;
break;
}
comma_offset = value_offset;
while((comma_offset = dissect_sip_contact_item(tvb, pinfo, sip_element_tree, comma_offset, next_offset)) != -1)
{
contacts++;
if(comma_offset == next_offset)
{
/* Line End reached: Stop Parsing */
break;
}
if(tvb_get_guint8(tvb, comma_offset) != ',')
{
/* Undefined value reached: Stop Parsing */
break;
}
comma_offset++; /* skip comma */
}
break;
case POS_AUTHORIZATION:
case POS_WWW_AUTHENTICATE:
case POS_PROXY_AUTHENTICATE:
case POS_PROXY_AUTHORIZATION:
case POS_AUTHENTICATION_INFO:
/* Add tree using whole text of line */
if (hdr_tree) {
proto_item *ti_c;
/* Add whole line as header tree */
sip_element_item = proto_tree_add_string_format(hdr_tree,
hf_header_array[hf_index], tvb,
offset, next_offset - offset,
value, "%s",
tvb_format_text(tvb, offset, linelen));
sip_element_tree = proto_item_add_subtree( sip_element_item,
ett_sip_element);
/* Set sip.auth as a hidden field/filter */
ti_c = proto_tree_add_item(hdr_tree, hf_sip_auth, tvb,
offset, next_offset-offset,
FALSE);
PROTO_ITEM_SET_HIDDEN(ti_c);
/* Parse each individual parameter in the line */
comma_offset = tvb_pbrk_guint8(tvb, value_offset, line_end_offset - value_offset, " \t\r\n", NULL);
/* Authentication-Info does not begin with the scheme name */
if (hf_index != POS_AUTHENTICATION_INFO)
{
proto_tree_add_item(sip_element_tree, hf_sip_auth_scheme,
tvb, value_offset, comma_offset - value_offset,
FALSE);
}
while ((comma_offset = dissect_sip_authorization_item(tvb, sip_element_tree, comma_offset, line_end_offset)) != -1)
{
if(comma_offset == line_end_offset)
{
/* Line End reached: Stop Parsing */
break;
}
if(tvb_get_guint8(tvb, comma_offset) != ',')
{
/* Undefined value reached: Stop Parsing */
break;
}
comma_offset++; /* skip comma */
}
}/*hdr_tree*/
break;
case POS_VIA:
/* Add Via subtree */
if (hdr_tree) {
sip_element_item = proto_tree_add_string_format(hdr_tree,
hf_header_array[hf_index], tvb,
offset, next_offset - offset,
value, "%s",
tvb_format_text(tvb, offset, linelen));
via_tree = proto_item_add_subtree(sip_element_item, ett_sip_via);
dissect_sip_via_header(tvb, via_tree, value_offset, line_end_offset);
}
break;
case POS_REASON:
if(hdr_tree) {
sip_element_item = proto_tree_add_string_format(hdr_tree,
hf_header_array[hf_index], tvb,
offset, next_offset - offset,
value, "%s",
tvb_format_text(tvb, offset, linelen));
reason_tree = proto_item_add_subtree(sip_element_item, ett_sip_reason);
dissect_sip_reason_header(tvb, reason_tree, value_offset, line_end_offset);
}
break;
default :
/* Default case is to assume its an FT_STRING field */
if(hdr_tree) {
sip_element_item = proto_tree_add_string_format(hdr_tree,
hf_header_array[hf_index], tvb,
offset, next_offset - offset,
value, "%s",
tvb_format_text(tvb, offset, linelen));
}
break;
}/* end switch */
}/*if HF_index */
}/* if colon_offset */
if (is_no_header_termination == TRUE){
/* Header not terminated by empty line CRLF */
cause=proto_tree_add_text(hdr_tree, tvb, line_end_offset, -1,
"[Header not terminated by empty line (CRLF)]");
proto_item_set_expert_flags(cause, PI_MALFORMED, PI_WARN);
expert_add_info_format(pinfo, sip_element_item,
PI_MALFORMED, PI_WARN,
"Header not terminated by empty line (CRLF)");
}
offset = next_offset;
}/* End while */
datalen = tvb_length_remaining(tvb, offset);
reported_datalen = tvb_reported_length_remaining(tvb, offset);
if (content_length != -1) {
if (datalen > content_length)
datalen = content_length;
if (reported_datalen > content_length)
reported_datalen = content_length;
}
if (datalen > 0) {
/*
* There's a message body starting at "offset".
* Set the length of the header item.
*/
proto_item_set_end(th, tvb, offset);
next_tvb = tvb_new_subset(tvb, offset, datalen, reported_datalen);
if(sip_tree) {
ti_a = proto_tree_add_item(sip_tree, hf_sip_msg_body, next_tvb, 0, -1,
FALSE);
message_body_tree = proto_item_add_subtree(ti_a, ett_sip_message_body);
}
/* give the content type parameters to sub dissectors */
if ( media_type_str_lower_case != NULL ) {
void *save_private_data = pinfo->private_data;
pinfo->private_data = content_type_parameter_str;
found_match = dissector_try_string(media_type_dissector_table,
media_type_str_lower_case,
next_tvb, pinfo,
message_body_tree);
pinfo->private_data = save_private_data;
/* If no match dump as text */
}
if ( found_match != TRUE )
{
if (!(dissector_try_heuristic(heur_subdissector_list,
next_tvb, pinfo, message_body_tree))) {
int tmp_offset = 0;
while (tvb_offset_exists(next_tvb, tmp_offset)) {
tvb_find_line_end(next_tvb, tmp_offset, -1, &next_offset, FALSE);
linelen = next_offset - tmp_offset;
if(message_body_tree) {
proto_tree_add_text(message_body_tree, next_tvb,
tmp_offset, linelen, "%s",
tvb_format_text(next_tvb, tmp_offset, linelen));
}
tmp_offset = next_offset;
}/* end while */
}
}
offset += datalen;
}
/* Add to info column interesting things learned from header fields. */
if (check_col(pinfo->cinfo, COL_INFO))
{
/* Registration requests */
if (current_method_idx == SIP_METHOD_REGISTER)
{
if (contact_is_star && expires_is_0)
{
col_append_str(pinfo->cinfo, COL_INFO, " (remove all bindings)");
}
else
if (!contacts)
{
col_append_str(pinfo->cinfo, COL_INFO, " (fetch bindings)");
}
}
/* Registration responses */
if (line_type == STATUS_LINE && (strcmp(cseq_method, "REGISTER") == 0))
{
col_append_fstr(pinfo->cinfo, COL_INFO, " (%d bindings)", contacts);
}
}
/* Find the total setup time, Must be done before checking for resend
* As that will overwrite the "Request packet no".
*/
if ((line_type == REQUEST_LINE)&&(strcmp(cseq_method, "ACK") == 0))
{
request_for_response = sip_find_invite(pinfo, cseq_method, call_id,
cseq_number_set, cseq_number,
&response_time);
stat_info->setup_time = response_time;
}
/* Check if this packet is a resend. */
resend_for_packet = sip_is_packet_resend(pinfo, cseq_method, call_id,
cseq_number_set, cseq_number,
line_type);
/* Mark whether this is a resend for the tap */
stat_info->resend = (resend_for_packet > 0);
/* And add the filterable field to the request/response line */
if (reqresp_tree)
{
proto_item *item;
item = proto_tree_add_boolean(reqresp_tree, hf_sip_resend, tvb, orig_offset, 0,
resend_for_packet > 0);
PROTO_ITEM_SET_GENERATED(item);
if (resend_for_packet > 0)
{
item = proto_tree_add_uint(reqresp_tree, hf_sip_original_frame,
tvb, orig_offset, 0, resend_for_packet);
PROTO_ITEM_SET_GENERATED(item);
}
}
/* For responses, try to link back to request frame */
if (line_type == STATUS_LINE)
{
request_for_response = sip_find_request(pinfo, cseq_method, call_id,
cseq_number_set, cseq_number,
&response_time);
}
if (reqresp_tree)
{
proto_item *item;
if (request_for_response > 0)
{
item = proto_tree_add_uint(reqresp_tree, hf_sip_matching_request_frame,
tvb, orig_offset, 0, request_for_response);
PROTO_ITEM_SET_GENERATED(item);
item = proto_tree_add_uint(reqresp_tree, hf_sip_response_time,
tvb, orig_offset, 0, response_time);
PROTO_ITEM_SET_GENERATED(item);
if ((line_type == STATUS_LINE)&&(strcmp(cseq_method, "BYE") == 0)){
item = proto_tree_add_uint(reqresp_tree, hf_sip_release_time,
tvb, orig_offset, 0, response_time);
PROTO_ITEM_SET_GENERATED(item);
}
}
}
if (ts != NULL)
proto_item_set_len(ts, offset - orig_offset);
if (global_sip_raw_text)
tvb_raw_text_add(tvb, orig_offset, offset - orig_offset, tree);
/* Report this packet to the tap */
if (!pinfo->in_error_pkt)
{
tap_queue_packet(sip_tap, pinfo, stat_info);
}
return offset - orig_offset;
}
/* Display filter for SIP Request-Line */
static void
dfilter_sip_request_line(tvbuff_t *tvb, proto_tree *tree, packet_info *pinfo, gint offset, guint meth_len, gint linelen)
{
char *value;
guint parameter_len = meth_len;
uri_offset_info uri_offsets;
/*
* We know we have the entire method; otherwise, "sip_parse_line()"
* would have returned OTHER_LINE.
* Request-Line = Method SP Request-URI SP SIP-Version CRLF
* SP = single space
* Request-URI = SIP-URI / SIPS-URI / absoluteURI
*/
/* get method string*/
value = tvb_get_ephemeral_string(tvb, offset, parameter_len);
/* Copy request method for telling tap */
stat_info->request_method = value;
if (tree) {
proto_tree_add_string(tree, hf_sip_Method, tvb, offset, parameter_len, value);
/* build Request-URI tree*/
offset=offset + parameter_len+1;
sip_uri_offset_init(&uri_offsets);
/* calc R-URI len*/
uri_offsets.uri_end = tvb_find_guint8(tvb, offset, linelen, ' ')-1;
dissect_sip_uri(tvb, pinfo, offset, offset + linelen, &uri_offsets);
display_sip_uri(tvb, tree, &uri_offsets, &sip_req_uri);
}
}
/* Display filter for SIP Status-Line */
static void
dfilter_sip_status_line(tvbuff_t *tvb, proto_tree *tree)
{
char string[3+1];
gint response_code = 0;
/*
* We know we have the entire status code; otherwise,
* "sip_parse_line()" would have returned OTHER_LINE.
* We also know that we have a version string followed by a
* space at the beginning of the line, for the same reason.
*/
tvb_memcpy(tvb, (guint8 *)string, SIP2_HDR_LEN + 1, 3);
string[3] = '\0';
response_code = atoi(string);
/* Add numerical response code to tree */
if (tree) {
proto_tree_add_uint(tree, hf_Status_Code, tvb, SIP2_HDR_LEN + 1,
3, response_code);
}
/* Add response code for sending to tap */
stat_info->response_code = response_code;
}
/* From section 4.1 of RFC 2543:
*
* Request-Line = Method SP Request-URI SP SIP-Version CRLF
*
* From section 5.1 of RFC 2543:
*
* Status-Line = SIP-version SP Status-Code SP Reason-Phrase CRLF
*
* From section 7.1 of RFC 3261:
*
* Unlike HTTP, SIP treats the version number as a literal string.
* In practice, this should make no difference.
*/
static line_type_t
sip_parse_line(tvbuff_t *tvb, int offset, gint linelen, guint *token_1_lenp)
{
gint space_offset;
gint token_1_start;
guint token_1_len;
gint token_2_start;
guint token_2_len;
gint token_3_start;
guint token_3_len;
gint colon_pos;
token_1_start = offset;
space_offset = tvb_find_guint8(tvb, token_1_start, -1, ' ');
if ((space_offset == -1) || (space_offset == token_1_start)) {
/*
* Either there's no space in the line (which means
* the line is empty or doesn't have a token followed
* by a space; neither is valid for a request or status), or
* the first character in the line is a space (meaning
* the method is empty, which isn't valid for a request,
* or the SIP version is empty, which isn't valid for a
* status).
*/
return OTHER_LINE;
}
token_1_len = space_offset - token_1_start;
token_2_start = space_offset + 1;
space_offset = tvb_find_guint8(tvb, token_2_start, -1, ' ');
if (space_offset == -1) {
/*
* There's no space after the second token, so we don't
* have a third token.
*/
return OTHER_LINE;
}
token_2_len = space_offset - token_2_start;
token_3_start = space_offset + 1;
token_3_len = token_1_start + linelen - token_3_start;
*token_1_lenp = token_1_len;
/*
* Is the first token a version string?
*/
if ( (strict_sip_version && (
token_1_len == SIP2_HDR_LEN
&& tvb_strneql(tvb, token_1_start, SIP2_HDR, SIP2_HDR_LEN) == 0)
) || (! strict_sip_version && (
tvb_strncaseeql(tvb, token_1_start, "SIP/", 4) == 0)
)) {
/*
* Yes, so this is either a Status-Line or something
* else other than a Request-Line. To be a Status-Line,
* the second token must be a 3-digit number.
*/
if (token_2_len != 3) {
/*
* We don't have 3-character status code.
*/
return OTHER_LINE;
}
if (!isdigit(tvb_get_guint8(tvb, token_2_start)) ||
!isdigit(tvb_get_guint8(tvb, token_2_start + 1)) ||
!isdigit(tvb_get_guint8(tvb, token_2_start + 2))) {
/*
* 3 characters yes, 3 digits no.
*/
return OTHER_LINE;
}
return STATUS_LINE;
} else {
/*
* No, so this is either a Request-Line or something
* other than a Status-Line. To be a Request-Line, the
* second token must be a URI and the third token must
* be a version string.
*/
if (token_2_len < 3) {
/*
* We don't have a URI consisting of at least 3
* characters.
*/
return OTHER_LINE;
}
colon_pos = tvb_find_guint8(tvb, token_2_start + 1, -1, ':');
if (colon_pos == -1) {
/*
* There is no colon after the method, so the URI
* doesn't have a colon in it, so it's not valid.
*/
return OTHER_LINE;
}
if (colon_pos >= token_3_start) {
/*
* The colon is in the version string, not the URI.
*/
return OTHER_LINE;
}
/* XXX - Check for a proper URI prefix? */
if ( (strict_sip_version && (
token_3_len != SIP2_HDR_LEN
|| tvb_strneql(tvb, token_3_start, SIP2_HDR, SIP2_HDR_LEN) == -1)
) || (! strict_sip_version && (
tvb_strncaseeql(tvb, token_3_start, "SIP/", 4) == -1)
)) {
/*
* The version string isn't an SIP version 2.0 version
* string.
*/
return OTHER_LINE;
}
return REQUEST_LINE;
}
}
static gboolean sip_is_known_request(tvbuff_t *tvb, int meth_offset,
guint meth_len, guint *meth_idx)
{
guint i;
for (i = 1; i < array_length(sip_methods); i++) {
if (meth_len == strlen(sip_methods[i]) &&
tvb_strneql(tvb, meth_offset, sip_methods[i], meth_len) == 0)
{
*meth_idx = i;
return TRUE;
}
}
return FALSE;
}
/* Returns index of method in sip_headers */
static gint sip_is_known_sip_header(tvbuff_t *tvb, int offset, guint header_len)
{
guint i;
guint pos=0;
gchar *header = tvb_format_text(tvb, offset, header_len);
/* Compact name is one character long */
if(header_len>1){
pos = GPOINTER_TO_INT(g_hash_table_lookup(sip_headers_hash, header));
if (pos!=0)
return pos;
}
/* Previous searching in the hash is case sensitive.
If the name has not been not found it could be also long name with case different from RFC,
it is not recommended but allowed.
*/
/* Look for compact name match or long name with non-standard case */
for (i = 1; i < array_length(sip_headers); i++) {
if (sip_headers[i].compact_name != NULL &&
header_len == strlen(sip_headers[i].compact_name) &&
tvb_strncaseeql(tvb, offset, sip_headers[i].compact_name, header_len) == 0)
return i;
if (header_len == strlen(sip_headers[i].name) &&
tvb_strncaseeql(tvb, offset, sip_headers[i].name, header_len) == 0)
return i;
}
return -1;
}
/*
* Display the entire message as raw text.
*/
static void
tvb_raw_text_add(tvbuff_t *tvb, int offset, int length, proto_tree *tree)
{
proto_tree *raw_tree = NULL;
proto_item *ti = NULL;
int next_offset, linelen, end_offset;
if (tree) {
ti = proto_tree_add_item(tree, proto_raw_sip, tvb, offset, length, FALSE);
raw_tree = proto_item_add_subtree(ti, ett_raw_text);
}
end_offset = offset + length;
while (offset < end_offset) {
tvb_find_line_end(tvb, offset, -1, &next_offset, FALSE);
linelen = next_offset - offset;
if (raw_tree) {
proto_tree_add_string_format(raw_tree, hf_raw_sip_line, tvb, offset, linelen,
tvb_format_text(tvb, offset, linelen),
"%s",
tvb_format_text(tvb, offset, linelen));
}
offset = next_offset;
}
}
/* Check to see if this packet is a resent request. Return value is the frame number
of the original frame this packet seems to be resending (0 = no resend). */
guint sip_is_packet_resend(packet_info *pinfo,
gchar *cseq_method,
gchar *call_id,
guchar cseq_number_set,
guint32 cseq_number, line_type_t line_type)
{
guint32 cseq_to_compare = 0;
sip_hash_key key;
sip_hash_key *p_key = 0;
sip_hash_value *p_val = 0;
sip_frame_result_value *sip_frame_result = NULL;
guint result = 0;
/* Only consider retransmission of UDP packets */
if (pinfo->ptype != PT_UDP)
{
return 0;
}
/* Don't consider packets that appear to be resent only because
they are e.g. returned in ICMP unreachable messages. */
if (pinfo->in_error_pkt)
{
return 0;
}
/* A broken packet may have no cseq number set. Don't consider it as
a resend */
if (!cseq_number_set)
{
return 0;
}
/* Return any answer stored from previous dissection */
if (pinfo->fd->flags.visited)
{
sip_frame_result = (sip_frame_result_value*)p_get_proto_data(pinfo->fd, proto_sip);
if (sip_frame_result != NULL)
{
return sip_frame_result->original_frame_num;
}
else
{
return 0;
}
}
/* No packet entry found, consult global hash table */
/* Prepare the key */
g_strlcpy(key.call_id, call_id, MAX_CALL_ID_SIZE);
/* We're only using these addresses locally (for the hash lookup) so
* there is no need to make a (g_malloc'd) copy of them.
*/
SET_ADDRESS(&key.dest_address, pinfo->net_dst.type, pinfo->net_dst.len,
pinfo->net_dst.data);
SET_ADDRESS(&key.source_address, pinfo->net_src.type,
pinfo->net_src.len, pinfo->net_src.data);
key.dest_port = pinfo->destport;
if (sip_retrans_the_same_sport) {
key.source_port = pinfo->srcport;
} else {
key.source_port = MAGIC_SOURCE_PORT;
}
/* Do the lookup */
p_val = (sip_hash_value*)g_hash_table_lookup(sip_hash, &key);
if (p_val)
{
/* Table entry found, we'll use its value for comparison */
cseq_to_compare = p_val->cseq;
/* First time through, must update value with current details if
cseq number has changed */
if (cseq_number != p_val->cseq)
{
p_val->cseq = cseq_number;
g_strlcpy(p_val->method, cseq_method, MAX_CSEQ_METHOD_SIZE);
p_val->transaction_state = nothing_seen;
p_val->frame_number = 0;
if (line_type == REQUEST_LINE)
{
p_val->request_time = pinfo->fd->abs_ts;
}
}
}
else
{
/* Need to create a new table entry */
/* Allocate a new key and value */
p_key = se_alloc(sizeof(sip_hash_key));
p_val = se_alloc(sizeof(sip_hash_value));
/* Fill in key and value details */
g_snprintf(p_key->call_id, MAX_CALL_ID_SIZE, "%s", call_id);
SE_COPY_ADDRESS(&(p_key->dest_address), &pinfo->net_dst);
SE_COPY_ADDRESS(&(p_key->source_address), &pinfo->net_src);
p_key->dest_port = pinfo->destport;
if (sip_retrans_the_same_sport) {
p_key->source_port = pinfo->srcport;
} else {
p_key->source_port = MAGIC_SOURCE_PORT;
}
p_val->cseq = cseq_number;
g_strlcpy(p_val->method, cseq_method, MAX_CSEQ_METHOD_SIZE);
p_val->transaction_state = nothing_seen;
p_val->frame_number = 0;
if (line_type == REQUEST_LINE)
{
p_val->request_time = pinfo->fd->abs_ts;
}
/* Add entry */
g_hash_table_insert(sip_hash, p_key, p_val);
/* Assume have seen no cseq yet */
cseq_to_compare = 0;
}
/******************************************/
/* Is it a resend??? */
/* Does this look like a resent request (discount ACK, CANCEL, or a
different method from the original one) ? */
if ((line_type == REQUEST_LINE) && (cseq_number == cseq_to_compare) &&
(p_val->transaction_state == request_seen) &&
(strcmp(cseq_method, p_val->method) == 0) &&
(strcmp(cseq_method, "ACK") != 0) &&
(strcmp(cseq_method, "CANCEL") != 0))
{
result = p_val->frame_number;
}
/* Does this look like a resent final response ? */
if ((line_type == STATUS_LINE) && (cseq_number == cseq_to_compare) &&
(p_val->transaction_state == final_response_seen) &&
(strcmp(cseq_method, p_val->method) == 0) &&
(stat_info->response_code >= 200) &&
(stat_info->response_code == p_val->response_code))
{
result = p_val->frame_number;
}
/* Update state for this entry */
p_val->cseq = cseq_number;
switch (line_type)
{
case REQUEST_LINE:
p_val->transaction_state = request_seen;
if (!result)
{
/* This frame is the original request */
p_val->frame_number = pinfo->fd->num;
}
break;
case STATUS_LINE:
if (stat_info->response_code >= 200)
{
p_val->response_code = stat_info->response_code;
p_val->transaction_state = final_response_seen;
if (!result)
{
/* This frame is the original response */
p_val->frame_number = pinfo->fd->num;
}
}
else
{
p_val->transaction_state = provisional_response_seen;
}
break;
default:
break;
}
sip_frame_result = p_get_proto_data(pinfo->fd, proto_sip);
if (sip_frame_result == NULL)
{
sip_frame_result = se_alloc(sizeof(sip_frame_result_value));
}
/* Store return value with this packet */
sip_frame_result->original_frame_num = result;
p_add_proto_data(pinfo->fd, proto_sip, sip_frame_result);
return result;
}
/* Check to see if this packet is a resent request. Return value is the frame number
of the original frame this packet seems to be resending (0 = no resend). */
guint sip_find_request(packet_info *pinfo,
gchar *cseq_method,
gchar *call_id,
guchar cseq_number_set,
guint32 cseq_number,
guint32 *response_time)
{
guint32 cseq_to_compare = 0;
sip_hash_key key;
sip_hash_value *p_val = 0;
sip_frame_result_value *sip_frame_result = NULL;
guint result = 0;
gint seconds_between_packets;
gint nseconds_between_packets;
/* Only consider UDP */
if (pinfo->ptype != PT_UDP)
{
return 0;
}
/* Ignore error (usually ICMP) frames */
if (pinfo->in_error_pkt)
{
return 0;
}
/* A broken packet may have no cseq number set. Ignore. */
if (!cseq_number_set)
{
return 0;
}
/* Return any answer stored from previous dissection */
if (pinfo->fd->flags.visited)
{
sip_frame_result = (sip_frame_result_value*)p_get_proto_data(pinfo->fd, proto_sip);
if (sip_frame_result != NULL)
{
*response_time = sip_frame_result->response_time;
return sip_frame_result->response_request_frame_num;
}
else
{
return 0;
}
}
/* No packet entry found, consult global hash table */
/* Prepare the key */
g_strlcpy(key.call_id, call_id, MAX_CALL_ID_SIZE);
/* Looking for matching request, so reverse addresses for this lookup */
SET_ADDRESS(&key.dest_address, pinfo->net_src.type, pinfo->net_src.len,
pinfo->net_src.data);
SET_ADDRESS(&key.source_address, pinfo->net_dst.type, pinfo->net_dst.len,
pinfo->net_dst.data);
key.dest_port = pinfo->srcport;
key.source_port = pinfo->destport;
/* Do the lookup */
p_val = (sip_hash_value*)g_hash_table_lookup(sip_hash, &key);
if (p_val)
{
/* Table entry found, we'll use its value for comparison */
cseq_to_compare = p_val->cseq;
}
else
{
/* We don't have the request */
return 0;
}
/**************************************************/
/* Is it a response to a request that we've seen? */
if ((cseq_number == cseq_to_compare) &&
(p_val->transaction_state == request_seen) &&
(strcmp(cseq_method, p_val->method) == 0))
{
result = p_val->frame_number;
}
/* Store return value with this packet */
sip_frame_result = p_get_proto_data(pinfo->fd, proto_sip);
if (sip_frame_result == NULL)
{
/* Allocate and set all values to zero */
sip_frame_result = se_alloc0(sizeof(sip_frame_result_value));
}
sip_frame_result->response_request_frame_num = result;
/* Work out response time */
seconds_between_packets = (gint)
(pinfo->fd->abs_ts.secs - p_val->request_time.secs);
nseconds_between_packets =
pinfo->fd->abs_ts.nsecs - p_val->request_time.nsecs;
sip_frame_result->response_time = (seconds_between_packets*1000) +
(nseconds_between_packets / 1000000);
*response_time = sip_frame_result->response_time;
p_add_proto_data(pinfo->fd, proto_sip, sip_frame_result);
return result;
}
/*
* Find the initial INVITE to calculate the total setup time
*/
guint sip_find_invite(packet_info *pinfo,
gchar *cseq_method _U_,
gchar *call_id,
guchar cseq_number_set,
guint32 cseq_number _U_,
guint32 *response_time)
{
guint32 cseq_to_compare = 0;
sip_hash_key key;
sip_hash_value *p_val = 0;
sip_frame_result_value *sip_frame_result = NULL;
guint result = 0;
gint seconds_between_packets;
gint nseconds_between_packets;
/* Only consider UDP */
if (pinfo->ptype != PT_UDP)
{
return 0;
}
/* Ignore error (usually ICMP) frames */
if (pinfo->in_error_pkt)
{
return 0;
}
/* A broken packet may have no cseq number set. Ignore. */
if (!cseq_number_set)
{
return 0;
}
/* Return any answer stored from previous dissection */
if (pinfo->fd->flags.visited)
{
sip_frame_result = (sip_frame_result_value*)p_get_proto_data(pinfo->fd, proto_sip);
if (sip_frame_result != NULL)
{
*response_time = sip_frame_result->response_time;
return sip_frame_result->response_request_frame_num;
}
else
{
return 0;
}
}
/* No packet entry found, consult global hash table */
/* Prepare the key */
g_strlcpy(key.call_id, call_id, MAX_CALL_ID_SIZE);
/* Looking for matching INVITE */
SET_ADDRESS(&key.dest_address, pinfo->net_dst.type, pinfo->net_dst.len,
pinfo->net_dst.data);
SET_ADDRESS(&key.source_address, pinfo->net_src.type, pinfo->net_src.len,
pinfo->net_src.data);
key.dest_port = pinfo->destport;
key.source_port = pinfo->srcport;
/* Do the lookup */
p_val = (sip_hash_value*)g_hash_table_lookup(sip_hash, &key);
if (p_val)
{
/* Table entry found, we'll use its value for comparison */
cseq_to_compare = p_val->cseq;
}
else
{
/* We don't have the request */
return 0;
}
/**************************************************/
/* Is it a response to a request that we've seen? */
#if 0
if ((cseq_number == cseq_to_compare) &&
(p_val->transaction_state == request_seen) &&
(strcmp(cseq_method, p_val->method) == 0))
{
result = p_val->frame_number;
}
#endif
result = p_val->frame_number;
/* Store return value with this packet */
sip_frame_result = p_get_proto_data(pinfo->fd, proto_sip);
if (sip_frame_result == NULL)
{
/* Allocate and set all values to zero */
sip_frame_result = se_alloc0(sizeof(sip_frame_result_value));
}
sip_frame_result->response_request_frame_num = result;
/* Work out response time */
seconds_between_packets = (gint)
(pinfo->fd->abs_ts.secs - p_val->request_time.secs);
nseconds_between_packets =
pinfo->fd->abs_ts.nsecs - p_val->request_time.nsecs;
sip_frame_result->response_time = (seconds_between_packets*1000) +
(nseconds_between_packets / 1000000);
*response_time = sip_frame_result->response_time;
p_add_proto_data(pinfo->fd, proto_sip, sip_frame_result);
return result;
}
/* Register the protocol with Wireshark */
void proto_register_sip(void)
{
/* Setup list of header fields */
static hf_register_info hf[] = {
{ &hf_msg_hdr,
{ "Message Header", "sip.msg_hdr",
FT_STRING, BASE_NONE, NULL, 0,
"Message Header in SIP message", HFILL }
},
{ &hf_sip_Method,
{ "Method", "sip.Method",
FT_STRING, BASE_NONE,NULL,0x0,
"SIP Method", HFILL }
},
{ &hf_Request_Line,
{ "Request-Line", "sip.Request-Line",
FT_STRING, BASE_NONE,NULL,0x0,
"SIP Request-Line", HFILL }
},
{ &hf_sip_ruri,
{ "Request-URI", "sip.r-uri",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: SIP R-URI", HFILL }
},
{ &hf_sip_ruri_user,
{ "Request-URI User Part", "sip.r-uri.user",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: SIP R-URI User", HFILL }
},
{ &hf_sip_ruri_host,
{ "Request-URI Host Part", "sip.r-uri.host",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: SIP R-URI Host", HFILL }
},
{ &hf_sip_ruri_port,
{ "Request-URI Host Port", "sip.r-uri.port",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: SIP R-URI Port", HFILL }
},
{ &hf_Status_Code,
{ "Status-Code", "sip.Status-Code",
FT_UINT32, BASE_DEC,NULL,0x0,
"SIP Status Code", HFILL }
},
{ &hf_Status_Line,
{ "Status-Line", "sip.Status-Line",
FT_STRING, BASE_NONE,NULL,0x0,
"SIP Status-Line", HFILL }
},
{ &hf_sip_display,
{ "SIP Display info", "sip.display.info",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: Display info", HFILL }
},
{ &hf_sip_to_addr,
{ "SIP to address", "sip.to.addr",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: To Address", HFILL }
},
{ &hf_sip_to_user,
{ "SIP to address User Part", "sip.to.user",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: To Address User", HFILL }
},
{ &hf_sip_to_host,
{ "SIP to address Host Part", "sip.to.host",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: To Address Host", HFILL }
},
{ &hf_sip_to_port,
{ "SIP to address Host Port", "sip.to.port",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: To Address Port", HFILL }
},
{ &hf_sip_from_addr,
{ "SIP from address", "sip.from.addr",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: From Address", HFILL }
},
{ &hf_sip_from_user,
{ "SIP from address User Part", "sip.from.user",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: From Address User", HFILL }
},
{ &hf_sip_from_host,
{ "SIP from address Host Part", "sip.from.host",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: From Address Host", HFILL }
},
{ &hf_sip_from_port,
{ "SIP from address Host Port", "sip.from.port",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: From Address Port", HFILL }
},
{ &hf_sip_contact_addr,
{ "SIP contact address", "sip.contact.addr",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: contact addr", HFILL }
},
{ &hf_sip_uri,
{ "URI", "sip.uri",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: SIP Uri", HFILL }
},
{ &hf_sip_contact_item,
{ "Contact Binding", "sip.contact.binding",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: one contact binding", HFILL }
},
{ &hf_sip_tag,
{ "SIP tag", "sip.tag",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: tag", HFILL }
},
{ &hf_sip_pai_addr,
{ "SIP PAI Address", "sip.pai.addr",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3325: P-Asserted-Identity Address", HFILL }
},
{ &hf_sip_pai_user,
{ "SIP PAI User Part", "sip.pai.user",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3325: P-Asserted-Identity User", HFILL }
},
{ &hf_sip_pai_host,
{ "SIP PAI Host Part", "sip.pai.host",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3325: P-Asserted-Identity Host", HFILL }
},
{ &hf_sip_pai_port,
{ "SIP PAI Host Port", "sip.pai.port",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3325: P-Asserted-Identity Port", HFILL }
},
{ &hf_sip_pmiss_addr,
{ "SIP PMISS Address", "sip.pmiss.addr",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3325: Permission Missing Address", HFILL }
},
{ &hf_sip_pmiss_user,
{ "SIP PMISS User Part", "sip.pmiss.user",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3325: Permission Missing User", HFILL }
},
{ &hf_sip_pmiss_host,
{ "SIP PMISS Host Part", "sip.pmiss.host",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3325: Permission Missing Host", HFILL }
},
{ &hf_sip_pmiss_port,
{ "SIP PMISS Host Port", "sip.pmiss.port",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3325: Permission Missing Port", HFILL }
},
{ &hf_sip_ppi_addr,
{ "SIP PPI Address", "sip.ppi.addr",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3325: P-Preferred-Identity Address", HFILL }
},
{ &hf_sip_ppi_user,
{ "SIP PPI User Part", "sip.ppi.user",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3325: P-Preferred-Identity User", HFILL }
},
{ &hf_sip_ppi_host,
{ "SIP PPI Host Part", "sip.ppi.host",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3325: P-Preferred-Identity Host", HFILL }
},
{ &hf_sip_ppi_port,
{ "SIP PPI Host Port", "sip.ppi.port",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3325: P-Preferred-Identity Port", HFILL }
},
{ &hf_sip_tc_addr,
{ "SIP TC Address", "sip.tc.addr",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3325: Trigger Consent Address", HFILL }
},
{ &hf_sip_tc_user,
{ "SIP TC User Part", "sip.tc.user",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3325: Trigger Consent User", HFILL }
},
{ &hf_sip_tc_host,
{ "SIP TC Host Part", "sip.tc.host",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3325: Trigger Consent Host", HFILL }
},
{ &hf_sip_tc_port,
{ "SIP TC Host Port", "sip.tc.port",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3325: Trigger Consent Port", HFILL }
},
{ &hf_sip_tc_turi,
{ "SIP TC Target URI", "sip.tc.target-uri",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3325: Trigger Consent Target URI", HFILL }
},
{ &hf_header_array[POS_ACCEPT],
{ "Accept", "sip.Accept",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: Accept Header", HFILL }
},
{ &hf_header_array[POS_ACCEPT_CONTACT],
{ "Accept-Contact", "sip.Accept-Contact",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3841: Accept-Contact Header", HFILL }
},
{ &hf_header_array[POS_ACCEPT_ENCODING],
{ "Accept-Encoding", "sip.Accept-Encoding",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3841: Accept-Encoding Header", HFILL }
},
{ &hf_header_array[POS_ACCEPT_LANGUAGE],
{ "Accept-Language", "sip.Accept-Language",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: Accept-Language Header", HFILL }
},
{ &hf_header_array[POS_ACCEPT_RESOURCE_PRIORITY],
{ "Accept-Resource-Priority", "sip.Accept-Resource-Priority",
FT_STRING, BASE_NONE,NULL,0x0,
"Draft: Accept-Resource-Priority Header", HFILL }
},
{ &hf_header_array[POS_ALERT_INFO],
{ "Alert-Info", "sip.Alert-Info",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: Alert-Info Header", HFILL }
},
{ &hf_header_array[POS_ALLOW],
{ "Allow", "sip.Allow",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: Allow Header", HFILL }
},
{ &hf_header_array[POS_ALLOW_EVENTS],
{ "Allow-Events", "sip.Allow-Events",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3265: Allow-Events Header", HFILL }
},
{ &hf_header_array[POS_ANSWER_MODE],
{ "Answer-Mode", "sip.Answer-Mode",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 5373: Answer-Mode Header", HFILL }
},
{ &hf_header_array[POS_AUTHENTICATION_INFO],
{ "Authentication-Info", "sip.Authentication-Info",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: Authentication-Info Header", HFILL }
},
{ &hf_header_array[POS_AUTHORIZATION],
{ "Authorization", "sip.Authorization",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: Authorization Header", HFILL }
},
{ &hf_header_array[POS_CALL_ID],
{ "Call-ID", "sip.Call-ID",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: Call-ID Header", HFILL }
},
{ &hf_header_array[POS_CALL_INFO],
{ "Call-Info", "sip.Call-Info",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: Call-Info Header", HFILL }
},
{ &hf_header_array[POS_CONTACT],
{ "Contact", "sip.Contact",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: Contact Header", HFILL }
},
{ &hf_header_array[POS_CONTENT_DISPOSITION],
{ "Content-Disposition", "sip.Content-Disposition",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: Content-Disposition Header", HFILL }
},
{ &hf_header_array[POS_CONTENT_ENCODING],
{ "Content-Encoding", "sip.Content-Encoding",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: Content-Encoding Header", HFILL }
},
{ &hf_header_array[POS_CONTENT_LANGUAGE],
{ "Content-Language", "sip.Content-Language",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: Content-Language Header", HFILL }
},
{ &hf_header_array[POS_CONTENT_LENGTH],
{ "Content-Length", "sip.Content-Length",
FT_UINT32, BASE_DEC,NULL,0x0,
"RFC 3261: Content-Length Header", HFILL }
},
{ &hf_header_array[POS_CONTENT_TYPE],
{ "Content-Type", "sip.Content-Type",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: Content-Type Header", HFILL }
},
{ &hf_header_array[POS_CSEQ],
{ "CSeq", "sip.CSeq",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: CSeq Header", HFILL }
},
{ &hf_header_array[POS_DATE],
{ "Date", "sip.Date",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: Date Header", HFILL }
},
{ &hf_header_array[POS_ERROR_INFO],
{ "Error-Info", "sip.Error-Info",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: Error-Info Header", HFILL }
},
{ &hf_header_array[POS_EVENT],
{ "Event", "sip.Event",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3265: Event Header", HFILL }
},
{ &hf_header_array[POS_EXPIRES],
{ "Expires", "sip.Expires",
FT_UINT32, BASE_DEC,NULL,0x0,
"RFC 3261: Expires Header", HFILL }
},
{ &hf_header_array[POS_FROM],
{ "From", "sip.From",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: From Header", HFILL }
},
{ &hf_header_array[POS_IN_REPLY_TO],
{ "In-Reply-To", "sip.In-Reply-To",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: In-Reply-To Header", HFILL }
},
{ &hf_header_array[POS_JOIN],
{ "Join", "sip.Join",
FT_STRING, BASE_NONE,NULL,0x0,
"Draft: Join Header", HFILL }
},
{ &hf_header_array[POS_MAX_BREADTH],
{ "Max-Breadth", "sip.Max-Breadth",
FT_UINT32, BASE_DEC,NULL,0x0,
"RFC 5393: Max-Breadth Header", HFILL }
},
{ &hf_header_array[POS_MAX_FORWARDS],
{ "Max-Forwards", "sip.Max-Forwards",
FT_UINT32, BASE_DEC,NULL,0x0,
"RFC 3261: Max-Forwards Header", HFILL }
},
{ &hf_header_array[POS_MIME_VERSION],
{ "MIME-Version", "sip.MIME-Version",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: MIME-Version Header", HFILL }
},
{ &hf_header_array[POS_MIN_EXPIRES],
{ "Min-Expires", "sip.Min-Expires",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: Min-Expires Header", HFILL }
},
{ &hf_header_array[POS_MIN_SE],
{ "Min-SE", "sip.Min-SE",
FT_STRING, BASE_NONE,NULL,0x0,
"Draft: Min-SE Header", HFILL }
},
{ &hf_header_array[POS_ORGANIZATION],
{ "Organization", "sip.Organization",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: Organization Header", HFILL }
},
{ &hf_header_array[POS_P_ACCESS_NETWORK_INFO],
{ "P-Access-Network-Info", "sip.P-Access-Network-Info",
FT_STRING, BASE_NONE,NULL,0x0,
"P-Access-Network-Info Header", HFILL }
},
{ &hf_header_array[POS_P_ANSWER_STATE],
{ "P-Answer-State", "sip.P-Answer-State",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 4964: P-Answer-State Header", HFILL }
},
{ &hf_header_array[POS_P_ASSERTED_IDENTITY],
{ "P-Asserted-Identity", "sip.P-Asserted-Identity",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3325: P-Asserted-Identity Header", HFILL }
},
{ &hf_header_array[POS_P_ASSOCIATED_URI],
{ "P-Associated-URI", "sip.P-Associated-URI",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3455: P-Associated-URI Header", HFILL }
},
{ &hf_header_array[POS_P_CALLED_PARTY_ID],
{ "P-Called-Party-ID", "sip.P-Called-Party-ID",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3455: P-Called-Party-ID Header", HFILL }
},
{ &hf_header_array[POS_P_CHARGING_FUNCTION_ADDRESSES],
{ "P-Charging-Function-Addresses","sip.P-Charging-Function-Addresses",
FT_STRING, BASE_NONE,NULL,0x0,
NULL, HFILL }
},
{ &hf_header_array[POS_P_CHARGING_VECTOR],
{ "P-Charging-Vector", "sip.P-Charging-Vector",
FT_STRING, BASE_NONE,NULL,0x0,
"P-Charging-Vector Header", HFILL }
},
{ &hf_header_array[POS_P_DCS_TRACE_PARTY_ID],
{ "P-DCS-Trace-Party-ID", "sip.P-DCS-Trace-Party-ID",
FT_STRING, BASE_NONE,NULL,0x0,
"P-DCS-Trace-Party-ID Header", HFILL }
},
{ &hf_header_array[POS_P_DCS_OSPS],
{ "P-DCS-OSPS", "sip.P-DCS-OSPS",
FT_STRING, BASE_NONE,NULL,0x0,
"P-DCS-OSPS Header", HFILL }
},
{ &hf_header_array[POS_P_DCS_BILLING_INFO],
{ "P-DCS-Billing-Info", "sip.P-DCS-Billing-Info",
FT_STRING, BASE_NONE,NULL,0x0,
"P-DCS-Billing-Info Header", HFILL }
},
{ &hf_header_array[POS_P_DCS_LAES],
{ "P-DCS-LAES", "sip.P-DCS-LAES",
FT_STRING, BASE_NONE,NULL,0x0,
"P-DCS-LAES Header", HFILL }
},
{ &hf_header_array[POS_P_DCS_REDIRECT],
{ "P-DCS-Redirect", "sip.P-DCS-Redirect",
FT_STRING, BASE_NONE,NULL,0x0,
"P-DCS-Redirect Header", HFILL }
},
{ &hf_header_array[POS_P_EARLY_MEDIA],
{ "P-Early-Media", "sip.P-Early-Media",
FT_STRING, BASE_NONE,NULL,0x0,
"P-Early-Media Header", HFILL }
},
{ &hf_header_array[POS_P_MEDIA_AUTHORIZATION],
{ "P-Media-Authorization", "sip.P-Media-Authorization",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3313: P-Media-Authorization Header", HFILL }
},
{ &hf_header_array[POS_P_PREFERRED_IDENTITY],
{ "P-Preferred-Identity", "sip.P-Preferred-Identity",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3325: P-Preferred-Identity Header", HFILL }
},
{ &hf_header_array[POS_P_PROFILE_KEY],
{ "P-Profile-Key", "sip.P-Profile-Key",
FT_STRING, BASE_NONE,NULL,0x0,
"P-Profile-Key Header", HFILL }
},
{ &hf_header_array[POS_P_REFUSED_URI_LST],
{ "P-Refused-URI-List", "sip.P-Refused-URI-List",
FT_STRING, BASE_NONE,NULL,0x0,
"P-Refused-URI-List Header", HFILL }
},
{ &hf_header_array[POS_P_SERVED_USER],
{ "P-Served-User", "sip.P-Served-User",
FT_STRING, BASE_NONE,NULL,0x0,
"P-Served-User", HFILL }
},
{ &hf_header_array[POS_P_USER_DATABASE],
{ "P-User-Database", "sip.P-User-Database",
FT_STRING, BASE_NONE,NULL,0x0,
"P-User-Database Header", HFILL }
},
{ &hf_header_array[POS_P_VISITED_NETWORK_ID],
{ "P-Visited-Network-ID", "sip.P-Visited-Network-ID",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3455: P-Visited-Network-ID Header", HFILL }
},
{ &hf_header_array[POS_PATH],
{ "Path", "sip.Path",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3327: Path Header", HFILL }
},
{ &hf_header_array[POS_PERMISSION_MISSING],
{ "Permission-Missing", "sip.Permission-Missing",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 5360: Permission Missing Header", HFILL }
},
{ &hf_header_array[POS_PRIORITY],
{ "Priority", "sip.Priority",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: Priority Header", HFILL }
},
{ &hf_header_array[POS_PRIV_ANSWER_MODE],
{ "Priv-Answer-mode", "sip.Priv-Answer-mode",
FT_STRING, BASE_NONE,NULL,0x0,
NULL, HFILL }
},
{ &hf_header_array[POS_PRIVACY],
{ "Privacy", "sip.Privacy",
FT_STRING, BASE_NONE,NULL,0x0,
"Privacy Header", HFILL }
},
{ &hf_header_array[POS_PROXY_AUTHENTICATE],
{ "Proxy-Authenticate", "sip.Proxy-Authenticate",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: Proxy-Authenticate Header", HFILL }
},
{ &hf_header_array[POS_PROXY_AUTHORIZATION],
{ "Proxy-Authorization", "sip.Proxy-Authorization",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: Proxy-Authorization Header", HFILL }
},
{ &hf_header_array[POS_PROXY_REQUIRE],
{ "Proxy-Require", "sip.Proxy-Require",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: Proxy-Require Header", HFILL }
},
{ &hf_header_array[POS_RACK],
{ "RAck", "sip.RAck",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3262: RAck Header", HFILL }
},
{ &hf_header_array[POS_REASON],
{ "Reason", "sip.Reason",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3326 Reason Header", HFILL }
},
{ &hf_header_array[POS_RECORD_ROUTE],
{ "Record-Route", "sip.Record-Route",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: Record-Route Header", HFILL }
},
{ &hf_header_array[POS_REFER_SUB],
{ "Refer-Sub", "sip.Refer-Sub",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 4488: Refer-Sub Header", HFILL }
},
{ &hf_header_array[POS_REFERED_BY],
{ "Refered By", "sip.Refered-by",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3892: Refered-by Header", HFILL }
},
{ &hf_header_array[POS_REJECT_CONTACT],
{ "Reject-Contact", "sip.Reject-Contact",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3841: Reject-Contact Header", HFILL }
},
{ &hf_header_array[POS_REPLACES],
{ "Replaces", "sip.Replaces",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3891: Replaces Header", HFILL }
},
{ &hf_header_array[POS_REPLY_TO],
{ "Reply-To", "sip.Reply-To",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: Reply-To Header", HFILL }
},
{ &hf_header_array[POS_REQUEST_DISPOSITION],
{ "Request-Disposition", "sip.Request-Disposition",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3841: Request-Disposition Header", HFILL }
},
{ &hf_header_array[POS_REQUIRE],
{ "Require", "sip.Require",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: Require Header", HFILL }
},
{ &hf_header_array[POS_RESOURCE_PRIORITY],
{ "Resource-Priority", "sip.Resource-Priority",
FT_STRING, BASE_NONE,NULL,0x0,
"Draft: Resource-Priority Header", HFILL }
},
{ &hf_header_array[POS_RETRY_AFTER],
{ "Retry-After", "sip.Retry-After",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: Retry-After Header", HFILL }
},
{ &hf_header_array[POS_ROUTE],
{ "Route", "sip.Route",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: Route Header", HFILL }
},
{ &hf_header_array[POS_RSEQ],
{ "RSeq", "sip.RSeq",
FT_UINT32, BASE_DEC,NULL,0x0,
"RFC 3262: RSeq Header", HFILL }
},
{ &hf_header_array[ POS_SECURITY_CLIENT],
{ "Security-Client", "sip.Security-Client",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3329 Security-Client Header", HFILL }
},
{ &hf_header_array[ POS_SECURITY_SERVER],
{ "Security-Server", "sip.Security-Server",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3329 Security-Server Header", HFILL }
},
{ &hf_header_array[ POS_SECURITY_VERIFY],
{ "Security-Verify", "sip.Security-Verify",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3329 Security-Verify Header", HFILL }
},
{ &hf_header_array[POS_SERVER],
{ "Server", "sip.Server",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: Server Header", HFILL }
},
{ &hf_header_array[POS_SERVICE_ROUTE],
{ "Service-Route", "sip.Service-Route",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3608: Service-Route Header", HFILL }
},
{ &hf_header_array[POS_SESSION_EXPIRES],
{ "Session-Expires", "sip.Session-Expires",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 4028: Session-Expires Header", HFILL }
},
{ &hf_header_array[POS_SIP_ETAG],
{ "ETag", "sip.ETag",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3903: SIP-ETag Header", HFILL }
},
{ &hf_header_array[POS_SIP_IF_MATCH],
{ "If_Match", "sip.If_Match",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3903: SIP-If-Match Header", HFILL }
},
{ &hf_header_array[POS_SUBJECT],
{ "Subject", "sip.Subject",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: Subject Header", HFILL }
},
{ &hf_header_array[POS_SUBSCRIPTION_STATE],
{ "Subscription-State", "sip.Subscription-State",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3265: Subscription-State Header", HFILL }
},
{ &hf_header_array[POS_SUPPORTED],
{ "Supported", "sip.Supported",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: Supported Header", HFILL }
},
{ &hf_header_array[POS_TARGET_DALOG],
{ "Target-Dialog", "sip.Target-Dialog",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 4538: Target-Dialog Header", HFILL }
},
{ &hf_header_array[POS_TIMESTAMP],
{ "Timestamp", "sip.Timestamp",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: Timestamp Header", HFILL }
},
{ &hf_header_array[POS_TO],
{ "To", "sip.To",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: To Header", HFILL }
},
{ &hf_header_array[POS_TRIGGER_CONSENT],
{ "Trigger-Consent", "sip.Trigger-Consent",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 5380: Trigger Consent", HFILL }
},
{ &hf_header_array[POS_UNSUPPORTED],
{ "Unsupported", "sip.Unsupported",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: Unsupported Header", HFILL }
},
{ &hf_header_array[POS_USER_AGENT],
{ "User-Agent", "sip.User-Agent",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: User-Agent Header", HFILL }
},
{ &hf_header_array[POS_VIA],
{ "Via", "sip.Via",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: Via Header", HFILL }
},
{ &hf_header_array[POS_WARNING],
{ "Warning", "sip.Warning",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: Warning Header", HFILL }
},
{ &hf_header_array[POS_WWW_AUTHENTICATE],
{ "WWW-Authenticate", "sip.WWW-Authenticate",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3261: WWW-Authenticate Header", HFILL }
},
{ &hf_header_array[POS_REFER_TO],
{ "Refer-To", "sip.Refer-To",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 3515: Refer-To Header", HFILL }
},
{ &hf_header_array[POS_HISTORY_INFO],
{ "History-Info", "sip.History-Info",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 4244: Request History Information", HFILL }
},
{ &hf_header_array[POS_IDENTITY],
{ "Identity", "sip.Identity",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 4474: Request Identity", HFILL }
},
{ &hf_header_array[POS_IDENTITY_INFO],
{ "Identity-info", "sip.Identity-info",
FT_STRING, BASE_NONE,NULL,0x0,
"RFC 4474: Request Identity-info", HFILL }
},
{ &hf_sip_resend,
{ "Resent Packet", "sip.resend",
FT_BOOLEAN, BASE_NONE, NULL, 0x0,
NULL, HFILL }
},
{ &hf_sip_original_frame,
{ "Suspected resend of frame", "sip.resend-original",
FT_FRAMENUM, BASE_NONE, NULL, 0x0,
"Original transmission of frame", HFILL}
},
{ &hf_sip_matching_request_frame,
{ "Request Frame", "sip.response-request",
FT_FRAMENUM, BASE_NONE, NULL, 0x0,
NULL, HFILL}
},
{ &hf_sip_response_time,
{ "Response Time (ms)", "sip.response-time",
FT_UINT32, BASE_DEC, NULL, 0x0,
"Response time since original request (in milliseconds)", HFILL}
},
{ &hf_sip_release_time,
{ "Release Time (ms)", "sip.release-time",
FT_UINT32, BASE_DEC, NULL, 0x0,
"release time since original BYE (in milliseconds)", HFILL}
},
{ &hf_sip_auth,
{ "Authentication", "sip.auth",
FT_STRING, BASE_NONE, NULL, 0x0,
"SIP Authentication", HFILL}
},
{ &hf_sip_auth_scheme,
{ "Authentication Scheme", "sip.auth.scheme",
FT_STRING, BASE_NONE, NULL, 0x0,
"SIP Authentication Scheme", HFILL}
},
{ &hf_sip_auth_digest_response,
{ "Digest Authentication Response", "sip.auth.digest.response",
FT_STRING, BASE_NONE, NULL, 0x0,
"SIP Digest Authentication Response Value", HFILL}
},
{ &hf_sip_auth_nc,
{ "Nonce Count", "sip.auth.nc",
FT_STRING, BASE_NONE, NULL, 0x0,
"SIP Authentication Nonce count", HFILL}
},
{ &hf_sip_auth_username,
{ "Username", "sip.auth.username",
FT_STRING, BASE_NONE, NULL, 0x0,
"SIP Authentication Username", HFILL}
},
{ &hf_sip_auth_realm,
{ "Realm", "sip.auth.realm",
FT_STRING, BASE_NONE, NULL, 0x0,
"SIP Authentication Realm", HFILL}
},
{ &hf_sip_auth_nonce,
{ "Nonce Value", "sip.auth.nonce",
FT_STRING, BASE_NONE, NULL, 0x0,
"SIP Authentication Nonce", HFILL}
},
{ &hf_sip_auth_algorithm,
{ "Algorithm", "sip.auth.algorithm",
FT_STRING, BASE_NONE, NULL, 0x0,
"SIP Authentication Algorithm", HFILL}
},
{ &hf_sip_auth_opaque,
{ "Opaque Value", "sip.auth.opaque",
FT_STRING, BASE_NONE, NULL, 0x0,
"SIP Authentication Opaque value", HFILL}
},
{ &hf_sip_auth_qop,
{ "QOP", "sip.auth.qop",
FT_STRING, BASE_NONE, NULL, 0x0,
"SIP Authentication QOP", HFILL}
},
{ &hf_sip_auth_cnonce,
{ "CNonce Value", "sip.auth.cnonce",
FT_STRING, BASE_NONE, NULL, 0x0,
"SIP Authentication Client Nonce", HFILL}
},
{ &hf_sip_auth_uri,
{ "Authentication URI", "sip.auth.uri",
FT_STRING, BASE_NONE, NULL, 0x0,
"SIP Authentication URI", HFILL}
},
{ &hf_sip_auth_domain,
{ "Authentication Domain", "sip.auth.domain",
FT_STRING, BASE_NONE, NULL, 0x0,
"SIP Authentication Domain", HFILL}
},
{ &hf_sip_auth_stale,
{ "Stale Flag", "sip.auth.stale",
FT_STRING, BASE_NONE, NULL, 0x0,
"SIP Authentication Stale Flag", HFILL}
},
{ &hf_sip_auth_auts,
{ "Authentication Token", "sip.auth.auts",
FT_STRING, BASE_NONE, NULL, 0x0,
"SIP Authentication Token", HFILL}
},
{ &hf_sip_auth_rspauth,
{ "Response auth", "sip.auth.rspauth",
FT_STRING, BASE_NONE, NULL, 0x0,
"SIP Authentication Response auth", HFILL}
},
{ &hf_sip_auth_nextnonce,
{ "Next Nonce", "sip.auth.nextnonce",
FT_STRING, BASE_NONE, NULL, 0x0,
"SIP Authentication Next Nonce", HFILL}
},
{ &hf_sip_auth_ik,
{ "Integrity Key", "sip.auth.ik",
FT_STRING, BASE_NONE, NULL, 0x0,
"SIP Authentication Integrity Key", HFILL}
},
{ &hf_sip_auth_ck,
{ "Cyphering Key", "sip.auth.ck",
FT_STRING, BASE_NONE, NULL, 0x0,
"SIP Authentication Cyphering Key", HFILL}
},
{ &hf_sip_cseq_seq_no,
{ "Sequence Number", "sip.CSeq.seq",
FT_UINT32, BASE_DEC, NULL, 0x0,
"CSeq header sequence number", HFILL}
},
{ &hf_sip_cseq_method,
{ "Method", "sip.CSeq.method",
FT_STRING, BASE_NONE, NULL, 0x0,
"CSeq header method", HFILL}
},
{ &hf_sip_via_transport,
{ "Transport", "sip.Via.transport",
FT_STRING, BASE_NONE, NULL, 0x0,
"Via header Transport", HFILL}
},
{ &hf_sip_via_sent_by_address,
{ "Sent-by Address", "sip.Via.sent-by.address",
FT_STRING, BASE_NONE, NULL, 0x0,
"Via header Sent-by Address", HFILL}
},
{ &hf_sip_via_sent_by_port,
{ "Sent-by port", "sip.Via.sent-by.port",
FT_UINT16, BASE_DEC, NULL, 0x0,
"Via header Sent-by Port", HFILL}
},
{ &hf_sip_via_branch,
{ "Branch", "sip.Via.branch",
FT_STRING, BASE_NONE, NULL, 0x0,
"SIP Via Branch", HFILL},
},
{ &hf_sip_via_maddr,
{ "Maddr", "sip.Via.maddr",
FT_STRING, BASE_NONE, NULL, 0x0,
"SIP Via Maddr", HFILL},
},
{ &hf_sip_via_rport,
{ "RPort", "sip.Via.rport",
FT_STRING, BASE_NONE, NULL, 0x0,
"SIP Via RPort", HFILL},
},
{ &hf_sip_via_received,
{ "Received", "sip.Via.received",
FT_STRING, BASE_NONE, NULL, 0x0,
"SIP Via Received", HFILL},
},
{ &hf_sip_via_ttl,
{ "TTL", "sip.Via.ttl",
FT_STRING, BASE_NONE, NULL, 0x0,
"SIP Via TTL", HFILL}
},
{ &hf_sip_via_comp,
{ "Comp", "sip.Via.comp",
FT_STRING, BASE_NONE, NULL, 0x0,
"SIP Via comp", HFILL}
},
{ &hf_sip_via_sigcomp_id,
{ "Sigcomp identifier", "sip.Via.sigcomp-id",
FT_STRING, BASE_NONE, NULL, 0x0,
"SIP Via sigcomp identifier", HFILL}
},
{ &hf_sip_rack_rseq_no,
{ "RSeq Sequence Number", "sip.RAck.RSeq.seq",
FT_UINT32, BASE_DEC, NULL, 0x0,
"RAck RSeq header sequence number (from prov response)", HFILL}
},
{ &hf_sip_rack_cseq_no,
{ "CSeq Sequence Number", "sip.RAck.CSeq.seq",
FT_UINT32, BASE_DEC, NULL, 0x0,
"RAck CSeq header sequence number (from prov response)", HFILL}
},
{ &hf_sip_rack_cseq_method,
{ "CSeq Method", "sip.RAck.CSeq.method",
FT_STRING, BASE_NONE, NULL, 0x0,
"RAck CSeq header method (from prov response)", HFILL}
},
{ &hf_sip_msg_body,
{ "Message Body", "sip.msg_body",
FT_NONE, BASE_NONE, NULL, 0x0,
"Message Body in SIP message", HFILL }
}};
/* raw_sip header field(s) */
static hf_register_info raw_hf[] = {
{ &hf_raw_sip_line,
{ "Raw SIP Line", "raw_sip.line",
FT_STRING, BASE_NONE,NULL,0x0,
NULL, HFILL }
}};
/* Setup protocol subtree array */
static gint *ett[] = {
&ett_sip,
&ett_sip_reqresp,
&ett_sip_hdr,
&ett_sip_ext_hdr,
&ett_sip_element,
&ett_sip_uri,
&ett_sip_contact_item,
&ett_sip_message_body,
&ett_sip_cseq,
&ett_sip_via,
&ett_sip_reason,
&ett_sip_rack,
&ett_sip_ruri,
&ett_sip_pai_uri,
&ett_sip_pmiss_uri,
&ett_sip_ppi_uri,
&ett_sip_tc_uri,
&ett_sip_to_uri,
&ett_sip_from_uri
};
static gint *ett_raw[] = {
&ett_raw_text,
};
module_t *sip_module;
/* Register the protocol name and description */
proto_sip = proto_register_protocol("Session Initiation Protocol",
"SIP", "sip");
proto_raw_sip = proto_register_protocol("Session Initiation Protocol (SIP as raw text)",
"Raw_SIP", "raw_sip");
new_register_dissector("sip", dissect_sip, proto_sip);
register_dissector("sip.tcp", dissect_sip_tcp, proto_sip);
/* Required function calls to register the header fields and subtrees used */
proto_register_field_array(proto_sip, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
proto_register_subtree_array(ett_raw, array_length(ett_raw));
/* Register raw_sip field(s) */
proto_register_field_array(proto_raw_sip, raw_hf, array_length(raw_hf));
sip_module = prefs_register_protocol(proto_sip, proto_reg_handoff_sip);
prefs_register_uint_preference(sip_module, "tcp.port",
"SIP TCP Port",
"SIP Server TCP Port",
10, &sip_tcp_port);
prefs_register_uint_preference(sip_module, "tls.port",
"SIP TLS Port",
"SIP Server TLS Port",
10, &sip_tls_port);
prefs_register_bool_preference(sip_module, "display_raw_text",
"Display raw text for SIP message",
"Specifies that the raw text of the "
"SIP message should be displayed "
"in addition to the dissection tree",
&global_sip_raw_text);
prefs_register_bool_preference(sip_module, "strict_sip_version",
"Enforce strict SIP version check (" SIP2_HDR ")",
"If enabled, only " SIP2_HDR " traffic will be dissected as SIP. "
"Disable it to allow SIP traffic with a different version "
"to be dissected as SIP.",
&strict_sip_version);
prefs_register_bool_preference(sip_module, "desegment_headers",
"Reassemble SIP headers spanning multiple TCP segments",
"Whether the SIP dissector should reassemble headers "
"of a request spanning multiple TCP segments. "
"To use this option, you must also enable "
"\"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
&sip_desegment_headers);
prefs_register_bool_preference(sip_module, "desegment_body",
"Reassemble SIP bodies spanning multiple TCP segments",
"Whether the SIP dissector should use the "
"\"Content-length:\" value, if present, to reassemble "
"the body of a request spanning multiple TCP segments, "
"and reassemble chunked data spanning multiple TCP segments. "
"To use this option, you must also enable "
"\"Allow subdissectors to reassemble TCP streams\" in the TCP protocol settings.",
&sip_desegment_body);
prefs_register_bool_preference(sip_module, "retrans_the_same_sport",
"Retransmissions always use the same source port",
"Whether retransmissions are detected coming from the same source port only.",
&sip_retrans_the_same_sport);
register_init_routine(&sip_init_protocol);
register_heur_dissector_list("sip", &heur_subdissector_list);
/* Register for tapping */
sip_tap = register_tap("sip");
ext_hdr_subdissector_table = register_dissector_table("sip.hdr", "SIP Extension header", FT_STRING, BASE_NONE);
}
void
proto_reg_handoff_sip(void)
{
static dissector_handle_t sip_tcp_handle;
static guint saved_sip_tcp_port;
static guint saved_sip_tls_port;
static gboolean sip_prefs_initialized = FALSE;
if (!sip_prefs_initialized) {
dissector_handle_t sip_handle;
sip_handle = find_dissector("sip");
sip_tcp_handle = find_dissector("sip.tcp");
sigcomp_handle = find_dissector("sigcomp");
/* SIP content type and internet media type used by other dissectors are the same */
media_type_dissector_table = find_dissector_table("media_type");
dissector_add("udp.port", UDP_PORT_SIP, sip_handle);
dissector_add_string("media_type", "message/sip", sip_handle);
heur_dissector_add("udp", dissect_sip_heur, proto_sip);
heur_dissector_add("tcp", dissect_sip_tcp_heur, proto_sip);
heur_dissector_add("sctp", dissect_sip_heur, proto_sip);
heur_dissector_add("stun", dissect_sip_heur, proto_sip);
sip_prefs_initialized = TRUE;
} else {
dissector_delete("tcp.port", saved_sip_tcp_port, sip_tcp_handle);
ssl_dissector_delete(saved_sip_tls_port, "sip.tcp", TRUE);
}
/* Set our port number for future use */
saved_sip_tcp_port = sip_tcp_port;
saved_sip_tls_port = sip_tls_port;
dissector_add("tcp.port", saved_sip_tcp_port, sip_tcp_handle);
ssl_dissector_add(saved_sip_tls_port, "sip.tcp", TRUE);
}