diff --git a/epan/dissectors/packet-http.c b/epan/dissectors/packet-http.c index d7b40b341c..6632e094d7 100644 --- a/epan/dissectors/packet-http.c +++ b/epan/dissectors/packet-http.c @@ -60,6 +60,7 @@ void proto_reg_handoff_message_http(void); static int http_tap = -1; static int http_eo_tap = -1; +static int http_follow_tap = -1; static int proto_http = -1; static int proto_http2 = -1; @@ -1181,6 +1182,11 @@ dissect_http_message(tvbuff_t *tvb, int offset, packet_info *pinfo, } } + /* Give the follw tap what we've currently dissected */ + if(have_tap_listener(http_follow_tap)) { + tap_queue_packet(http_follow_tap, pinfo, tvb_new_subset_length(tvb, 0, offset)); + } + reported_datalen = tvb_reported_length_remaining(tvb, offset); datalen = tvb_captured_length_remaining(tvb, offset); @@ -1444,6 +1450,13 @@ dissect_http_message(tvbuff_t *tvb, int offset, packet_info *pinfo, tap_queue_packet(http_eo_tap, pinfo, eo_info); } + /* Save values for the Export Object GUI feature if we have + * an active listener to process it (which happens when + * the export object window is open). */ + if(have_tap_listener(http_follow_tap)) { + tap_queue_packet(http_follow_tap, pinfo, next_tvb); + } + /* * Do subdissector checks. * @@ -3497,6 +3510,7 @@ proto_register_http(void) */ http_tap = register_tap("http"); /* HTTP statistics tap */ http_eo_tap = register_tap("http_eo"); /* HTTP Export Object tap */ + http_follow_tap = register_tap("http_follow"); /* HTTP Follow tap */ } /* diff --git a/epan/follow.c b/epan/follow.c index 75ddc020f1..89e6519193 100644 --- a/epan/follow.c +++ b/epan/follow.c @@ -86,7 +86,7 @@ follow_stats(follow_stats_t* stats) chance that two streams could intersect, but not a very good one */ gchar* -build_follow_conv_filter( packet_info *pi ) { +build_follow_conv_filter( packet_info *pi, const char* append_filter ) { char* buf; int len; conversation_t *conv=NULL; @@ -122,7 +122,11 @@ build_follow_conv_filter( packet_info *pi ) { /* TCP over IPv4/6 */ tcpd=get_tcp_conversation_data(conv, pi); if (tcpd) { - buf = g_strdup_printf("tcp.stream eq %d", tcpd->stream); + if (append_filter == NULL) { + buf = g_strdup_printf("tcp.stream eq %d", tcpd->stream); + } else { + buf = g_strdup_printf("((tcp.stream eq %d) && (%s))", tcpd->stream, append_filter); + } stream_to_follow[TCP_STREAM] = tcpd->stream; if (pi->net_src.type == AT_IPv4) { len = 4; @@ -142,7 +146,11 @@ build_follow_conv_filter( packet_info *pi ) { /* UDP over IPv4/6 */ udpd=get_udp_conversation_data(conv, pi); if (udpd) { - buf = g_strdup_printf("udp.stream eq %d", udpd->stream); + if (append_filter == NULL) { + buf = g_strdup_printf("udp.stream eq %d", udpd->stream); + } else { + buf = g_strdup_printf("((udp.stream eq %d) && (%s))", udpd->stream, append_filter); + } stream_to_follow[UDP_STREAM] = udpd->stream; if (pi->net_src.type == AT_IPv4) { len = 4; diff --git a/epan/follow.h b/epan/follow.h index 4e6b748840..7eefe2270e 100644 --- a/epan/follow.h +++ b/epan/follow.h @@ -54,11 +54,12 @@ typedef struct _tcp_stream_chunk { /** Build a follow filter based on the current packet's conversation. * * @param packet_info [in] The current packet. + * @param append_filter [in] Optional filter to && (AND) to generated one. * @return A filter that specifies the conversation. Must be g_free()d * the caller. */ WS_DLL_PUBLIC -gchar* build_follow_conv_filter( packet_info * packet_info); +gchar* build_follow_conv_filter( packet_info * packet_info, const char* append_filter); /** Build a follow filter based on the current TCP/UDP stream index. * follow_index() must be called prior to calling this. diff --git a/ui/follow.h b/ui/follow.h index 1ece30ebf1..815e7b49a1 100644 --- a/ui/follow.h +++ b/ui/follow.h @@ -44,7 +44,8 @@ typedef struct { typedef enum { FOLLOW_TCP, FOLLOW_SSL, - FOLLOW_UDP + FOLLOW_UDP, + FOLLOW_HTTP } follow_type_t; /* Show Stream */ diff --git a/ui/gtk/CMakeLists.txt b/ui/gtk/CMakeLists.txt index 4501f2d32d..b32db52c87 100644 --- a/ui/gtk/CMakeLists.txt +++ b/ui/gtk/CMakeLists.txt @@ -53,6 +53,7 @@ set(WIRESHARK_GTK_SRC filter_utils.c find_dlg.c firewall_dlg.c + follow_http.c follow_ssl.c follow_stream.c follow_tcp.c diff --git a/ui/gtk/Makefile.common b/ui/gtk/Makefile.common index 04e739985f..ffdb84e707 100644 --- a/ui/gtk/Makefile.common +++ b/ui/gtk/Makefile.common @@ -72,6 +72,7 @@ WIRESHARK_COMMON_GTK_SRC = \ filter_utils.c \ find_dlg.c \ firewall_dlg.c \ + follow_http.c \ follow_ssl.c \ follow_stream.c \ follow_tcp.c \ @@ -199,6 +200,7 @@ noinst_HEADERS = \ filter_utils.h \ find_dlg.h \ firewall_dlg.h \ + follow_http.h \ follow_ssl.h \ follow_stream.h \ follow_tcp.h \ diff --git a/ui/gtk/follow_http.c b/ui/gtk/follow_http.c new file mode 100644 index 0000000000..dced654025 --- /dev/null +++ b/ui/gtk/follow_http.c @@ -0,0 +1,313 @@ +/* follow_http.c + * HTTP specific routines for following traffic streams + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. + */ + +#include "config.h" +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include "gtkglobals.h" +#include "ui/gtk/follow_stream.h" +#include "ui/gtk/keys.h" +#include "ui/gtk/main.h" +#include "ui/gtk/follow_http.h" + +static gboolean +http_queue_packet_data(void *tapdata, packet_info *pinfo, + epan_dissect_t *edt _U_, const void *data) +{ + follow_record_t *follow_record; + follow_info_t *follow_info = (follow_info_t *)tapdata; + tvbuff_t *next_tvb = (tvbuff_t *)data; + + follow_record = g_new(follow_record_t,1); + + follow_record->data = g_byte_array_sized_new(tvb_captured_length(next_tvb)); + follow_record->data = g_byte_array_append(follow_record->data, + tvb_get_ptr(next_tvb, 0, -1), + tvb_captured_length(next_tvb)); + + if (follow_info->client_port == 0) { + follow_info->client_port = pinfo->srcport; + copy_address(&follow_info->client_ip, &pinfo->src); + } + + if (addresses_equal(&follow_info->client_ip, &pinfo->src) && follow_info->client_port == pinfo->srcport) + follow_record->is_server = FALSE; + else + follow_record->is_server = TRUE; + + /* update stream counter */ + follow_info->bytes_written[follow_record->is_server] += follow_record->data->len; + + follow_info->payload = g_list_append(follow_info->payload, follow_record); + return FALSE; +} + +/* Follow the HTTP stream, if any, to which the last packet that we called + a dissection routine on belongs (this might be the most recently + selected packet, or it might be the last packet in the file). */ +void +follow_http_stream_cb(GtkWidget *w _U_, gpointer data _U_) +{ + GtkWidget *filter_te, *filter_cm; + gchar *follow_filter; + const gchar *previous_filter; + int filter_out_filter_len, previous_filter_len; + const char *hostname0, *hostname1; + char *port0, *port1; + gchar *server_to_client_string = NULL; + gchar *client_to_server_string = NULL; + gchar *both_directions_string = NULL; + follow_stats_t stats; + follow_info_t *follow_info; + GString *msg; + gboolean is_http = FALSE; + + is_http = proto_is_frame_protocol(cfile.edt->pi.layers, "http"); + + if (!is_http) { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, + "Error following stream. Please make\n" + "sure you have a UDP packet selected."); + return; + } + + follow_info = g_new0(follow_info_t, 1); + follow_info->follow_type = FOLLOW_HTTP; + + /* Create a new filter that matches all packets in the HTTP stream, + and set the display filter entry accordingly */ + follow_filter = build_follow_conv_filter(&cfile.edt->pi, "http"); + if (!follow_filter) + { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, + "Error creating filter for this stream.\n" + "A network layer header is needed"); + g_free(follow_info); + return; + } + + /* Set the display filter entry accordingly */ + filter_cm = (GtkWidget *)g_object_get_data(G_OBJECT(top_level), E_DFILTER_CM_KEY); + filter_te = gtk_bin_get_child(GTK_BIN(filter_cm)); + + /* needed in follow_filter_out_stream(), is there a better way? */ + follow_info->filter_te = filter_te; + + /* save previous filter, const since we're not supposed to alter */ + previous_filter = + (const gchar *)gtk_entry_get_text(GTK_ENTRY(filter_te)); + + /* allocate our new filter. API claims g_malloc terminates program on failure */ + /* my calc for max alloc needed is really +10 but when did a few extra bytes hurt ? */ + previous_filter_len = previous_filter?(int)strlen(previous_filter):0; + filter_out_filter_len = (int)strlen(follow_filter) + previous_filter_len + 16; + follow_info->filter_out_filter = (gchar *)g_malloc(filter_out_filter_len); + + /* append the negation */ + if(previous_filter_len) { + g_snprintf(follow_info->filter_out_filter, filter_out_filter_len, + "%s and !(%s)", previous_filter, follow_filter); + } else { + g_snprintf(follow_info->filter_out_filter, filter_out_filter_len, + "!(%s)", follow_filter); + } + + /* data will be passed via tap callback*/ + msg = register_tap_listener("http_follow", follow_info, follow_filter, + 0, NULL, http_queue_packet_data, NULL); + if (msg) { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, + "Can't register http_follow tap: %s\n", + msg->str); + g_free(follow_info->filter_out_filter); + g_free(follow_info); + g_free(follow_filter); + return; + } + + gtk_entry_set_text(GTK_ENTRY(filter_te), follow_filter); + + /* Run the display filter so it goes in effect - even if it's the + same as the previous display filter. */ + main_filter_packets(&cfile, follow_filter, TRUE); + + /* Free the filter string, as we're done with it. */ + g_free(follow_filter); + + remove_tap_listener(follow_info); + + /* Stream to show */ + follow_stats(&stats); + + if (stats.is_ipv6) { + struct e_in6_addr ipaddr; + memcpy(&ipaddr, stats.ip_address[0], 16); + hostname0 = get_hostname6(&ipaddr); + memcpy(&ipaddr, stats.ip_address[1], 16); + hostname1 = get_hostname6(&ipaddr); + } else { + guint32 ipaddr; + memcpy(&ipaddr, stats.ip_address[0], 4); + hostname0 = get_hostname(ipaddr); + memcpy(&ipaddr, stats.ip_address[1], 4); + hostname1 = get_hostname(ipaddr); + } + + port0 = tcp_port_to_display(NULL, stats.port[0]); + port1 = tcp_port_to_display(NULL, stats.port[1]); + + follow_info->is_ipv6 = stats.is_ipv6; + + /* Both Stream Directions */ + both_directions_string = g_strdup_printf("Entire conversation (%u bytes)", follow_info->bytes_written[0] + follow_info->bytes_written[1]); + + if ((follow_info->client_port == stats.port[0]) && + ((stats.is_ipv6 && (memcmp(follow_info->client_ip.data, stats.ip_address[0], 16) == 0)) || + (!stats.is_ipv6 && (memcmp(follow_info->client_ip.data, stats.ip_address[0], 4) == 0)))) { + server_to_client_string = + g_strdup_printf("%s:%s " UTF8_RIGHTWARDS_ARROW " %s:%s (%u bytes)", + hostname0, port0, + hostname1, port1, + follow_info->bytes_written[0]); + + client_to_server_string = + g_strdup_printf("%s:%s " UTF8_RIGHTWARDS_ARROW " %s:%s (%u bytes)", + hostname1, port1, + hostname0, port0, + follow_info->bytes_written[1]); + } else { + server_to_client_string = + g_strdup_printf("%s:%s " UTF8_RIGHTWARDS_ARROW " %s:%s (%u bytes)", + hostname1, port1, + hostname0, port0, + follow_info->bytes_written[0]); + + client_to_server_string = + g_strdup_printf("%s:%s " UTF8_RIGHTWARDS_ARROW " %s:%s (%u bytes)", + hostname0, port0, + hostname1, port1, + follow_info->bytes_written[1]); + } + + follow_stream("Follow HTTP Stream", follow_info, both_directions_string, + server_to_client_string, client_to_server_string); + + wmem_free(NULL, port0); + wmem_free(NULL, port1); + g_free(both_directions_string); + g_free(server_to_client_string); + g_free(client_to_server_string); +} + +#define FLT_BUF_SIZE 1024 + +/* + * XXX - the routine pointed to by "print_line_fcn_p" doesn't get handed lines, + * it gets handed bufferfuls. That's fine for "follow_write_raw()" + * and "follow_add_to_gtk_text()", but, as "follow_print_text()" calls + * the "print_line()" routine from "print.c", and as that routine might + * genuinely expect to be handed a line (if, for example, it's using + * some OS or desktop environment's printing API, and that API expects + * to be handed lines), "follow_print_text()" should probably accumulate + * lines in a buffer and hand them "print_line()". (If there's a + * complete line in a buffer - i.e., there's nothing of the line in + * the previous buffer or the next buffer - it can just hand that to + * "print_line()" after filtering out non-printables, as an + * optimization.) + * + * This might or might not be the reason why C arrays display + * correctly but get extra blank lines very other line when printed. + */ +frs_return_t +follow_read_http_stream(follow_info_t *follow_info, + gboolean (*print_line_fcn_p)(char *, size_t, gboolean, void *), + void *arg) +{ + guint32 global_client_pos = 0, global_server_pos = 0; + guint32 server_packet_count = 0; + guint32 client_packet_count = 0; + guint32 *global_pos; + gboolean skip; + GList* cur; + frs_return_t frs_return; + follow_record_t *follow_record; + char *buffer; + + + for (cur = follow_info->payload; cur; cur = g_list_next(cur)) { + follow_record = (follow_record_t *)cur->data; + skip = FALSE; + if (!follow_record->is_server) { + global_pos = &global_client_pos; + if(follow_info->show_stream == FROM_SERVER) { + skip = TRUE; + } + } else { + global_pos = &global_server_pos; + if (follow_info->show_stream == FROM_CLIENT) { + skip = TRUE; + } + } + + if (!skip) { + buffer = (char *)g_memdup(follow_record->data->data, + follow_record->data->len); + + frs_return = follow_show(follow_info, print_line_fcn_p, + buffer, + follow_record->data->len, + follow_record->is_server, arg, + global_pos, + &server_packet_count, + &client_packet_count); + g_free(buffer); + if(frs_return == FRS_PRINT_ERROR) + return frs_return; + } + } + + return FRS_OK; +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/ui/gtk/follow_http.h b/ui/gtk/follow_http.h new file mode 100644 index 0000000000..011fc353f4 --- /dev/null +++ b/ui/gtk/follow_http.h @@ -0,0 +1,44 @@ +/* follow_http.h + * HTTP specific routines for following traffic streams + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __FOLLOW_HTTP_H__ +#define __FOLLOW_HTTP_H__ + +/* Follow the HTTP stream, if any, to which the last packet that we called + a dissection routine on belongs (this might be the most recently + selected packet, or it might be the last packet in the file). */ +void follow_http_stream_cb(GtkWidget * w, gpointer data _U_); + +#endif /* __FOLLOW_HTTP_H__ */ + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/ui/gtk/follow_ssl.c b/ui/gtk/follow_ssl.c index 22569f1692..7c30b94f41 100644 --- a/ui/gtk/follow_ssl.c +++ b/ui/gtk/follow_ssl.c @@ -160,7 +160,7 @@ follow_ssl_stream_cb(GtkWidget * w _U_, gpointer data _U_) /* Create a new filter that matches all packets in the SSL stream, and set the display filter entry accordingly */ reset_tcp_reassembly(); - follow_filter = build_follow_conv_filter(&cfile.edt->pi); + follow_filter = build_follow_conv_filter(&cfile.edt->pi, NULL); if (!follow_filter) { simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, diff --git a/ui/gtk/follow_stream.c b/ui/gtk/follow_stream.c index f30e6648cf..4a8d4e7b91 100644 --- a/ui/gtk/follow_stream.c +++ b/ui/gtk/follow_stream.c @@ -87,6 +87,9 @@ follow_read_stream(follow_info_t *follow_info, case FOLLOW_SSL : return follow_read_ssl_stream(follow_info, print_line_fcn_p, arg); + case FOLLOW_HTTP : + return follow_read_http_stream(follow_info, print_line_fcn_p, arg); + default : g_assert_not_reached(); return (frs_return_t)0; @@ -865,6 +868,7 @@ follow_destroy_cb(GtkWidget *w, gpointer data _U_) break; case FOLLOW_UDP : + case FOLLOW_HTTP : for(cur = follow_info->payload; cur; cur = g_list_next(cur)) if(cur->data) { follow_record = (follow_record_t *)cur->data; diff --git a/ui/gtk/follow_stream.h b/ui/gtk/follow_stream.h index 60d3283de3..df8c77da46 100644 --- a/ui/gtk/follow_stream.h +++ b/ui/gtk/follow_stream.h @@ -70,6 +70,7 @@ frs_return_t follow_show(follow_info_t *follow_info, gboolean follow_add_to_gtk_text(char *buffer, size_t nchars, gboolean is_server, void *arg); +frs_return_t follow_read_http_stream(follow_info_t *follow_info, gboolean (*print_line)(char *, size_t, gboolean, void *), void *arg); frs_return_t follow_read_tcp_stream(follow_info_t *follow_info, gboolean (*print_line)(char *, size_t, gboolean, void *), void *arg); frs_return_t follow_read_udp_stream(follow_info_t *follow_info, gboolean (*print_line)(char *, size_t, gboolean, void *), void *arg); frs_return_t follow_read_ssl_stream(follow_info_t *follow_info, gboolean (*print_line)(char *, size_t, gboolean, void *), void *arg); diff --git a/ui/gtk/follow_tcp.c b/ui/gtk/follow_tcp.c index 369a756b3e..111b5de0f1 100644 --- a/ui/gtk/follow_tcp.c +++ b/ui/gtk/follow_tcp.c @@ -112,7 +112,7 @@ follow_tcp_stream_cb(GtkWidget * w _U_, gpointer data _U_) /* Create a new filter that matches all packets in the TCP stream, and set the display filter entry accordingly */ reset_tcp_reassembly(); - follow_filter = build_follow_conv_filter(&cfile.edt->pi); + follow_filter = build_follow_conv_filter(&cfile.edt->pi, NULL); if (!follow_filter) { simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "Error creating filter for this stream.\n" diff --git a/ui/gtk/follow_udp.c b/ui/gtk/follow_udp.c index ae6f362e49..960fb2a662 100644 --- a/ui/gtk/follow_udp.c +++ b/ui/gtk/follow_udp.c @@ -106,7 +106,7 @@ follow_udp_stream_cb(GtkWidget *w _U_, gpointer data _U_) /* Create a new filter that matches all packets in the UDP stream, and set the display filter entry accordingly */ - follow_filter = build_follow_conv_filter(&cfile.edt->pi); + follow_filter = build_follow_conv_filter(&cfile.edt->pi, NULL); if (!follow_filter) { simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, diff --git a/ui/gtk/main_menubar.c b/ui/gtk/main_menubar.c index 8da4c80299..c31cb335ac 100644 --- a/ui/gtk/main_menubar.c +++ b/ui/gtk/main_menubar.c @@ -65,6 +65,7 @@ #include "ui/gtk/summary_dlg.h" #include "ui/gtk/prefs_dlg.h" #include "ui/gtk/packet_win.h" +#include "ui/gtk/follow_http.h" #include "ui/gtk/follow_tcp.h" #include "ui/gtk/follow_udp.h" #include "ui/gtk/follow_ssl.h" @@ -1002,6 +1003,7 @@ static const char *ui_desc_menubar = " \n" " \n" " \n" +" \n" " \n" " \n" " \n" @@ -1436,6 +1438,7 @@ static const GtkActionEntry main_menu_bar_entries[] = { { "/Analyze/FollowTCPStream", NULL, "Follow TCP Stream", NULL, NULL, G_CALLBACK(follow_tcp_stream_cb) }, { "/Analyze/FollowUDPStream", NULL, "Follow UDP Stream", NULL, NULL, G_CALLBACK(follow_udp_stream_cb) }, { "/Analyze/FollowSSLStream", NULL, "Follow SSL Stream", NULL, NULL, G_CALLBACK(follow_ssl_stream_cb) }, + { "/Analyze/FollowHTTPStream", NULL, "Follow HTTP Stream", NULL, NULL, G_CALLBACK(follow_http_stream_cb) }, { "/Analyze/ExpertInfo", WIRESHARK_STOCK_EXPERT_INFO, "Expert _Info", NULL, NULL, G_CALLBACK(expert_comp_dlg_launch) }, @@ -2201,6 +2204,7 @@ static const char *ui_desc_packet_list_menu_popup = " \n" " \n" " \n" +" \n" " \n" " \n" " \n" @@ -2265,6 +2269,7 @@ static const GtkActionEntry packet_list_menu_popup_action_entries[] = { { "/Follow TCP Stream", NULL, "Follow TCP Stream", NULL, NULL, G_CALLBACK(follow_tcp_stream_cb) }, { "/Follow UDP Stream", NULL, "Follow UDP Stream", NULL, NULL, G_CALLBACK(follow_udp_stream_cb) }, { "/Follow SSL Stream", NULL, "Follow SSL Stream", NULL, NULL, G_CALLBACK(follow_ssl_stream_cb) }, + { "/Follow HTTP Stream", NULL, "Follow HTTP Stream", NULL, NULL, G_CALLBACK(follow_http_stream_cb) }, { "/Copy", NULL, "Copy", NULL, NULL, NULL }, { "/Copy/SummaryTxt", NULL, "Summary (Text)", NULL, NULL, G_CALLBACK(packet_list_menu_copy_sum_txt) }, @@ -2329,6 +2334,7 @@ static const char *ui_desc_tree_view_menu_popup = " \n" " \n" " \n" +" \n" " \n" " \n" " \n" @@ -2387,6 +2393,7 @@ static const GtkActionEntry tree_view_menu_popup_action_entries[] = { { "/Follow TCP Stream", NULL, "Follow TCP Stream", NULL, NULL, G_CALLBACK(follow_tcp_stream_cb) }, { "/Follow UDP Stream", NULL, "Follow UDP Stream", NULL, NULL, G_CALLBACK(follow_udp_stream_cb) }, { "/Follow SSL Stream", NULL, "Follow SSL Stream", NULL, NULL, G_CALLBACK(follow_ssl_stream_cb) }, + { "/Follow HTTP Stream", NULL, "Follow HTTP Stream", NULL, NULL, G_CALLBACK(follow_http_stream_cb) }, { "/Copy", NULL, "Copy", NULL, NULL, NULL }, { "/Copy/Description", NULL, "Description", NULL, NULL, G_CALLBACK(tree_view_menu_copy_desc) }, @@ -4533,7 +4540,7 @@ set_menus_for_selected_packet(capture_file *cf) gboolean properties = FALSE; const char *abbrev = NULL; char *prev_abbrev; - gboolean is_ip = FALSE, is_tcp = FALSE, is_udp = FALSE, is_sctp = FALSE, is_ssl = FALSE, is_lte_rlc = FALSE; + gboolean is_ip = FALSE, is_tcp = FALSE, is_udp = FALSE, is_sctp = FALSE, is_ssl = FALSE, is_lte_rlc = FALSE, is_http = FALSE; /* Making the menu context-sensitive allows for easier selection of the desired item and has the added benefit, with large captures, of @@ -4556,8 +4563,10 @@ set_menus_for_selected_packet(capture_file *cf) than one time reference frame or the current frame isn't a time reference frame). (XXX - why check frame_selected?) */ if (cf->edt) + { proto_get_frame_protocols(cf->edt->pi.layers, &is_ip, &is_tcp, &is_udp, &is_sctp, &is_ssl, NULL, &is_lte_rlc); - + is_http = proto_is_frame_protocol(cf->edt->pi.layers, "http"); + } if (cf->edt && cf->edt->tree) { GPtrArray *ga; header_field_info *hfinfo; @@ -4663,11 +4672,16 @@ set_menus_for_selected_packet(capture_file *cf) frame_selected ? is_udp : FALSE); set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/FollowSSLStream", frame_selected ? is_ssl : FALSE); + set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/FollowHTTPStream", + frame_selected ? is_http : FALSE); + set_menu_sensitivity(ui_manager_tree_view_menu, "/TreeViewPopup/FollowSSLStream", frame_selected ? is_ssl : FALSE); - set_menu_sensitivity(ui_manager_tree_view_menu, "/TreeViewPopup/FollowUDPStream", frame_selected ? is_udp : FALSE); + set_menu_sensitivity(ui_manager_tree_view_menu, "/TreeViewPopup/FollowHTTPStream", + frame_selected ? is_http : FALSE); + set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/ColorizeConversation", frame_selected); set_menu_sensitivity(ui_manager_packet_list_menu, "/PacketListMenuPopup/DecodeAs", @@ -4704,6 +4718,8 @@ set_menus_for_selected_packet(capture_file *cf) frame_selected ? is_udp : FALSE); set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/AnalyzeMenu/FollowSSLStream", frame_selected ? is_ssl : FALSE); + set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/AnalyzeMenu/FollowHTTPStream", + frame_selected ? is_http : FALSE); set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/AnalyzeMenu/DecodeAs", frame_selected && decode_as_ok()); set_menu_sensitivity(ui_manager_main_menubar, "/Menubar/ViewMenu/NameResolution/ResolveName", diff --git a/ui/qt/follow_stream_dialog.cpp b/ui/qt/follow_stream_dialog.cpp index cf020ad00e..dec1a0576e 100644 --- a/ui/qt/follow_stream_dialog.cpp +++ b/ui/qt/follow_stream_dialog.cpp @@ -252,9 +252,9 @@ void FollowStreamDialog::filterOut() close(); } -void FollowStreamDialog::on_cbDirections_currentIndexChanged(int index) +void FollowStreamDialog::on_cbDirections_currentIndexChanged(int idx) { - switch(index) + switch(idx) { case 0 : follow_info_.show_stream = BOTH_HOSTS; @@ -272,10 +272,10 @@ void FollowStreamDialog::on_cbDirections_currentIndexChanged(int index) readStream(); } -void FollowStreamDialog::on_cbCharset_currentIndexChanged(int index) +void FollowStreamDialog::on_cbCharset_currentIndexChanged(int idx) { - if (index < 0) return; - follow_info_.show_type = static_cast(ui->cbCharset->itemData(index).toInt()); + if (idx < 0) return; + follow_info_.show_type = static_cast(ui->cbCharset->itemData(idx).toInt()); readStream(); } @@ -361,6 +361,10 @@ FollowStreamDialog::readStream() ret = readSslStream(); break; + case FOLLOW_HTTP : + ret = readHttpStream(); + break; + default : g_assert_not_reached(); ret = (frs_return_t)0; @@ -465,6 +469,39 @@ ssl_queue_packet_data(void *tapdata, packet_info *pinfo, epan_dissect_t *, const return FALSE; } +//Copy from ui/gtk/follow_http.c +static gboolean +http_queue_packet_data(void *tapdata, packet_info *pinfo, + epan_dissect_t *, const void *data) +{ + follow_record_t *follow_record; + follow_info_t *follow_info = (follow_info_t *)tapdata; + tvbuff_t *next_tvb = (tvbuff_t *)data; + + follow_record = g_new(follow_record_t,1); + + follow_record->data = g_byte_array_sized_new(tvb_captured_length(next_tvb)); + follow_record->data = g_byte_array_append(follow_record->data, + tvb_get_ptr(next_tvb, 0, -1), + tvb_captured_length(next_tvb)); + + if (follow_info->client_port == 0) { + follow_info->client_port = pinfo->srcport; + copy_address(&follow_info->client_ip, &pinfo->src); + } + + if (addresses_equal(&follow_info->client_ip, &pinfo->src) && follow_info->client_port == pinfo->srcport) + follow_record->is_server = FALSE; + else + follow_record->is_server = TRUE; + + /* update stream counter */ + follow_info->bytes_written[follow_record->is_server] += follow_record->data->len; + + follow_info->payload = g_list_append(follow_info->payload, follow_record); + return FALSE; +} + /* * XXX - the routine pointed to by "print_line_fcn_p" doesn't get handed lines, * it gets handed bufferfuls. That's fine for "follow_write_raw()" @@ -519,6 +556,52 @@ FollowStreamDialog::readSslStream() return FRS_OK; } +/* XXX - Currently the same as readUdpStream() */ +frs_return_t +FollowStreamDialog::readHttpStream() +{ + guint32 global_client_pos = 0, global_server_pos = 0; + guint32 *global_pos; + gboolean skip; + GList* cur; + frs_return_t frs_return; + follow_record_t *follow_record; + char *buffer; + + for (cur = follow_info_.payload; cur; cur = g_list_next(cur)) { + follow_record = (follow_record_t *)cur->data; + skip = FALSE; + if (!follow_record->is_server) { + global_pos = &global_client_pos; + if(follow_info_.show_stream == FROM_SERVER) { + skip = TRUE; + } + } else { + global_pos = &global_server_pos; + if (follow_info_.show_stream == FROM_CLIENT) { + skip = TRUE; + } + } + + if (!skip) { + buffer = (char *)g_memdup(follow_record->data->data, + follow_record->data->len); + + frs_return = showBuffer( + buffer, + follow_record->data->len, + follow_record->is_server, + follow_record->packet_num, + global_pos); + g_free(buffer); + if(frs_return == FRS_PRINT_ERROR) + return frs_return; + } + } + + return FRS_OK; +} + void FollowStreamDialog::followStream() { @@ -856,7 +939,7 @@ bool FollowStreamDialog::follow(QString previous_filter, bool use_stream_index) follow_stats_t stats; tcp_stream_chunk sc; size_t nchars; - gboolean is_tcp = FALSE, is_udp = FALSE; + gboolean is_tcp = FALSE, is_udp = FALSE, is_http = FALSE; resetStream(); @@ -873,6 +956,7 @@ bool FollowStreamDialog::follow(QString previous_filter, bool use_stream_index) } proto_get_frame_protocols(cap_file_.capFile()->edt->pi.layers, NULL, &is_tcp, &is_udp, NULL, NULL, NULL, NULL); + is_http = proto_is_frame_protocol(cap_file_.capFile()->edt->pi.layers, "http"); switch (follow_type_) { @@ -897,6 +981,12 @@ bool FollowStreamDialog::follow(QString previous_filter, bool use_stream_index) return false; } break; + case FOLLOW_HTTP: + if (!is_http) { + QMessageBox::warning(this, tr("Error following stream."), tr("Please make sure you have a HTTP packet selected.")); + return false; + } + break; } if (follow_type_ == FOLLOW_TCP || follow_type_ == FOLLOW_SSL) @@ -912,7 +1002,11 @@ bool FollowStreamDialog::follow(QString previous_filter, bool use_stream_index) follow_filter = gchar_free_to_qstring( build_follow_index_filter((follow_type_ == FOLLOW_TCP) ? TCP_STREAM : UDP_STREAM)); } else { - follow_filter = gchar_free_to_qstring(build_follow_conv_filter(&cap_file_.capFile()->edt->pi)); + if (follow_type_ == FOLLOW_HTTP) { + follow_filter = gchar_free_to_qstring(build_follow_conv_filter(&cap_file_.capFile()->edt->pi, "http")); + } else { + follow_filter = gchar_free_to_qstring(build_follow_conv_filter(&cap_file_.capFile()->edt->pi, NULL)); + } } if (follow_filter.isEmpty()) { QMessageBox::warning(this, @@ -1006,6 +1100,14 @@ bool FollowStreamDialog::follow(QString previous_filter, bool use_stream_index) return false; } break; + case FOLLOW_HTTP: + /* data will be passed via tap callback*/ + if (!registerTapListener("http_follow", &follow_info_, + follow_filter.toUtf8().constData(), + 0, NULL, http_queue_packet_data, NULL)) { + return false; + } + break; } beginRetapPackets(); @@ -1022,6 +1124,7 @@ bool FollowStreamDialog::follow(QString previous_filter, bool use_stream_index) break; case FOLLOW_UDP: case FOLLOW_SSL: + case FOLLOW_HTTP: removeTapListeners(); break; } @@ -1097,6 +1200,7 @@ bool FollowStreamDialog::follow(QString previous_filter, bool use_stream_index) switch (follow_type_) { case FOLLOW_TCP: + case FOLLOW_HTTP: port0 = tcp_port_to_display(NULL, stats.port[0]); port1 = tcp_port_to_display(NULL, stats.port[1]); break; @@ -1231,6 +1335,13 @@ bool FollowStreamDialog::follow(QString previous_filter, bool use_stream_index) format_size_unit_bytes|format_size_prefix_si))); setWindowSubtitle(tr("Follow SSL Stream (%1)").arg(follow_filter)); break; + case FOLLOW_HTTP: + both_directions_string = QString("Entire conversation (%1)") + .arg(gchar_free_to_qstring(format_size( + stats.bytes_written[0] + stats.bytes_written[1], + format_size_unit_bytes|format_size_prefix_si))); + setWindowSubtitle(tr("Follow HTTP Stream (%1)").arg(follow_filter)); + break; } ui->cbDirections->clear(); diff --git a/ui/qt/follow_stream_dialog.h b/ui/qt/follow_stream_dialog.h index 86041e7192..2d82a83ea7 100644 --- a/ui/qt/follow_stream_dialog.h +++ b/ui/qt/follow_stream_dialog.h @@ -79,8 +79,8 @@ protected: void keyPressEvent(QKeyEvent *event); private slots: - void on_cbCharset_currentIndexChanged(int index); - void on_cbDirections_currentIndexChanged(int index); + void on_cbCharset_currentIndexChanged(int idx); + void on_cbDirections_currentIndexChanged(int idx); void on_bFind_clicked(); void on_leFind_returnPressed(); @@ -113,6 +113,7 @@ private: frs_return_t readTcpStream(); frs_return_t readUdpStream(); frs_return_t readSslStream(); + frs_return_t readHttpStream(); void followStream(); void addText(QString text, gboolean is_from_server, guint32 packet_num); diff --git a/ui/qt/main_window.h b/ui/qt/main_window.h index f7ffcfbabe..aacbb46aa3 100644 --- a/ui/qt/main_window.h +++ b/ui/qt/main_window.h @@ -463,6 +463,7 @@ private slots: void on_actionAnalyzeFollowTCPStream_triggered(); void on_actionAnalyzeFollowUDPStream_triggered(); void on_actionAnalyzeFollowSSLStream_triggered(); + void on_actionAnalyzeFollowHTTPStream_triggered(); void statCommandExpertInfo(const char *, void *); void on_actionAnalyzeExpertInfo_triggered(); diff --git a/ui/qt/main_window.ui b/ui/qt/main_window.ui index 9c5bf183f0..4a9db3e4b0 100644 --- a/ui/qt/main_window.ui +++ b/ui/qt/main_window.ui @@ -386,6 +386,7 @@ + @@ -1699,6 +1700,14 @@ SSL Stream + + + false + + + HTTP Stream + + Time Sequence (tcptrace) diff --git a/ui/qt/main_window_slots.cpp b/ui/qt/main_window_slots.cpp index 2cc5755626..a564ae2626 100644 --- a/ui/qt/main_window_slots.cpp +++ b/ui/qt/main_window_slots.cpp @@ -1046,7 +1046,7 @@ void MainWindow::recentActionTriggered() { void MainWindow::setMenusForSelectedPacket() { - gboolean is_ip = FALSE, is_tcp = FALSE, is_udp = FALSE, is_sctp = FALSE, is_ssl = FALSE, is_rtp = FALSE, is_lte_rlc = FALSE; + gboolean is_ip = FALSE, is_tcp = FALSE, is_udp = FALSE, is_sctp = FALSE, is_ssl = FALSE, is_rtp = FALSE, is_lte_rlc = FALSE, is_http = FALSE; /* Making the menu context-sensitive allows for easier selection of the desired item and has the added benefit, with large captures, of @@ -1100,6 +1100,7 @@ void MainWindow::setMenusForSelectedPacket() proto_get_frame_protocols(capture_file_.capFile()->edt->pi.layers, &is_ip, &is_tcp, &is_udp, &is_sctp, &is_ssl, &is_rtp, &is_lte_rlc); + is_http = proto_is_frame_protocol(capture_file_.capFile()->edt->pi.layers, "http"); } } @@ -1148,6 +1149,7 @@ void MainWindow::setMenusForSelectedPacket() main_ui_->actionAnalyzeFollowTCPStream->setEnabled(is_tcp); main_ui_->actionAnalyzeFollowUDPStream->setEnabled(is_udp); main_ui_->actionAnalyzeFollowSSLStream->setEnabled(is_ssl); + main_ui_->actionAnalyzeFollowHTTPStream->setEnabled(is_http); foreach (QAction *cc_action, cc_actions) { cc_action->setEnabled(frame_selected); @@ -2671,6 +2673,11 @@ void MainWindow::on_actionAnalyzeFollowSSLStream_triggered() openFollowStreamDialog(FOLLOW_SSL); } +void MainWindow::on_actionAnalyzeFollowHTTPStream_triggered() +{ + openFollowStreamDialog(FOLLOW_HTTP); +} + void MainWindow::openSCTPAllAssocsDialog() { SCTPAllAssocsDialog *sctp_dialog = new SCTPAllAssocsDialog(this, capture_file_.capFile()); diff --git a/ui/qt/packet_list.cpp b/ui/qt/packet_list.cpp index 0f5d57d694..11d7e2e585 100644 --- a/ui/qt/packet_list.cpp +++ b/ui/qt/packet_list.cpp @@ -326,6 +326,7 @@ PacketList::PacketList(QWidget *parent) : submenu->addAction(window()->findChild("actionAnalyzeFollowTCPStream")); submenu->addAction(window()->findChild("actionAnalyzeFollowUDPStream")); submenu->addAction(window()->findChild("actionAnalyzeFollowSSLStream")); + submenu->addAction(window()->findChild("actionAnalyzeFollowHTTPStream")); ctx_menu_.addSeparator(); diff --git a/ui/qt/proto_tree.cpp b/ui/qt/proto_tree.cpp index 577cf1f8c9..a4fcd3458e 100644 --- a/ui/qt/proto_tree.cpp +++ b/ui/qt/proto_tree.cpp @@ -209,6 +209,7 @@ ProtoTree::ProtoTree(QWidget *parent) : submenu->addAction(window()->findChild("actionAnalyzeFollowTCPStream")); submenu->addAction(window()->findChild("actionAnalyzeFollowUDPStream")); submenu->addAction(window()->findChild("actionAnalyzeFollowSSLStream")); + submenu->addAction(window()->findChild("actionAnalyzeFollowHTTPStream")); ctx_menu_.addSeparator(); main_menu_item = window()->findChild("menuEditCopy");