static int http_eo_tap = -1;
static int proto_http = -1;
+static int proto_http2 = -1;
+static int proto_ssdp = -1;
static int hf_http_notification = -1;
static int hf_http_response = -1;
static int hf_http_request = -1;
+static int hf_http_response_number = -1;
+static int hf_http_request_number = -1;
static int hf_http_response_line = -1;
static int hf_http_request_line = -1;
static int hf_http_basic = -1;
static int hf_http_prev_request_in = -1;
static int hf_http_prev_response_in = -1;
static int hf_http_time = -1;
+static int hf_http_chunk_size = -1;
+static int hf_http_chunk_boundary = -1;
static int hf_http_chunked_trailer_part = -1;
+static int hf_http_unknown_header = -1;
static gint ett_http = -1;
static gint ett_http_ntlmssp = -1;
static dissector_handle_t media_handle;
static dissector_handle_t websocket_handle;
static dissector_handle_t http2_handle;
+static dissector_handle_t sstp_handle;
+static dissector_handle_t ntlmssp_handle;
+static dissector_handle_t gssapi_handle;
/* Stuff for generation/handling of fields for custom HTTP headers */
typedef struct _header_field_t {
static GHashTable* header_fields_hash = NULL;
-static void
+static gboolean
header_fields_update_cb(void *r, char **err)
{
header_field_t *rec = (header_field_t *)r;
if (rec->header_name == NULL) {
*err = g_strdup("Header name can't be empty");
- return;
+ return FALSE;
}
g_strstrip(rec->header_name);
if (rec->header_name[0] == 0) {
*err = g_strdup("Header name can't be empty");
- return;
+ return FALSE;
}
/* Check for invalid characters (to avoid asserting out when
c = proto_check_field_name(rec->header_name);
if (c) {
*err = g_strdup_printf("Header name can't contain '%c'", c);
- return;
+ return FALSE;
}
*err = NULL;
+ return TRUE;
}
static void *
#define UPGRADE_WEBSOCKET 1
#define UPGRADE_HTTP2 2
+#define UPGRADE_SSTP 3
static range_t *global_http_tcp_range = NULL;
static range_t *global_http_ssl_range = NULL;
static dissector_table_t media_type_subdissector_table;
static heur_dissector_list_t heur_subdissector_list;
-static dissector_handle_t ntlmssp_handle;
-static dissector_handle_t gssapi_handle;
-
/* --- HTTP Status Codes */
/* Note: The reference for uncommented entries is RFC 2616 */
static const value_string vals_status_code[] = {
static http_conv_t *
-get_http_conversation_data(packet_info *pinfo)
+get_http_conversation_data(packet_info *pinfo, conversation_t **conversation)
{
- conversation_t *conversation;
http_conv_t *conv_data;
- conversation = find_or_create_conversation(pinfo);
+ *conversation = find_or_create_conversation(pinfo);
/* Retrieve information from conversation
* or add it if it isn't there yet
*/
- conv_data = (http_conv_t *)conversation_get_proto_data(conversation, proto_http);
+ conv_data = (http_conv_t *)conversation_get_proto_data(*conversation, proto_http);
if(!conv_data) {
/* Setup the conversation structure itself */
conv_data = (http_conv_t *)wmem_alloc0(wmem_file_scope(), sizeof(http_conv_t));
- conversation_add_proto_data(conversation, proto_http,
+ conversation_add_proto_data(*conversation, proto_http,
conv_data);
}
static int
dissect_http_message(tvbuff_t *tvb, int offset, packet_info *pinfo,
- proto_tree *tree, http_conv_t *conv_data)
+ proto_tree *tree, http_conv_t *conv_data, const char* proto_tag, int proto)
{
- const char *proto_tag;
proto_tree *http_tree = NULL;
proto_item *ti = NULL;
proto_item *hidden_item;
stat_info->request_uri = NULL;
stat_info->http_host = NULL;
- switch (pinfo->match_uint) {
-
- case TCP_PORT_SSDP: /* TCP_PORT_SSDP = UDP_PORT_SSDP */
- proto_tag = "SSDP";
- break;
-
- default:
- proto_tag = "HTTP";
- break;
- }
-
orig_offset = offset;
/*
headers.transfer_encoding = NULL; /* transfer encoding not known yet */
headers.upgrade = 0; /* assume we're not upgrading */
saw_req_resp_or_header = FALSE; /* haven't seen anything yet */
- while (tvb_reported_length_remaining(tvb, offset) > 0) {
+ while (tvb_offset_exists(tvb, offset)) {
/*
* Find the end of the line.
* XXX - what if we don't find it because the packet
}
if ((tree) && (http_tree == NULL)) {
- ti = proto_tree_add_item(tree, proto_http, tvb, orig_offset, -1, ENC_NA);
+ ti = proto_tree_add_item(tree, proto, tvb, orig_offset, -1, ENC_NA);
http_tree = proto_item_add_subtree(ti, ett_http);
if(leading_crlf){
proto_tree_add_expert(http_tree, pinfo, &ei_http_leading_crlf, tvb, orig_offset-2, 2);
if (curr) {
nstime_t delta;
- pi = proto_tree_add_text(http_tree, tvb, 0, 0, "HTTP response %u/%u", curr->number, conv_data->req_res_num);
+ pi = proto_tree_add_uint_format(http_tree, hf_http_response_number, tvb, 0, 0, curr->number, "HTTP response %u/%u", curr->number, conv_data->req_res_num);
PROTO_ITEM_SET_GENERATED(pi);
if (! nstime_is_unset(&(curr->req_ts))) {
PROTO_ITEM_SET_HIDDEN(hidden_item);
if (curr) {
- pi = proto_tree_add_text(http_tree, tvb, 0, 0, "HTTP request %u/%u", curr->number, conv_data->req_res_num);
+ pi = proto_tree_add_uint_format(http_tree, hf_http_request_number, tvb, 0, 0, curr->number, "HTTP request %u/%u", curr->number, conv_data->req_res_num);
PROTO_ITEM_SET_GENERATED(pi);
}
if (prev && prev->req_framenum) {
offset += datalen;
}
+ if (http_type == HTTP_RESPONSE && conv_data->upgrade == UPGRADE_SSTP) {
+ conv_data->startframe = pinfo->fd->num + 1;
+ headers.upgrade = conv_data->upgrade;
+ }
+
if (http_type == HTTP_RESPONSE && pinfo->desegment_offset<=0 && pinfo->desegment_len<=0) {
conv_data->upgrade = headers.upgrade;
conv_data->startframe = pinfo->fd->num + 1;
- SE_COPY_ADDRESS(&conv_data->server_addr, &pinfo->src);
+ copy_address_wmem(wmem_file_scope(), &conv_data->server_addr, &pinfo->src);
conv_data->server_port = pinfo->srcport;
}
}
-#if 0 /* XXX: Replaced by code creating the "Dechunked" tvb O(N) rather tan O(N^2) */
+#if 0 /* XXX: Replaced by code creating the "Dechunked" tvb O(N) rather than O(N^2) */
/*
* Dissect the http data chunks and add them to the tree.
*/
ett_http_chunked_response, NULL, "HTTP chunked response");
while (datalen > 0) {
- proto_item *chunk_ti = NULL;
+ proto_item *chunk_ti = NULL, *chuck_size_item;
proto_tree *chunk_subtree = NULL;
tvbuff_t *data_tvb = NULL; /* */
gchar *c = NULL;
ett_http_chunk_data, NULL, "Data chunk (%u octets)", chunk_size);
}
- proto_tree_add_text(chunk_subtree, tvb, offset,
- chunk_offset - offset, "Chunk size: %u octets",
- chunk_size);
-
- data_tvb = tvb_new_subset_length(tvb, chunk_offset, chunk_size);
-
+ chuck_size_item = proto_tree_add_uint_format_value(chunk_subtree, hf_http_chunk_size, tvb, offset,
+ 1, chunk_size, "%u octets", chunk_size);
+ proto_item_set_len(chuck_size_item, chunk_offset - offset);
/*
- * XXX - just use "proto_tree_add_text()"?
- * This means that, in TShark, you get
- * the entire chunk dumped out in hex,
- * in addition to whatever dissection is
- * done on the reassembled data.
+ * XXX - just add the chunk's data as an item?
+ *
+ * Using the data dissector means that, in
+ * TShark, you get the entire chunk dumped
+ * out in hex, in addition to whatever
+ * dissection is done on the reassembled data.
*/
+ data_tvb = tvb_new_subset_length(tvb, chunk_offset, chunk_size);
call_dissector(data_handle, data_tvb, pinfo,
chunk_subtree);
- proto_tree_add_text(chunk_subtree, tvb, chunk_offset +
- chunk_size, 2, "Chunk boundary");
+ proto_tree_add_item(chunk_subtree, hf_http_chunked_boundary, tvb,
+ chunk_offset + chunk_size, 2, ENC_NA);
}
chunks_decoded++;
if (subtree) {
proto_tree *chunk_subtree;
+ proto_item *chunk_size_item;
if(chunk_size == 0) {
chunk_subtree = proto_tree_add_subtree(subtree, tvb,
"Data chunk (%u octets)", chunk_size);
}
- proto_tree_add_text(chunk_subtree, tvb, offset,
- chunk_offset - offset, "Chunk size: %u octets",
- chunk_size);
+ chunk_size_item = proto_tree_add_uint_format_value(chunk_subtree, hf_http_chunk_size, tvb, offset,
+ 1, chunk_size, "%u octets", chunk_size);
+ proto_item_set_len(chunk_size_item, chunk_offset - offset);
/* last-chunk does not have chunk-data CRLF. */
if (chunk_size > 0) {
- data_tvb = tvb_new_subset(tvb, chunk_offset, chunk_size, datalen);
-
/*
- * XXX - just use "proto_tree_add_text()"?
- * This means that, in TShark, you get
- * the entire chunk dumped out in hex,
- * in addition to whatever dissection is
- * done on the reassembled data.
+ * XXX - just add the chunk's data as an item?
+ *
+ * Using the data dissector means that, in
+ * TShark, you get the entire chunk dumped
+ * out in hex, in addition to whatever
+ * dissection is done on the reassembled data.
*/
+ data_tvb = tvb_new_subset_length(tvb, chunk_offset, chunk_size);
call_dissector(data_handle, data_tvb, pinfo,
chunk_subtree);
- proto_tree_add_text(chunk_subtree, tvb, chunk_offset +
- chunk_size, 2, "Chunk boundary");
+ proto_tree_add_item(chunk_subtree, hf_http_chunk_boundary, tvb,
+ chunk_offset + chunk_size, 2, ENC_NA);
}
}
* conversation (e.g., one we detected heuristically or via Decode-As) call the data
* dissector directly.
*/
- if (value_is_in_range(http_tcp_range, uri_port) || (conv && conv->dissector_handle == http_handle)) {
+ if (value_is_in_range(http_tcp_range, uri_port) || (conv && conversation_get_dissector(conv, pinfo->fd->num) == http_handle)) {
call_dissector(data_handle, tvb, pinfo, tree);
} else {
/* set pinfo->{src/dst port} and call the TCP sub-dissector lookup */
strncmp(data, "BMOVE", indx) == 0 ||
strncmp(data, "MKCOL", indx) == 0 ||
strncmp(data, "TRACE", indx) == 0 ||
+ strncmp(data, "PATCH", indx) == 0 || /* RFC 5789 */
strncmp(data, "LABEL", indx) == 0 || /* RFC 3253 8.2 */
strncmp(data, "MERGE", indx) == 0) { /* RFC 3253 11.2 */
*type = HTTP_REQUEST;
if (strncmp(data, "BASELINE-CONTROL", indx) == 0) { /* RFC 3253 12.6 */
*type = HTTP_REQUEST;
isHttpRequestOrReply = TRUE;
+ } else if (strncmp(data, "SSTP_DUPLEX_POST", indx) == 0) { /* MS SSTP */
+ *type = HTTP_REQUEST;
+ isHttpRequestOrReply = TRUE;
+ conv_data->upgrade = UPGRADE_SSTP;
}
break;
if (header_fields_hash && hf) {
guint hf_size = g_hash_table_size (header_fields_hash);
- /* Unregister all fields */
+ /* Deregister all fields */
for (i = 0; i < hf_size; i++) {
- proto_unregister_field (proto_http, *(hf[i].p_id));
+ proto_deregister_field (proto_http, *(hf[i].p_id));
g_free (hf[i].p_id);
}
g_hash_table_destroy (header_fields_hash);
* has value_len bytes in it.
*/
value_len = line_end_offset - value_offset;
- value = wmem_strndup(wmem_packet_scope(), &line[value_offset - offset], value_len);
+ value = (char *)wmem_alloc(wmem_packet_scope(), value_len+1);
+ memcpy(value, &line[value_offset - offset], value_len);
+ value[value_len] = '\0';
if (hf_index == -1) {
/*
proto_item_set_text(it, "%s",
format_text(line, len));
} else {
- proto_tree_add_text(tree, tvb, offset,
- len, "%s", format_text(line, len));
+ gchar* str = format_text(line, len);
+ proto_tree_add_string_format(tree, hf_http_unknown_header, tvb, offset,
+ len, str, "%s", str);
}
} else {
break;
case HDR_UPGRADE:
- if (g_ascii_strncasecmp(value, "WebSocket", value_len) == 0){
+ if (g_ascii_strncasecmp(value, "WebSocket", value_len) == 0) {
eh_ptr->upgrade = UPGRADE_WEBSOCKET;
}
/* Check if upgrade is HTTP 2.0 (Start with h2...) */
http_conv_t *conv_data;
int offset = 0;
int len;
+ conversation_t *conversation;
+ dissector_handle_t next_handle = NULL;
+
+ conv_data = get_http_conversation_data(pinfo, &conversation);
+ /* Call HTTP2 dissector directly when detected via heuristics, but not
+ * when it was upgraded (the conversation started with HTTP). */
+ if (conversation_get_proto_data(conversation, proto_http2) &&
+ conv_data->upgrade != UPGRADE_HTTP2) {
+ if (pinfo->can_desegment > 0)
+ pinfo->can_desegment++;
+ return call_dissector_only(http2_handle, tvb, pinfo, tree, NULL);
+ }
/*
* Check if this is proxied connection and if so, hand of dissection to the
* payload-dissector.
* Response code 200 means "OK" and strncmp() == 0 means the strings match exactly */
- conv_data = get_http_conversation_data(pinfo);
if(pinfo->fd->num >= conv_data->startframe &&
conv_data->response_code == 200 &&
conv_data->request_method &&
} else {
while (tvb_reported_length_remaining(tvb, offset) > 0) {
if (conv_data->upgrade == UPGRADE_WEBSOCKET && pinfo->fd->num >= conv_data->startframe) {
- call_dissector_only(websocket_handle, tvb_new_subset_remaining(tvb, offset), pinfo, tree, NULL);
- break;
+ next_handle = websocket_handle;
}
if (conv_data->upgrade == UPGRADE_HTTP2 && pinfo->fd->num >= conv_data->startframe) {
- call_dissector_only(http2_handle, tvb_new_subset_remaining(tvb, offset), pinfo, tree, NULL);
+ next_handle = http2_handle;
+ }
+ if (conv_data->upgrade == UPGRADE_SSTP && conv_data->response_code == 200 && pinfo->fd->num >= conv_data->startframe) {
+ next_handle = sstp_handle;
+ }
+ if (next_handle) {
+ /* Increase pinfo->can_desegment because we are traversing
+ * http and want to preserve desegmentation functionality for
+ * the proxied protocol
+ */
+ if (pinfo->can_desegment > 0)
+ pinfo->can_desegment++;
+
+ call_dissector_only(next_handle, tvb_new_subset_remaining(tvb, offset), pinfo, tree, NULL);
break;
}
- len = dissect_http_message(tvb, offset, pinfo, tree, conv_data);
+ len = dissect_http_message(tvb, offset, pinfo, tree, conv_data, "HTTP", proto_http);
if (len == -1)
break;
offset += len;
return FALSE;
}
-static void
-dissect_http_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+static int
+dissect_ssdp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
{
+ conversation_t *conversation;
http_conv_t *conv_data;
- conv_data = get_http_conversation_data(pinfo);
- dissect_http_message(tvb, 0, pinfo, tree, conv_data);
+ conv_data = get_http_conversation_data(pinfo, &conversation);
+ dissect_http_message(tvb, 0, pinfo, tree, conv_data, "SSDP", proto_ssdp);
+ return tvb_captured_length(tvb);
}
-
static void
range_delete_http_ssl_callback(guint32 port) {
- ssl_dissector_delete(port, "http", TRUE);
+ ssl_dissector_delete(port, http_handle);
}
static void
range_add_http_ssl_callback(guint32 port) {
- ssl_dissector_add(port, "http", TRUE);
+ ssl_dissector_add(port, http_handle);
}
static void reinit_http(void) {
{ "Request", "http.request",
FT_BOOLEAN, BASE_NONE, NULL, 0x0,
"TRUE if HTTP request", HFILL }},
+ { &hf_http_response_number,
+ { "Response number", "http.response_number",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }},
+ { &hf_http_request_number,
+ { "Request number", "http.request_number",
+ FT_UINT32, BASE_DEC, NULL, 0x0,
+ NULL, HFILL }},
{ &hf_http_basic,
{ "Credentials", "http.authbasic",
FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }},
{ &hf_http_time,
{ "Time since request", "http.time",
FT_RELATIVE_TIME, BASE_NONE, NULL, 0,
- "Time since the request was send", HFILL }},
+ "Time since the request was sent", HFILL }},
{ &hf_http_chunked_trailer_part,
{ "trailer-part", "http.chunked_trailer_part",
FT_STRING, BASE_NONE, NULL, 0,
"Optional trailer in a chunked body", HFILL }},
+ { &hf_http_chunk_boundary,
+ { "Chunk boundary", "http.chunk_boundary",
+ FT_BYTES, BASE_NONE, NULL, 0,
+ NULL, HFILL }},
+ { &hf_http_chunk_size,
+ { "Chunk size", "http.chunk_size",
+ FT_UINT32, BASE_DEC, NULL, 0,
+ NULL, HFILL }},
+ { &hf_http_unknown_header,
+ { "Unknown header", "http.unknown_header",
+ FT_STRING, BASE_NONE, NULL, 0,
+ NULL, HFILL }},
};
static gint *ett[] = {
&ett_http,
expert_module_t* expert_http;
uat_t* headers_uat;
- proto_http = proto_register_protocol("Hypertext Transfer Protocol",
- "HTTP", "http");
+ proto_http = proto_register_protocol("Hypertext Transfer Protocol", "HTTP", "http");
+ proto_ssdp = proto_register_protocol("Simple Service Discovery Protocol", "SSDP", "ssdp");
+
proto_register_field_array(proto_http, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
expert_http = expert_register_protocol(proto_http);
"SSL/TLS Ports range",
&global_http_ssl_range, 65535);
/* UAT */
- headers_uat = uat_new("Custom HTTP headers fields Table",
+ headers_uat = uat_new("Custom HTTP Header Fields",
sizeof(header_field_t),
"custom_http_header_fields",
TRUE,
custom_header_uat_fields
);
- prefs_register_uat_preference(http_module, "custom_http_header_fields", "Custom HTTP headers fields",
+ prefs_register_uat_preference(http_module, "custom_http_header_fields", "Custom HTTP header fields",
"A table to define custom HTTP header for which fields can be setup and used for filtering/data extraction etc.",
headers_uat);
* HTTP on a specific non-HTTP port.
*/
port_subdissector_table = register_dissector_table("http.port",
- "TCP port for protocols using HTTP", FT_UINT16, BASE_DEC);
+ "TCP port for protocols using HTTP", FT_UINT16, BASE_DEC, DISSECTOR_TABLE_NOT_ALLOW_DUPLICATE);
/*
* Dissectors can register themselves in this table.
*/
media_type_subdissector_table =
register_dissector_table("media_type",
- "Internet media type", FT_STRING, BASE_NONE);
+ "Internet media type", FT_STRING, BASE_NONE, DISSECTOR_TABLE_ALLOW_DUPLICATE);
/*
* Heuristic dissectors SHOULD register themselves in
void
proto_reg_handoff_http(void)
{
- dissector_handle_t http_udp_handle;
+ dissector_handle_t ssdp_handle;
data_handle = find_dissector("data");
media_handle = find_dissector("media");
* XXX - is there anything to dissect in the body of an SSDP
* request or reply? I.e., should there be an SSDP dissector?
*/
- http_udp_handle = create_dissector_handle(dissect_http_udp, proto_http);
- dissector_add_uint("udp.port", UDP_PORT_SSDP, http_udp_handle);
+ ssdp_handle = new_create_dissector_handle(dissect_ssdp, proto_ssdp);
+ dissector_add_uint("udp.port", UDP_PORT_SSDP, ssdp_handle);
ntlmssp_handle = find_dissector("ntlmssp");
gssapi_handle = find_dissector("gssapi");
+ sstp_handle = find_dissector("sstp");
stats_tree_register("http", "http", "HTTP/Packet Counter", 0, http_stats_tree_packet, http_stats_tree_init, NULL );
stats_tree_register("http", "http_req", "HTTP/Requests", 0, http_req_stats_tree_packet, http_req_stats_tree_init, NULL );
static gint proto_message_http = -1;
static gint ett_message_http = -1;
-static void
-dissect_message_http(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+static int
+dissect_message_http(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
{
proto_tree *subtree;
proto_item *ti;
ti = proto_tree_add_item(tree, proto_message_http,
tvb, 0, -1, ENC_NA);
subtree = proto_item_add_subtree(ti, ett_message_http);
- while (tvb_reported_length_remaining(tvb, offset) > 0) {
+ while (tvb_offset_exists(tvb, offset)) {
len = tvb_find_line_end(tvb, offset,
tvb_ensure_captured_length_remaining(tvb, offset),
&next_offset, FALSE);
if (len == -1)
break;
- proto_tree_add_text(subtree, tvb, offset, next_offset - offset,
- "%s", tvb_format_text(tvb, offset, len));
+ proto_tree_add_format_text(subtree, tvb, offset, len);
offset = next_offset;
}
}
+ return tvb_captured_length(tvb);
}
void
{
dissector_handle_t message_http_handle;
- message_http_handle = create_dissector_handle(dissect_message_http,
+ message_http_handle = new_create_dissector_handle(dissect_message_http,
proto_message_http);
dissector_add_string("media_type", "message/http", message_http_handle);
- heur_dissector_add("tcp", dissect_http_heur_tcp, proto_http);
+ heur_dissector_add("tcp", dissect_http_heur_tcp, "HTTP over TCP", "http_tcp", proto_http, HEURISTIC_ENABLE);
+ proto_http2 = proto_get_id_by_filter_name("http2");
reinit_http();
}