diff options
-rw-r--r-- | docbook/release-notes.asciidoc | 2 | ||||
-rw-r--r-- | epan/dissectors/packet-http.c | 194 | ||||
-rw-r--r-- | epan/dissectors/packet-http.h | 3 | ||||
-rw-r--r-- | ui/qt/main_window.h | 2 | ||||
-rw-r--r-- | ui/qt/main_window.ui | 8 | ||||
-rw-r--r-- | ui/qt/main_window_slots.cpp | 4 |
6 files changed, 177 insertions, 36 deletions
diff --git a/docbook/release-notes.asciidoc b/docbook/release-notes.asciidoc index 50c0196d06..1b9e4fb57c 100644 --- a/docbook/release-notes.asciidoc +++ b/docbook/release-notes.asciidoc @@ -42,7 +42,7 @@ Dumpcap might not quit if Wireshark or TShark crashes. The following features are new (or have been significantly updated) since version 2.5.0: -* HTTP Referer statistics are now supported. +* HTTP Request sequences are now supported. * Wireshark now supports MaxMind DB files. Support for GeoIP and GeoLite Legacy databases has been removed. * The Windows packages are now built using Microsoft Visual Studio 2017. diff --git a/epan/dissectors/packet-http.c b/epan/dissectors/packet-http.c index 83b2a7b957..85bba25eff 100644 --- a/epan/dissectors/packet-http.c +++ b/epan/dissectors/packet-http.c @@ -666,7 +666,7 @@ prior referers are 'ticked' as well, so that one can easily see the breakdown. /* Root node for all referer statistics */ static int st_node_requests_by_referer = -1; /* Referer statistics root node's text */ -static const gchar *st_str_requests_by_referer = "HTTP Requests by HTTP Referer"; +static const gchar *st_str_request_sequences = "HTTP Request Sequences"; /* Mapping of URIs to the most-recently seen node id */ static wmem_map_t* refstats_uri_to_node_id_hash = NULL; @@ -676,9 +676,9 @@ static wmem_map_t* refstats_node_id_to_uri_hash = NULL; static wmem_map_t* refstats_node_id_to_parent_node_id_hash = NULL; -/* HTTP/Referers stats init function */ +/* HTTP/Request Sequences stats init function */ static void -http_ref_stats_tree_init(stats_tree* st) +http_seq_stats_tree_init(stats_tree* st) { gint root_node_id = 0; gpointer root_node_id_p = GINT_TO_POINTER(root_node_id); @@ -690,9 +690,9 @@ http_ref_stats_tree_init(stats_tree* st) refstats_uri_to_node_id_hash = wmem_map_new(wmem_file_scope(), wmem_str_hash, g_str_equal); /* Add the root node and its mappings */ - st_node_requests_by_referer = stats_tree_create_node(st, st_str_requests_by_referer, root_node_id, TRUE); + st_node_requests_by_referer = stats_tree_create_node(st, st_str_request_sequences, root_node_id, TRUE); node_id_p = GINT_TO_POINTER(st_node_requests_by_referer); - uri = wmem_strdup(wmem_file_scope(), st_str_requests_by_referer); + uri = wmem_strdup(wmem_file_scope(), st_str_request_sequences); wmem_map_insert(refstats_uri_to_node_id_hash, uri, node_id_p); wmem_map_insert(refstats_node_id_to_uri_hash, node_id_p, uri); @@ -700,7 +700,7 @@ http_ref_stats_tree_init(stats_tree* st) } static gint -http_ref_stats_tick_referer(stats_tree* st, const http_info_value_t* v) +http_seq_stats_tick_referer(stats_tree* st, const gchar* arg_referer_uri) { gint root_node_id = st_node_requests_by_referer; gpointer root_node_id_p = GINT_TO_POINTER(st_node_requests_by_referer); @@ -712,13 +712,13 @@ http_ref_stats_tick_referer(stats_tree* st, const http_info_value_t* v) /* Tick the referer's URI */ /* Does the node exist? */ - if (!wmem_map_lookup_extended(refstats_uri_to_node_id_hash, v->referer_uri, NULL, &referer_node_id_p)) { + if (!wmem_map_lookup_extended(refstats_uri_to_node_id_hash, arg_referer_uri, NULL, &referer_node_id_p)) { /* The node for the referer didn't already exist, create the mappings */ - referer_node_id = tick_stat_node(st, v->referer_uri, root_node_id, TRUE); + referer_node_id = tick_stat_node(st, arg_referer_uri, root_node_id, TRUE); referer_node_id_p = GINT_TO_POINTER(referer_node_id); referer_parent_node_id_p = root_node_id_p; - referer_uri = wmem_strdup(wmem_file_scope(), v->referer_uri); + referer_uri = wmem_strdup(wmem_file_scope(), arg_referer_uri); wmem_map_insert(refstats_uri_to_node_id_hash, referer_uri, referer_node_id_p); wmem_map_insert(refstats_node_id_to_uri_hash, referer_node_id_p, referer_uri); wmem_map_insert(refstats_node_id_to_parent_node_id_hash, referer_node_id_p, referer_parent_node_id_p); @@ -726,20 +726,20 @@ http_ref_stats_tick_referer(stats_tree* st, const http_info_value_t* v) /* The node for the referer already exists, tick it */ referer_parent_node_id_p = wmem_map_lookup(refstats_node_id_to_parent_node_id_hash, referer_node_id_p); referer_parent_node_id = GPOINTER_TO_INT(referer_parent_node_id_p); - referer_node_id = tick_stat_node(st, v->referer_uri, referer_parent_node_id, TRUE); + referer_node_id = tick_stat_node(st, arg_referer_uri, referer_parent_node_id, TRUE); } return referer_node_id; } static void -http_ref_stats_tick_request(stats_tree* st, const http_info_value_t* v, gint referer_node_id) +http_seq_stats_tick_request(stats_tree* st, const gchar* arg_full_uri, gint referer_node_id) { gpointer referer_node_id_p = GINT_TO_POINTER(referer_node_id); gint node_id; gpointer node_id_p; gchar *uri; - node_id = tick_stat_node(st, v->full_uri, referer_node_id, TRUE); + node_id = tick_stat_node(st, arg_full_uri, referer_node_id, TRUE); node_id_p = GINT_TO_POINTER(node_id); /* Update the mappings. Even if the URI was already seen, the URI->node mapping may need to be updated */ @@ -748,7 +748,7 @@ http_ref_stats_tick_request(stats_tree* st, const http_info_value_t* v, gint ref uri = (gchar *) wmem_map_lookup(refstats_node_id_to_uri_hash, node_id_p); if (!uri) { /* node not found, add mappings for the node and uri */ - uri = wmem_strdup(wmem_file_scope(), v->full_uri); + uri = wmem_strdup(wmem_file_scope(), arg_full_uri); wmem_map_insert(refstats_uri_to_node_id_hash, uri, node_id_p); wmem_map_insert(refstats_node_id_to_uri_hash, node_id_p, uri); @@ -759,26 +759,155 @@ http_ref_stats_tick_request(stats_tree* st, const http_info_value_t* v, gint ref } } -/* HTTP/Referers stats packet function */ -static int -http_ref_stats_tree_packet(stats_tree* st, packet_info* pinfo _U_, epan_dissect_t* edt _U_, const void* p) +static gchar* +determine_http_location_target(const gchar *base_url, const gchar * location_url) { - const http_info_value_t* v = (const http_info_value_t*)p; + /* Resolving a base URI + relative URI to an absolute URI ("Relative Resolution") + is complicated. Because of that, we take shortcuts that may result in + inaccurate results, but is also significantly simpler. + It would be best to use an external library to do this for us. + For reference, the RFC is located at https://tools.ietf.org/html/rfc3986#section-5.4 + + Returns NULL if the resolution fails + */ + gchar *final_target; + + /* base_url must be an absolute URL.*/ + if (strstr(base_url, "://") == NULL){ + return NULL; + } - gint referer_node_id; + /* Empty Location */ + if (location_url[0] == '\0') { + final_target = wmem_strdup(wmem_packet_scope(), base_url); + return final_target; + } + /* Protocol Relative */ + else if (g_str_has_prefix(location_url, "//") ) { + char *base_scheme = g_uri_parse_scheme(base_url); + if (base_scheme == NULL) { + return NULL; + } + final_target = wmem_strdup_printf(wmem_packet_scope(), "%s:%s", base_scheme, location_url); + g_free(base_scheme); + return final_target; + } + /* Absolute URL*/ + else if (strstr(location_url, "://") != NULL) { + final_target = wmem_strdup(wmem_packet_scope(), location_url); + return final_target; + } + /* Relative */ + else { + gchar *start_fragment = strstr(base_url, "#"); + gchar *start_query = NULL; + gchar *base_url_no_fragment = NULL; + gchar *base_url_no_query = NULL; + + /* Strip off the fragment (which should never be present)*/ + if (start_fragment == NULL) { + base_url_no_fragment = wmem_strdup(wmem_packet_scope(), base_url); + } + else { + base_url_no_fragment = wmem_strndup(wmem_packet_scope(), base_url, start_fragment - base_url); + } - gint parent_node_id; - gpointer parent_node_id_p; - gpointer current_node_id_p; + /* Strip off the query (Queries are stripped from all relative URIs) */ + start_query = strstr(base_url_no_fragment, "?"); + if (start_query == NULL) { + base_url_no_query = wmem_strdup(wmem_packet_scope(), base_url_no_fragment); + } + else { + base_url_no_query = wmem_strndup(wmem_packet_scope(), base_url_no_fragment, start_query - base_url_no_fragment); + } - gchar *uri = NULL; + /* A leading question mark (?) means to replace the old query with the new*/ + if (g_str_has_prefix(location_url, "?")) { + final_target = wmem_strdup_printf(wmem_packet_scope(), "%s%s", base_url_no_query, location_url); + return final_target; + } + /* A leading slash means to put the location after the netloc */ + else if (g_str_has_prefix(location_url, "/")) { + gchar *scheme_end = strstr(base_url_no_query, "://") + 3; + gchar *netloc_end; + gint netloc_length; + if (scheme_end[0] == '\0') { + return NULL; + } + netloc_end = strstr(scheme_end, "/"); + if (netloc_end == NULL) { + return NULL; + } + netloc_length = (gint) (netloc_end - base_url_no_query); + final_target = wmem_strdup_printf(wmem_packet_scope(), "%.*s%s", netloc_length, base_url_no_query, location_url); + return final_target; + } + /* Otherwise, it replaces the last element in the URI */ + else { + gchar *scheme_end = strstr(base_url_no_query, "://") + 3; + gchar *end_of_path = g_strrstr(scheme_end, "/"); + if (end_of_path != NULL) { + gint base_through_path = (gint) (end_of_path - base_url_no_query); + final_target = wmem_strdup_printf(wmem_packet_scope(), "%.*s/%s", base_through_path, base_url_no_query, location_url); + } + else { + final_target = wmem_strdup_printf(wmem_packet_scope(), "%s/%s", base_url_no_query, location_url); + } + + return final_target; + } + } + return NULL; +} + +/* HTTP/Request Sequences stats packet function */ +static int +http_seq_stats_tree_packet(stats_tree* st, packet_info* pinfo _U_, epan_dissect_t* edt _U_, const void* p) +{ + const http_info_value_t* v = (const http_info_value_t*)p; + + /* Track HTTP Redirects */ + if (v->location_target && v->location_base_uri) { + gint referer_node_id; + gint parent_node_id; + gpointer parent_node_id_p; + gpointer current_node_id_p; + gchar *uri = NULL; + + gchar *absolute_target = determine_http_location_target(v->location_base_uri, v->location_target); + /* absolute_target is NULL if the resolution fails */ + if (absolute_target != NULL) { + /* We assume the user makes the request to the absolute_target */ + /* Tick the base URI */ + referer_node_id = http_seq_stats_tick_referer(st, v->location_base_uri); + + /* Tick the location header's resolved URI */ + http_seq_stats_tick_request(st, absolute_target, referer_node_id); + + /* Tick all stats nodes above the location */ + current_node_id_p = GINT_TO_POINTER(referer_node_id); + while (wmem_map_lookup_extended(refstats_node_id_to_parent_node_id_hash, current_node_id_p, NULL, &parent_node_id_p)) { + parent_node_id = GPOINTER_TO_INT(parent_node_id_p); + uri = (gchar *) wmem_map_lookup(refstats_node_id_to_uri_hash, current_node_id_p); + tick_stat_node(st, uri, parent_node_id, TRUE); + current_node_id_p = parent_node_id_p; + } + } + } + + /* Track HTTP Requests/Referers */ if (v->request_method && v->referer_uri && v->full_uri) { + gint referer_node_id; + gint parent_node_id; + gpointer parent_node_id_p; + gpointer current_node_id_p; + gchar *uri = NULL; /* Tick the referer's URI */ - referer_node_id = http_ref_stats_tick_referer(st, v); + referer_node_id = http_seq_stats_tick_referer(st, v->referer_uri); /* Tick the request's URI */ - http_ref_stats_tick_request(st, v, referer_node_id); + http_seq_stats_tick_request(st, v->full_uri, referer_node_id); /* Tick all stats nodes above the referer */ current_node_id_p = GINT_TO_POINTER(referer_node_id); @@ -1080,6 +1209,8 @@ dissect_http_message(tvbuff_t *tvb, int offset, packet_info *pinfo, stat_info->referer_uri = NULL; stat_info->http_host = NULL; stat_info->full_uri = NULL; + stat_info->location_target = NULL; + stat_info->location_base_uri = NULL; orig_offset = offset; @@ -1296,7 +1427,6 @@ dissect_http_message(tvbuff_t *tvb, int offset, packet_info *pinfo, } offset = next_offset; } - if (stat_info->http_host && stat_info->request_uri) { proto_item *e_ti; gchar *uri; @@ -1312,7 +1442,7 @@ dissect_http_message(tvbuff_t *tvb, int offset, packet_info *pinfo, g_strstrip(wmem_strdup(wmem_packet_scope(), stat_info->http_host)), stat_info->request_uri); } stat_info->full_uri = wmem_strdup(wmem_packet_scope(), uri); - + conv_data->full_uri = wmem_strdup(wmem_file_scope(), uri); if (tree) { e_ti = proto_tree_add_string(http_tree, hf_http_request_full_uri, tvb, 0, @@ -2593,6 +2723,7 @@ typedef struct { #define HDR_WEBSOCKET_PROTOCOL 10 #define HDR_WEBSOCKET_EXTENSIONS 11 #define HDR_REFERER 12 +#define HDR_LOCATION 13 static const header_info headers[] = { { "Authorization", &hf_http_authorization, HDR_AUTHORIZATION }, @@ -2615,7 +2746,7 @@ static const header_info headers[] = { { "Date", &hf_http_date, HDR_NO_SPECIAL }, { "Cache-Control", &hf_http_cache_control, HDR_NO_SPECIAL }, { "Server", &hf_http_server, HDR_NO_SPECIAL }, - { "Location", &hf_http_location, HDR_NO_SPECIAL }, + { "Location", &hf_http_location, HDR_LOCATION }, { "Sec-WebSocket-Accept", &hf_http_sec_websocket_accept, HDR_NO_SPECIAL }, { "Sec-WebSocket-Extensions", &hf_http_sec_websocket_extensions, HDR_WEBSOCKET_EXTENSIONS }, { "Sec-WebSocket-Key", &hf_http_sec_websocket_key, HDR_NO_SPECIAL }, @@ -3079,6 +3210,13 @@ process_header(tvbuff_t *tvb, int offset, int next_offset, case HDR_REFERER: stat_info->referer_uri = wmem_strndup(wmem_packet_scope(), value, value_len); break; + + case HDR_LOCATION: + if (conv_data->request_uri){ + stat_info->location_target = wmem_strndup(wmem_packet_scope(), value, value_len); + stat_info->location_base_uri = wmem_strdup(wmem_packet_scope(), conv_data->full_uri); + } + break; } } } @@ -3968,7 +4106,7 @@ proto_reg_handoff_http(void) 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 ); stats_tree_register("http", "http_srv", "HTTP/Load Distribution",0, http_reqs_stats_tree_packet, http_reqs_stats_tree_init, NULL ); - stats_tree_register("http", "http_ref", "HTTP/Referers", 0, http_ref_stats_tree_packet, http_ref_stats_tree_init, NULL ); + stats_tree_register("http", "http_seq", "HTTP/Request Sequences",0, http_seq_stats_tree_packet, http_seq_stats_tree_init, NULL ); } /* diff --git a/epan/dissectors/packet-http.h b/epan/dissectors/packet-http.h index 73d1b44bd6..eb3e2c17cf 100644 --- a/epan/dissectors/packet-http.h +++ b/epan/dissectors/packet-http.h @@ -31,6 +31,8 @@ typedef struct _http_info_value_t { const gchar *request_uri; const gchar *referer_uri; const gchar *full_uri; + const gchar *location_base_uri; + const gchar *location_target; } http_info_value_t; /** information about a request and response on a HTTP conversation. */ @@ -56,6 +58,7 @@ typedef struct _http_conv_t { gchar *http_host; gchar *request_method; gchar *request_uri; + gchar *full_uri; /** the number of requests on the conversation. */ guint32 req_res_num; guint8 upgrade; diff --git a/ui/qt/main_window.h b/ui/qt/main_window.h index 88d39f9fb9..fb9d03940e 100644 --- a/ui/qt/main_window.h +++ b/ui/qt/main_window.h @@ -612,7 +612,7 @@ private slots: void on_actionStatisticsHTTPPacketCounter_triggered(); void on_actionStatisticsHTTPRequests_triggered(); void on_actionStatisticsHTTPLoadDistribution_triggered(); - void on_actionStatisticsHTTPReferers_triggered(); + void on_actionStatisticsHTTPRequestSequences_triggered(); void on_actionStatisticsPacketLengths_triggered(); void statCommandIOGraph(const char *, void *); void on_actionStatisticsIOGraph_triggered(); diff --git a/ui/qt/main_window.ui b/ui/qt/main_window.ui index 28e3c929a2..27c9c2fff8 100644 --- a/ui/qt/main_window.ui +++ b/ui/qt/main_window.ui @@ -484,7 +484,7 @@ <addaction name="actionStatisticsHTTPPacketCounter"/> <addaction name="actionStatisticsHTTPRequests"/> <addaction name="actionStatisticsHTTPLoadDistribution"/> - <addaction name="actionStatisticsHTTPReferers"/> + <addaction name="actionStatisticsHTTPRequestSequences"/> </widget> <widget class="QMenu" name="menu29West"> <property name="title"> @@ -1942,12 +1942,12 @@ <string>HTTP load distribution</string> </property> </action> - <action name="actionStatisticsHTTPReferers"> + <action name="actionStatisticsHTTPRequestSequences"> <property name="text"> - <string>Referers</string> + <string>Request Sequences</string> </property> <property name="toolTip"> - <string>HTTP referers</string> + <string>HTTP Request Sequences</string> </property> </action> <action name="actionStatisticsPacketLengths"> diff --git a/ui/qt/main_window_slots.cpp b/ui/qt/main_window_slots.cpp index 1c70a6f389..b1cc787ad4 100644 --- a/ui/qt/main_window_slots.cpp +++ b/ui/qt/main_window_slots.cpp @@ -3324,9 +3324,9 @@ void MainWindow::on_actionStatisticsHTTPLoadDistribution_triggered() openStatisticsTreeDialog("http_srv"); } -void MainWindow::on_actionStatisticsHTTPReferers_triggered() +void MainWindow::on_actionStatisticsHTTPRequestSequences_triggered() { - openStatisticsTreeDialog("http_ref"); + openStatisticsTreeDialog("http_seq"); } void MainWindow::on_actionStatisticsPacketLengths_triggered() |