diff options
author | Alexis La Goutte <alexis.lagoutte@gmail.com> | 2013-09-09 19:30:30 +0000 |
---|---|---|
committer | Alexis La Goutte <alexis.lagoutte@gmail.com> | 2013-09-09 19:30:30 +0000 |
commit | 80f9326b2fbf8857f221b6ee6d44c3982cdcbff9 (patch) | |
tree | d5dd5ce48d3da0595cc10da6319c729604511c23 | |
parent | 41174fa490f141aaf1fdddfcb4f445ecd95888f7 (diff) | |
download | wireshark-80f9326b2fbf8857f221b6ee6d44c3982cdcbff9.tar.gz |
From Thomas ERSFELD (GSoC13)
Add TCP/UDP/SSL Follow feature to QtShark
Known issue :
* Duplicate code with GTK (function need follow_info_t struct but in GTK there is some GWidget variable in struct)
* Sometimes TCP Follow fail...
svn path=/trunk/; revision=51883
-rw-r--r-- | ui/CMakeLists.txt | 1 | ||||
-rw-r--r-- | ui/Makefile.common | 2 | ||||
-rw-r--r-- | ui/follow.c | 125 | ||||
-rw-r--r-- | ui/follow.h | 128 | ||||
-rw-r--r-- | ui/gtk/follow_ssl.c | 8 | ||||
-rw-r--r-- | ui/gtk/follow_stream.h | 36 | ||||
-rw-r--r-- | ui/gtk/follow_tcp.c | 78 | ||||
-rw-r--r-- | ui/gtk/follow_udp.c | 2 | ||||
-rw-r--r-- | ui/qt/CMakeLists.txt | 3 | ||||
-rw-r--r-- | ui/qt/Makefile.common | 12 | ||||
-rw-r--r-- | ui/qt/QtShark.pro | 4 | ||||
-rw-r--r-- | ui/qt/follow_stream_dialog.cpp | 1192 | ||||
-rw-r--r-- | ui/qt/follow_stream_dialog.h | 161 | ||||
-rw-r--r-- | ui/qt/follow_stream_dialog.ui | 125 | ||||
-rw-r--r-- | ui/qt/main_window.cpp | 36 | ||||
-rw-r--r-- | ui/qt/main_window.h | 8 | ||||
-rw-r--r-- | ui/qt/main_window.ui | 29 | ||||
-rw-r--r-- | ui/qt/main_window_slots.cpp | 64 | ||||
-rw-r--r-- | ui/qt/packet_list.cpp | 57 | ||||
-rw-r--r-- | ui/qt/packet_list.h | 2 |
20 files changed, 1945 insertions, 128 deletions
diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt index 81c39dc966..ff15e2eb99 100644 --- a/ui/CMakeLists.txt +++ b/ui/CMakeLists.txt @@ -28,6 +28,7 @@ set(COMMON_UI_SRC export_object_dicom.c export_object_http.c export_object_smb.c + follow.c help_url.c packet_list_utils.c iface_lists.c diff --git a/ui/Makefile.common b/ui/Makefile.common index 6246cdceb3..fc4ceb8825 100644 --- a/ui/Makefile.common +++ b/ui/Makefile.common @@ -49,6 +49,7 @@ WIRESHARK_UI_SRC = \ export_object_dicom.c \ export_object_http.c \ export_object_smb.c \ + follow.c \ iface_lists.c \ help_url.c \ packet_list_utils.c \ @@ -70,6 +71,7 @@ noinst_HEADERS = \ export_object.h \ last_open_dir.h \ file_dialog.h \ + follow.h \ help_url.h \ packet_list_utils.h \ iface_lists.h \ diff --git a/ui/follow.c b/ui/follow.c new file mode 100644 index 0000000000..fe08c02156 --- /dev/null +++ b/ui/follow.c @@ -0,0 +1,125 @@ +/* follow.c + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * 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 <string.h> + +#include <glib.h> + +#include <epan/filesystem.h> +#include <epan/dfilter/dfilter.h> + +#include "ui/follow.h" + +#ifdef HAVE_LIBZ +char * +sgetline(char *str, int *next) +{ + char *end; + + end = strstr(str, "\r\n"); + if (!end) { + *next = (int)strlen(str); + return NULL; + } + *end = '\0'; + *next = (int)(end-str+2); + return str; +} + +gboolean +parse_http_header(char *data, size_t len, size_t *content_start) +{ + char *tmp, *copy, *line; + size_t pos = 0; + int next_line; + gboolean is_gzipped; + + /* XXX handle case where only partial header is passed in here. + * we should pass something back to indicate whether header is complete. + * (if not, is_gzipped is may still be unknown) + */ + + /* + * In order to parse header, we duplicate data and tokenize lines. + * We aren't interested in actual data, so use g_strndup instead of memcpy + * to (possibly) copy fewer bytes (e.g., if a nul byte exists in data) + * This also ensures that we have a terminated string for futher processing. + */ + tmp = copy = g_strndup(data, len); + if (!tmp) { + *content_start = 0; + return FALSE; + } + + /* skip HTTP... line*/ + /*line = */sgetline(tmp, &next_line); + + tmp += next_line; + pos += next_line; + + is_gzipped = FALSE; + + *content_start = -1; + while ((line = sgetline(tmp, &next_line))) { + char *key, *val, *c; + + tmp += next_line; + pos += next_line; + + if (strlen(line) == 0) { + /* end of header*/ + break; + } + + c = strchr(line, ':'); + if (!c) break; + + key = line; + *c = '\0'; + val = c+2; + + if (!strcmp(key, "Content-Encoding") && strstr(val, "gzip")) { + is_gzipped = TRUE; + } + } + *content_start = pos; + g_free(copy); + return is_gzipped; +} +#endif + + +/* + * Editor modelines + * + * Local Variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * ex: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/ui/follow.h b/ui/follow.h new file mode 100644 index 0000000000..ec026f900e --- /dev/null +++ b/ui/follow.h @@ -0,0 +1,128 @@ +/* follow.h + * Common routines for following data streams (qt/gtk) + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * 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__H__ +#define __FOLLOW__H__ + +#include "glib.h" + +#include <epan/follow.h> +#include <epan/dissectors/packet-ipv6.h> +#include <epan/prefs.h> +#include <epan/addr_resolv.h> +#include <epan/charsets.h> +#include <epan/epan_dissect.h> +#include <epan/filesystem.h> +#include <epan/ipproto.h> +#include <epan/charsets.h> + +#include "config.h" +#include "globals.h" +#include "file.h" + +#include "version_info.h" + +#ifdef HAVE_LIBZ +#include <zlib.h> +#endif + +#ifdef SSL_PLUGIN +#include "packet-ssl-utils.h" +#else +#include <epan/dissectors/packet-ssl-utils.h> +#endif + +#ifndef QT_CORE_LIB +#include <gtk/gtk.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef struct { + gboolean is_from_server; + StringInfo data; +} SslDecryptedRecord; + +/* Type of follow we are doing */ +typedef enum { + FOLLOW_TCP, + FOLLOW_SSL, + FOLLOW_UDP +} follow_type_t; + +/* Show Stream */ +typedef enum { + FROM_CLIENT, + FROM_SERVER, + BOTH_HOSTS +} show_stream_t; + +/* Show Type */ +typedef enum { + SHOW_ASCII, + SHOW_EBCDIC, + SHOW_HEXDUMP, + SHOW_CARRAY, + SHOW_RAW +} show_type_t; + +typedef enum { + FRS_OK, + FRS_OPEN_ERROR, + FRS_READ_ERROR, + FRS_PRINT_ERROR +} frs_return_t; + +typedef struct { + gboolean is_server; + GByteArray *data; +} follow_record_t; + +char * +sgetline(char *str, int *next); + +gboolean +parse_http_header(char *data, size_t len, size_t *content_start); + +#ifdef __cplusplus +} +#endif + +#endif + +/* + * Editor modelines + * + * Local Variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * ex: 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 42a79a6497..8ef803bc70 100644 --- a/ui/gtk/follow_ssl.c +++ b/ui/gtk/follow_ssl.c @@ -57,6 +57,7 @@ #include "ui/gtk/keys.h" #include "ui/gtk/gui_utils.h" #include "ui/gtk/font_utils.h" +#include "ui/follow.h" #include "ui/gtk/follow_ssl.h" #include "ui/gtk/follow_stream.h" @@ -66,12 +67,6 @@ #include <epan/dissectors/packet-ssl-utils.h> #endif - -typedef struct { - gboolean is_from_server; - StringInfo data; -} SslDecryptedRecord; - static int ssl_queue_packet_data(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _U_, const void *ssl) { @@ -130,7 +125,6 @@ ssl_queue_packet_data(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _U_ return 0; } - /* Follow the SSL 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). */ diff --git a/ui/gtk/follow_stream.h b/ui/gtk/follow_stream.h index c55abf8abc..e88ba23e1c 100644 --- a/ui/gtk/follow_stream.h +++ b/ui/gtk/follow_stream.h @@ -27,41 +27,7 @@ #define __FOLLOW_STREAM_H__ #include <gtk/gtk.h> - -/* Type of follow we are doing */ -typedef enum { - FOLLOW_TCP, - FOLLOW_SSL, - FOLLOW_UDP -} follow_type_t; - -/* Show Stream */ -typedef enum { - FROM_CLIENT, - FROM_SERVER, - BOTH_HOSTS -} show_stream_t; - -/* Show Type */ -typedef enum { - SHOW_ASCII, - SHOW_EBCDIC, - SHOW_HEXDUMP, - SHOW_CARRAY, - SHOW_RAW -} show_type_t; - -typedef enum { - FRS_OK, - FRS_OPEN_ERROR, - FRS_READ_ERROR, - FRS_PRINT_ERROR -} frs_return_t; - -typedef struct { - gboolean is_server; - GByteArray *data; -} follow_record_t; +#include <ui/follow.h> typedef struct { follow_type_t follow_type; diff --git a/ui/gtk/follow_tcp.c b/ui/gtk/follow_tcp.c index c188e0134c..8ca8be452a 100644 --- a/ui/gtk/follow_tcp.c +++ b/ui/gtk/follow_tcp.c @@ -316,84 +316,6 @@ follow_tcp_stream_cb(GtkWidget * w _U_, gpointer data _U_) data_out_file = NULL; } -#ifdef HAVE_LIBZ -static char * -sgetline(char *str, int *next) -{ - char *end; - - end = strstr(str, "\r\n"); - if (!end) { - *next = (int)strlen(str); - return NULL; - } - *end = '\0'; - *next = (int)(end-str+2); - return str; -} - -static gboolean -parse_http_header(char *data, size_t len, size_t *content_start) -{ - char *tmp, *copy, *line; - size_t pos = 0; - int next_line; - gboolean is_gzipped; - - /* XXX handle case where only partial header is passed in here. - * we should pass something back to indicate whether header is complete. - * (if not, is_gzipped is may still be unknown) - */ - - /* - * In order to parse header, we duplicate data and tokenize lines. - * We aren't interested in actual data, so use g_strndup instead of memcpy - * to (possibly) copy fewer bytes (e.g., if a nul byte exists in data) - * This also ensures that we have a terminated string for futher processing. - */ - tmp = copy = g_strndup(data, len); - if (!tmp) { - *content_start = 0; - return FALSE; - } - - /* skip HTTP... line*/ - /*line = */sgetline(tmp, &next_line); - - tmp += next_line; - pos += next_line; - - is_gzipped = FALSE; - - *content_start = -1; - while ((line = sgetline(tmp, &next_line))) { - char *key, *val, *c; - - tmp += next_line; - pos += next_line; - - if (strlen(line) == 0) { - /* end of header*/ - break; - } - - c = strchr(line, ':'); - if (!c) break; - - key = line; - *c = '\0'; - val = c+2; - - if (!strcmp(key, "Content-Encoding") && strstr(val, "gzip")) { - is_gzipped = TRUE; - } - } - *content_start = pos; - g_free(copy); - return is_gzipped; -} -#endif - #define FLT_BUF_SIZE 1024 /* diff --git a/ui/gtk/follow_udp.c b/ui/gtk/follow_udp.c index 37061de568..93a5106b7e 100644 --- a/ui/gtk/follow_udp.c +++ b/ui/gtk/follow_udp.c @@ -39,6 +39,7 @@ #include <ui/utf8_entities.h> #include "gtkglobals.h" +#include "ui/follow.h" #include "ui/gtk/follow_stream.h" #include "ui/gtk/keys.h" #include "ui/gtk/main.h" @@ -76,7 +77,6 @@ udp_queue_packet_data(void *tapdata, packet_info *pinfo, return 0; } - /* Follow the UDP 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). */ diff --git a/ui/qt/CMakeLists.txt b/ui/qt/CMakeLists.txt index 879b71a1f7..daee6e0038 100644 --- a/ui/qt/CMakeLists.txt +++ b/ui/qt/CMakeLists.txt @@ -44,6 +44,7 @@ set(QTSHARK_H_SRC export_object_dialog.h file_set_dialog.h filter_expressions_preferences_frame.h + follow_stream_dialog.h font_color_preferences_frame.h import_text_dialog.h interface_tree.h @@ -104,6 +105,7 @@ set(CLEAN_FILES export_object_dialog.cpp file_set_dialog.cpp filter_expressions_preferences_frame.cpp + follow_stream_dialog.cpp font_color_preferences_frame.cpp import_text_dialog.cpp interface_tree.cpp @@ -152,6 +154,7 @@ set(QTSHARK_UI export_object_dialog.ui file_set_dialog.ui filter_expressions_preferences_frame.ui + follow_stream_dialog.ui font_color_preferences_frame.ui import_text_dialog.ui layout_preferences_frame.ui diff --git a/ui/qt/Makefile.common b/ui/qt/Makefile.common index 0781e8f526..d294285fb1 100644 --- a/ui/qt/Makefile.common +++ b/ui/qt/Makefile.common @@ -35,8 +35,9 @@ NODIST_GENERATED_HEADER_FILES = \ ui_column_preferences_frame.h \ ui_export_object_dialog.h \ ui_file_set_dialog.h \ - ui_font_color_preferences_frame.h \ ui_filter_expressions_preferences_frame.h \ + ui_follow_stream_dialog.h \ + ui_font_color_preferences_frame.h \ ui_import_text_dialog.h \ ui_layout_preferences_frame.h \ ui_main_welcome.h \ @@ -50,8 +51,8 @@ NODIST_GENERATED_HEADER_FILES = \ ui_print_dialog.h \ ui_profile_dialog.h \ ui_search_frame.h \ - ui_summary_dialog.h \ ui_splash_overlay.h \ + ui_summary_dialog.h \ ui_tcp_stream_dialog.h \ ui_time_shift_dialog.h \ ui_uat_dialog.h @@ -109,6 +110,7 @@ MOC_HDRS = \ export_object_dialog.h \ file_set_dialog.h \ filter_expressions_preferences_frame.h \ + follow_stream_dialog.h \ font_color_preferences_frame.h \ import_text_dialog.h \ interface_tree.h \ @@ -134,9 +136,9 @@ MOC_HDRS = \ related_packet_delegate.h \ search_frame.h \ simple_dialog_qt.h \ - summary_dialog.h \ sparkline_delegate.h \ splash_overlay.h \ + summary_dialog.h \ syntax_line_edit.h \ tcp_stream_dialog.h \ time_shift_dialog.h \ @@ -153,6 +155,7 @@ UI_FILES = \ export_object_dialog.ui \ file_set_dialog.ui \ filter_expressions_preferences_frame.ui \ + follow_stream_dialog.ui \ font_color_preferences_frame.ui \ import_text_dialog.ui \ layout_preferences_frame.ui \ @@ -167,8 +170,8 @@ UI_FILES = \ print_dialog.ui \ profile_dialog.ui \ search_frame.ui \ - summary_dialog.ui \ splash_overlay.ui \ + summary_dialog.ui \ tcp_stream_dialog.ui \ time_shift_dialog.ui \ uat_dialog.ui @@ -239,6 +242,7 @@ WIRESHARK_QT_SRC = \ export_object_dialog.cpp \ file_set_dialog.cpp \ filter_expressions_preferences_frame.cpp \ + follow_stream_dialog.cpp \ font_color_preferences_frame.cpp \ import_text_dialog.cpp \ interface_tree.cpp \ diff --git a/ui/qt/QtShark.pro b/ui/qt/QtShark.pro index 40cca77016..9f1fb142ee 100644 --- a/ui/qt/QtShark.pro +++ b/ui/qt/QtShark.pro @@ -210,6 +210,7 @@ FORMS += \ export_object_dialog.ui \ file_set_dialog.ui \ filter_expressions_preferences_frame.ui \ + follow_stream_dialog.ui \ font_color_preferences_frame.ui \ import_text_dialog.ui \ layout_preferences_frame.ui \ @@ -230,7 +231,6 @@ FORMS += \ uat_dialog.ui \ tcp_stream_dialog.ui - win32 { ## These should be in config.pri ?? !isEmpty(PORTAUDIO_DIR) { PA_OBJECTS = \ @@ -260,6 +260,7 @@ HEADERS += $$HEADERS_WS_C \ export_dissection_dialog.h \ export_object_dialog.h \ filter_expressions_preferences_frame.h \ + follow_stream_dialog.h \ font_color_preferences_frame.h \ layout_preferences_frame.h \ main_window_preferences_frame.h \ @@ -551,6 +552,7 @@ SOURCES += \ export_object_dialog.cpp \ file_set_dialog.cpp \ filter_expressions_preferences_frame.cpp \ + follow_stream_dialog.cpp \ font_color_preferences_frame.cpp \ import_text_dialog.cpp \ interface_tree.cpp \ diff --git a/ui/qt/follow_stream_dialog.cpp b/ui/qt/follow_stream_dialog.cpp new file mode 100644 index 0000000000..516dd3ec8c --- /dev/null +++ b/ui/qt/follow_stream_dialog.cpp @@ -0,0 +1,1192 @@ +/* follow_stream_dialog.cpp + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * 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 "follow_stream_dialog.h" +#include "ui_follow_stream_dialog.h" + +#include "main_window.h" +#include "wireshark_application.h" + +#include "ui/follow.h" + +FollowStreamDialog::FollowStreamDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::FollowStreamDialog) +{ + follow_info = NULL; + ui->setupUi(this); + + this->setFixedSize(this->size()); + + connect(ui->buttonBox, SIGNAL(helpRequested()), this, SLOT(HelpButton())); + + bFilterOut = ui->buttonBox->addButton(tr("Filter out this stream"), QDialogButtonBox::ActionRole); + connect(bFilterOut, SIGNAL(clicked()), this, SLOT(FilterOut())); + + + bFind = ui->buttonBox->addButton(tr("Find"), QDialogButtonBox::ActionRole); + connect(bFind, SIGNAL(clicked()), this, SLOT(FindText())); + + bPrint = ui->buttonBox->addButton(tr("Print"), QDialogButtonBox::ActionRole); + connect(bPrint, SIGNAL(clicked()), this, SLOT(Print())); + + bSave = ui->buttonBox->addButton(tr("Save as..."), QDialogButtonBox::ActionRole); + connect(bSave, SIGNAL(clicked()), this, SLOT(SaveAs())); + + save_as = false; +} + +void FollowStreamDialog::Print() +{ +#ifndef QT_NO_PRINTER + QPrinter printer(QPrinter::HighResolution); + QPrintDialog dialog(&printer, this); + if ( dialog.exec() == QDialog::Accepted) + ui->teStreamContent->print(&printer); +#endif +} + +void FollowStreamDialog::FindText() +{ + bool ok; + QString text = QInputDialog::getText(this, tr("Wireshark: Find text"), + tr("Find text:"), QLineEdit::Normal, + " ", &ok); + if (ok && !text.isEmpty()) + { + ui->teStreamContent->moveCursor(QTextCursor::Start); + ui->teStreamContent->find(text); + } +} + +void FollowStreamDialog::SaveAs() +{ + QString file_name = QFileDialog::getSaveFileName(this, "Wireshark: Save stream content as"); + file.setFileName(file_name); + file.open( QIODevice::WriteOnly ); + QTextStream out(&file); + + save_as = true; + + follow_read_stream(); + + if (follow_info->show_type != SHOW_RAW) + { + out << ui->teStreamContent->toPlainText(); + } + + save_as = false; + + file.close(); +} + +void FollowStreamDialog::HelpButton() +{ + wsApp->helpTopicAction(HELP_FOLLOW_STREAM_DIALOG); +} + +void FollowStreamDialog::FilterOut() +{ + + QString filter = QString(follow_info->filter_out_filter); + emit updateFilter(filter, TRUE); + + this->close(); +} + +void FollowStreamDialog::on_cbDirections_currentIndexChanged(int index) +{ + if (!follow_info) + return; + + switch(index) + { + case 0 : + follow_info->show_stream = BOTH_HOSTS; + break; + case 1 : + follow_info->show_stream = FROM_SERVER; + break; + case 2 : + follow_info->show_stream = FROM_CLIENT; + break; + default: + return; + } + + follow_read_stream(); +} + +void FollowStreamDialog::on_cbCharset_currentIndexChanged(int index) +{ + switch (index) + { + case 0: + follow_info->show_type = SHOW_ASCII; + break; + + case 1: + follow_info->show_type = SHOW_EBCDIC; + break; + + case 2: + follow_info->show_type = SHOW_CARRAY; + break; + + case 3: + follow_info->show_type = SHOW_HEXDUMP; + break; + + case 4: + follow_info->show_type = SHOW_RAW; + break; + + default: + return; + } + + follow_read_stream(); +} + +frs_return_t +FollowStreamDialog::follow_read_stream() +{ + ui->teStreamContent->clear(); + + switch(follow_info->follow_type) { + + case FOLLOW_TCP : + return follow_read_tcp_stream(); + + case FOLLOW_UDP : + return follow_read_udp_stream(); + + case FOLLOW_SSL : + return follow_read_ssl_stream(); + + default : + g_assert_not_reached(); + return (frs_return_t)0; + } +} + +//Copy from ui/gtk/follow_udp.c +static int +udp_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_length(next_tvb)); + follow_record->data = g_byte_array_append(follow_record->data, + tvb_get_ptr(next_tvb, 0, -1), + tvb_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 0; +} + +//Copy from ui/gtk/follow_ssl.c +static int +ssl_queue_packet_data(void *tapdata, packet_info *pinfo, epan_dissect_t *edt _U_, const void *ssl) +{ + follow_info_t * follow_info = (follow_info_t*) tapdata; + SslDecryptedRecord * rec = NULL; + SslDataInfo * appl_data = NULL; + int proto_ssl = GPOINTER_TO_INT(ssl); + SslPacketInfo * pi = NULL; + show_stream_t from = FROM_CLIENT; + + /* Skip packets without decrypted payload data. */ + pi = (SslPacketInfo*) p_get_proto_data(pinfo->fd, proto_ssl, 0); + if (!pi || !pi->appl_data) return 0; + + /* Compute the packet's sender. */ + 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) { + from = FROM_CLIENT; + } else { + from = FROM_SERVER; + } + + for (appl_data = pi->appl_data; appl_data != NULL; appl_data = appl_data->next) { + + /* TCP segments that contain the end of two or more SSL PDUs will be + queued to SSL taps for each of those PDUs. Therefore a single + packet could be processed by this SSL tap listener multiple times. + The following test handles that scenario by treating the + follow_info->bytes_written[] values as the next expected + appl_data->seq. Any appl_data instances that fall below that have + already been processed and must be skipped. */ + if (appl_data->seq < follow_info->bytes_written[from]) continue; + + /* Allocate a SslDecryptedRecord to hold the current appl_data + instance's decrypted data. Even though it would be possible to + consolidate multiple appl_data instances into a single rec, it is + beneficial to use a one-to-one mapping. This affords the Follow + Stream dialog view modes (ASCII, EBCDIC, Hex Dump, C Arrays, Raw) + the opportunity to accurately reflect SSL PDU boundaries. Currently + the Hex Dump view does by starting a new line, and the C Arrays + view does by starting a new array declaration. */ + rec = (SslDecryptedRecord*) g_malloc(sizeof(SslDecryptedRecord) + appl_data->plain_data.data_len); + rec->is_from_server = from == FROM_SERVER; + rec->data.data = (guchar*) (rec + 1); + rec->data.data_len = appl_data->plain_data.data_len; + memcpy(rec->data.data, appl_data->plain_data.data, appl_data->plain_data.data_len); + + /* Append the record to the follow_info structure. */ + follow_info->payload = g_list_append(follow_info->payload, rec); + follow_info->bytes_written[from] += rec->data.data_len; + } + + return 0; +} + + +/* + * 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 +FollowStreamDialog::follow_read_ssl_stream() +{ + guint32 global_client_pos = 0, global_server_pos = 0; + guint32 server_packet_count = 0; + guint32 client_packet_count = 0; + guint32 * global_pos; + GList * cur; + frs_return_t frs_return; + + for (cur = follow_info->payload; cur; cur = g_list_next(cur)) { + SslDecryptedRecord * rec = (SslDecryptedRecord*) cur->data; + gboolean include_rec = FALSE; + + if (rec->is_from_server) { + global_pos = &global_server_pos; + include_rec = (follow_info->show_stream == BOTH_HOSTS) || + (follow_info->show_stream == FROM_SERVER); + } else { + global_pos = &global_client_pos; + include_rec = (follow_info->show_stream == BOTH_HOSTS) || + (follow_info->show_stream == FROM_CLIENT); + } + + if (include_rec) { + size_t nchars = rec->data.data_len; + gchar *buffer = (gchar *)g_memdup(rec->data.data, (guint) nchars); + + frs_return = follow_show(buffer, nchars, + rec->is_from_server, global_pos, + &server_packet_count, &client_packet_count); + g_free(buffer); + if (frs_return == FRS_PRINT_ERROR) + return frs_return; + } + } + + return FRS_OK; +} + +void +FollowStreamDialog::follow_stream() +{ + follow_stats_t stats; + + follow_info->show_type = SHOW_RAW; + follow_info->show_stream = BOTH_HOSTS; + + /* Stream to show */ + follow_stats(&stats); + + follow_info->is_ipv6 = stats.is_ipv6; + + follow_read_stream(); +} + + + + +void FollowStreamDialog::add_text(char *buffer, size_t nchars, gboolean is_from_server) +{ + size_t i; + QString buf; + gchar *str; + + if (save_as == true) + { + //FILE *fh = (FILE *)arg; + size_t nwritten; + int FileDescriptor = file.handle(); + FILE* fh = fdopen(dup(FileDescriptor), "wb"); + nwritten = fwrite(buffer, 1, nchars, fh); + fclose(fh); + return; + if (nwritten != nchars) + return; + } + + QColor tagserver_fg = QColor(prefs.st_server_fg.red>>8, + prefs.st_server_fg.green>>8, + prefs.st_server_fg.blue>>8); + QColor tagserver_bg = QColor(prefs.st_server_bg.red>>8, + prefs.st_server_bg.green>>8, + prefs.st_server_bg.blue>>8); + + QColor tagclient_fg = QColor(prefs.st_client_fg.red>>8, + prefs.st_client_fg.green>>8, + prefs.st_client_fg.blue>>8); + QColor tagclient_bg = QColor(prefs.st_client_bg.red>>8, + prefs.st_client_bg.green>>8, + prefs.st_client_bg.blue>>8); + + for (i = 0; i < nchars; i++) { + if (buffer[i] == '\n' || buffer[i] == '\r') + continue; + if (! isprint((guchar)buffer[i])) { + buffer[i] = '.'; + } + } + + + + /* convert unterminated char array to a zero terminated string */ + str = (char *)g_malloc(nchars + 1); + memcpy(str, buffer, nchars); + str[nchars] = 0; + buf = QString(str); + g_free(str); + + + + if (is_from_server) + { + ui->teStreamContent->setTextColor(tagserver_fg); + ui->teStreamContent->setTextBackgroundColor(tagserver_bg); + ui->teStreamContent->insertPlainText(buf); + } + else + { + ui->teStreamContent->setTextColor(tagclient_fg); + ui->teStreamContent->setTextBackgroundColor(tagclient_bg); + ui->teStreamContent->insertPlainText(buf); + } +} + + +frs_return_t +FollowStreamDialog::follow_show(char *buffer, size_t nchars, gboolean is_from_server, + guint32 *global_pos, guint32 *server_packet_count, + guint32 *client_packet_count) +{ + gchar initbuf[256]; + guint32 current_pos; + static const gchar hexchars[16] = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; + + switch (follow_info->show_type) { + + case SHOW_EBCDIC: + /* If our native arch is ASCII, call: */ + EBCDIC_to_ASCII((guint8*)buffer, (guint) nchars); + add_text(buffer, nchars, is_from_server); + break; + + case SHOW_ASCII: + /* If our native arch is EBCDIC, call: + * ASCII_TO_EBCDIC(buffer, nchars); + */ + add_text(buffer, nchars, is_from_server); + break; + + case SHOW_RAW: + /* Don't translate, no matter what the native arch + * is. + */ + add_text(buffer, nchars, is_from_server); + break; + + case SHOW_HEXDUMP: + current_pos = 0; + while (current_pos < nchars) { + gchar hexbuf[256]; + int i; + gchar *cur = hexbuf, *ascii_start; + + /* is_from_server indentation : put 4 spaces at the + * beginning of the string */ + /* XXX - We might want to prepend each line with "C" or "S" instead. */ + if (is_from_server && follow_info->show_stream == BOTH_HOSTS) { + memset(cur, ' ', 4); + cur += 4; + } + cur += g_snprintf(cur, 20, "%08X ", *global_pos); + /* 49 is space consumed by hex chars */ + ascii_start = cur + 49; + for (i = 0; i < 16 && current_pos + i < nchars; i++) { + *cur++ = + hexchars[(buffer[current_pos + i] & 0xf0) >> 4]; + *cur++ = + hexchars[buffer[current_pos + i] & 0x0f]; + *cur++ = ' '; + if (i == 7) + *cur++ = ' '; + } + /* Fill it up if column isn't complete */ + while (cur < ascii_start) + *cur++ = ' '; + + /* Now dump bytes as text */ + for (i = 0; i < 16 && current_pos + i < nchars; i++) { + *cur++ = + (isprint((guchar)buffer[current_pos + i]) ? + buffer[current_pos + i] : '.' ); + if (i == 7) { + *cur++ = ' '; + } + } + current_pos += i; + (*global_pos) += i; + *cur++ = '\n'; + *cur = 0; + + add_text(hexbuf, strlen(hexbuf), is_from_server); + } + break; + + case SHOW_CARRAY: + current_pos = 0; + g_snprintf(initbuf, sizeof(initbuf), "char peer%d_%d[] = {\n", + is_from_server ? 1 : 0, + is_from_server ? (*server_packet_count)++ : (*client_packet_count)++); + add_text(initbuf, strlen(initbuf), is_from_server); + + while (current_pos < nchars) { + gchar hexbuf[256]; + int i, cur; + + cur = 0; + for (i = 0; i < 8 && current_pos + i < nchars; i++) { + /* Prepend entries with "0x" */ + hexbuf[cur++] = '0'; + hexbuf[cur++] = 'x'; + hexbuf[cur++] = + hexchars[(buffer[current_pos + i] & 0xf0) >> 4]; + hexbuf[cur++] = + hexchars[buffer[current_pos + i] & 0x0f]; + + /* Delimit array entries with a comma */ + if (current_pos + i + 1 < nchars) + hexbuf[cur++] = ','; + + hexbuf[cur++] = ' '; + } + + /* Terminate the array if we are at the end */ + if (current_pos + i == nchars) { + hexbuf[cur++] = '}'; + hexbuf[cur++] = ';'; + } + + current_pos += i; + (*global_pos) += i; + hexbuf[cur++] = '\n'; + hexbuf[cur] = 0; + add_text(hexbuf, strlen(hexbuf), is_from_server); + } + break; + } + + return FRS_OK; +} + + + +bool FollowStreamDialog::Follow(QString previous_filter_, follow_type_t type) +{ + int tmp_fd; + gchar *follow_filter; + const gchar *previous_filter = previous_filter_.toStdString().c_str(); + int filter_out_filter_len, previous_filter_len; + const char *hostname0 = NULL, *hostname1 = NULL; + char *port0 = NULL, *port1 = NULL; + gchar *server_to_client_string = NULL; + gchar *client_to_server_string = NULL; + gchar *both_directions_string = NULL; + follow_stats_t stats; + tcp_stream_chunk sc; + size_t nchars; + gchar *data_out_filename; + GString * msg; + + if (cfile.edt == NULL) + { + QMessageBox::warning(this, tr("Error following stream."), tr("Capture file invalid.")); + return false; + } + + switch (type) + { + case FOLLOW_TCP: + if (cfile.edt->pi.ipproto != IP_PROTO_TCP) { + QMessageBox::warning(this, tr("Error following stream."), tr("Please make sure you have a TCP packet selected.")); + return false; + } + break; + case FOLLOW_UDP: + if (cfile.edt->pi.ipproto != IP_PROTO_UDP) { + QMessageBox::warning(this, tr("Error following stream."), tr("Please make sure you have a UDP packet selected.")); + return false; + } + break; + case FOLLOW_SSL: + /* we got ssl so we can follow */ + if (!epan_dissect_packet_contains_field(cfile.edt, "ssl")) { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, + "Error following stream. Please make\n" + "sure you have an SSL packet selected."); + return false; + } + break; + } + + follow_info = g_new0(follow_info_t, 1); + follow_info->follow_type = type; + + if (type == FOLLOW_TCP || type == FOLLOW_SSL) + { + /* 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_filter(&cfile.edt->pi); + if (!follow_filter) { + QMessageBox::warning(this, + tr("Error creating filter for this stream."), + tr("A transport or network layer header is needed.")); + g_free(follow_info); + return false; + } + + if (type == FOLLOW_TCP || type == FOLLOW_SSL) + { + /* Create a temporary file into which to dump the reassembled data + from the TCP stream, and set "data_out_file" to refer to it, so + that the TCP code will write to it. + + XXX - it might be nicer to just have the TCP code directly + append stuff to the text widget for the TCP stream window, + if we can arrange that said window not pop up until we're + done. */ + tmp_fd = create_tempfile(&data_out_filename, "follow"); + follow_info->data_out_filename = g_strdup(data_out_filename); + + if (tmp_fd == -1) { + QMessageBox::warning(this, "Error", + "Could not create temporary file %1: %2", + follow_info->data_out_filename, g_strerror(errno)); + g_free(follow_info->data_out_filename); + g_free(follow_info); + g_free(follow_filter); + return false; + } + + data_out_file = fdopen(tmp_fd, "w+b"); + if (data_out_file == NULL) { + QMessageBox::warning(this, "Error", + "Could not create temporary file %1: %2", + follow_info->data_out_filename, g_strerror(errno)); + //ws_close(tmp_fd); + ws_unlink(follow_info->data_out_filename); + g_free(follow_info->data_out_filename); + g_free(follow_info); + g_free(follow_filter); + return false; + } + } + + + /* 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) + strlen(previous_filter) + 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); + } + + switch (type) + { + case FOLLOW_TCP: + + break; + case FOLLOW_UDP: + /* data will be passed via tap callback*/ + msg = register_tap_listener("udp_follow", follow_info, follow_filter, + 0, NULL, udp_queue_packet_data, NULL); + if (msg) { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, + "Can't register udp_follow tap: %s\n", + msg->str); + g_free(follow_info->filter_out_filter); + g_free(follow_info); + g_free(follow_filter); + return false; + } + break; + case FOLLOW_SSL: + /* we got ssl so we can follow */ + msg = register_tap_listener("ssl", follow_info, follow_filter, 0, + NULL, ssl_queue_packet_data, NULL); + if (msg) + { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, + "Can't register ssl tap: %s\n",msg->str); + g_free(follow_info->filter_out_filter); + g_free(follow_info); + g_free(follow_filter); + return false; + } + break; + } + + + + /* Run the display filter so it goes in effect - even if it's the + same as the previous display filter. */ + QString filter = QString(follow_filter); + emit updateFilter(filter, TRUE); + + switch (type) + { + case FOLLOW_TCP: + + break; + case FOLLOW_UDP: + remove_tap_listener(follow_info); + break; + case FOLLOW_SSL: + remove_tap_listener(follow_info); + break; + } + + + if (type == FOLLOW_TCP) + { + /* Check whether we got any data written to the file. */ + if (empty_tcp_stream) { + QMessageBox::warning(this, "Error", + "The packets in the capture file for that stream have no data."); + //ws_close(tmp_fd); + ws_unlink(follow_info->data_out_filename); + g_free(follow_info->data_out_filename); + g_free(follow_info->filter_out_filter); + g_free(follow_info); + return false; + } + + rewind(data_out_file); + nchars=fread(&sc, 1, sizeof(sc), data_out_file); + if (nchars != sizeof(sc)) { + if (ferror(data_out_file)) { + QMessageBox::warning(this, "Error", + QString(tr("Could not read from temporary file %1: %2")) + .arg(follow_info->data_out_filename) + .arg(g_strerror(errno))); + } else { + QMessageBox::warning(this, "Error", + QString(tr("Short read from temporary file %1: expected %2, got %3")) + .arg(follow_info->data_out_filename) + .arg((unsigned long)sizeof(sc)) + .arg((unsigned long)nchars)); + + } + //ws_close(tmp_fd); + ws_unlink(follow_info->data_out_filename); + g_free(follow_info->data_out_filename); + g_free(follow_info->filter_out_filter); + g_free(follow_info); + return false; + } + fclose(data_out_file); + } + + /* 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, (type == FOLLOW_TCP) ? stats.ip_address[1] : stats.ip_address[0], 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); + } + + switch (type) + { + case FOLLOW_TCP: + port0 = get_tcp_port(stats.port[0]); + port1 = get_tcp_port(stats.port[1]); + break; + case FOLLOW_UDP: + port0 = get_udp_port(stats.port[0]); + port1 = get_udp_port(stats.port[1]); + break; + case FOLLOW_SSL: + port0 = get_tcp_port(stats.port[0]); + port1 = get_tcp_port(stats.port[1]); + break; + } + + follow_info->is_ipv6 = stats.is_ipv6; + + if (type == FOLLOW_TCP) + { + /* Host 0 --> Host 1 */ + if(sc.src_port == stats.port[0]) { + server_to_client_string = + g_strdup_printf("%s:%s " UTF8_RIGHTWARDS_ARROW " %s:%s (%u bytes)", + hostname0, port0, + hostname1, port1, + stats.bytes_written[0]); + } else { + server_to_client_string = + g_strdup_printf("%s:%s " UTF8_RIGHTWARDS_ARROW " %s:%s (%u bytes)", + hostname1, port1, + hostname0,port0, + stats.bytes_written[0]); + } + + /* Host 1 --> Host 0 */ + if(sc.src_port == stats.port[1]) { + client_to_server_string = + g_strdup_printf("%s:%s " UTF8_RIGHTWARDS_ARROW " %s:%s (%u bytes)", + hostname0, port0, + hostname1, port1, + stats.bytes_written[1]); + } else { + client_to_server_string = + g_strdup_printf("%s:%s " UTF8_RIGHTWARDS_ARROW " %s:%s (%u bytes)", + hostname1, port1, + hostname0, port0, + stats.bytes_written[1]); + } + + } + else + { + if(follow_info->client_port == stats.port[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]); + } + } + + /* Both Stream Directions */ + switch (type) + { + case FOLLOW_TCP: + both_directions_string = g_strdup_printf("Entire conversation (%u bytes)", stats.bytes_written[0] + stats.bytes_written[1]); + this->setWindowTitle(QString("Follow TCP Stream (%1)").arg(follow_filter)); + break; + case FOLLOW_UDP: + both_directions_string = g_strdup_printf("Entire conversation (%u bytes)", follow_info->bytes_written[0] + follow_info->bytes_written[1]); + this->setWindowTitle(QString("Follow UDP Stream (%1)").arg(follow_filter)); + break; + case FOLLOW_SSL: + both_directions_string = g_strdup_printf("Entire conversation (%u bytes)", follow_info->bytes_written[0] + follow_info->bytes_written[1]); + this->setWindowTitle(QString("Follow SSL Stream (%1)").arg(follow_filter)); + break; + } + + + ui->cbDirections->clear(); + this->ui->cbDirections->addItem(QString(both_directions_string)); + this->ui->cbDirections->addItem(QString(client_to_server_string)); + this->ui->cbDirections->addItem(QString(server_to_client_string)); + + + follow_stream(); + + /* Free the filter string, as we're done with it. */ + g_free(follow_filter); + + data_out_file = NULL; + + return true; +} + + +#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 +FollowStreamDialog::follow_read_tcp_stream() +{ + FILE *data_out_file; + tcp_stream_chunk sc; + size_t bcount; + size_t bytes_read; + int iplen; + guint8 client_addr[MAX_IPADDR_LEN]; + guint16 client_port = 0; + gboolean is_server; + guint32 global_client_pos = 0, global_server_pos = 0; + guint32 server_packet_count = 0; + guint32 client_packet_count = 0; + guint32 *global_pos; + gboolean skip; + char buffer[FLT_BUF_SIZE+1]; /* +1 to fix ws bug 1043 */ + size_t nchars; + frs_return_t frs_return; +#ifdef HAVE_LIBZ + char outbuffer[FLT_BUF_SIZE+1]; + z_stream strm; + gboolean gunzip = FALSE; + int ret; +#endif + + + iplen = (follow_info->is_ipv6) ? 16 : 4; + + data_out_file = ws_fopen(follow_info->data_out_filename, "rb"); + if (data_out_file == NULL) { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, + "Could not open temporary file %s: %s", follow_info->data_out_filename, + g_strerror(errno)); + return FRS_OPEN_ERROR; + } + + while ((nchars=fread(&sc, 1, sizeof(sc), data_out_file))) { + if (nchars != sizeof(sc)) { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, + "Short read from temporary file %s: expected %lu, got %lu", + follow_info->data_out_filename, + (unsigned long)sizeof(sc), + (unsigned long)nchars); + fclose(data_out_file); + data_out_file = NULL; + return FRS_READ_ERROR; + } + if (client_port == 0) { + memcpy(client_addr, sc.src_addr, iplen); + client_port = sc.src_port; + } + skip = FALSE; + if (memcmp(client_addr, sc.src_addr, iplen) == 0 && + client_port == sc.src_port) { + is_server = FALSE; + global_pos = &global_client_pos; + if (follow_info->show_stream == FROM_SERVER) { + skip = TRUE; + } + } + else { + is_server = TRUE; + global_pos = &global_server_pos; + if (follow_info->show_stream == FROM_CLIENT) { + skip = TRUE; + } + } + + bytes_read = 0; + while (bytes_read < sc.dlen) { + bcount = ((sc.dlen-bytes_read) < FLT_BUF_SIZE) ? (sc.dlen-bytes_read) : FLT_BUF_SIZE; + nchars = fread(buffer, 1, bcount, data_out_file); + if (nchars == 0) + break; + /* XXX - if we don't get "bcount" bytes, is that an error? */ + bytes_read += nchars; + +#ifdef HAVE_LIBZ + /* If we are on the first packet of an HTTP response, check if data is gzip + * compressed. + */ + if (is_server && bytes_read == nchars && !memcmp(buffer, "HTTP", 4)) { + size_t header_len; + gunzip = parse_http_header(buffer, nchars, &header_len); + if (gunzip) { + /* show header (which is not gzipped)*/ + frs_return = follow_show(buffer, + header_len, is_server, global_pos, + &server_packet_count, &client_packet_count); + if (frs_return == FRS_PRINT_ERROR) { + fclose(data_out_file); + data_out_file = NULL; + return frs_return; + } + + /* init gz_stream*/ + strm.next_in = Z_NULL; + strm.avail_in = 0; + strm.next_out = Z_NULL; + strm.avail_out = 0; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + ret = inflateInit2(&strm, MAX_WBITS+16); + if (ret != Z_OK) { + fclose(data_out_file); + data_out_file = NULL; + return FRS_READ_ERROR; + } + + /* prepare remainder of buffer to be inflated below */ + memmove(buffer, buffer+header_len, nchars-header_len); + nchars -= header_len; + } + } + + if (gunzip) { + strm.next_in = (Bytef*)buffer; + strm.avail_in = (int)nchars; + do { + strm.next_out = (Bytef*)outbuffer; + strm.avail_out = FLT_BUF_SIZE; + + ret = inflate(&strm, Z_NO_FLUSH); + if (ret < 0 || ret == Z_NEED_DICT) { + inflateEnd(&strm); + fclose(data_out_file); + data_out_file = NULL; + return FRS_READ_ERROR; + } else if (ret == Z_STREAM_END) { + inflateEnd(&strm); + } + + frs_return = follow_show(outbuffer, + FLT_BUF_SIZE-strm.avail_out, is_server, + global_pos, + &server_packet_count, + &client_packet_count); + if(frs_return == FRS_PRINT_ERROR) { + inflateEnd(&strm); + fclose(data_out_file); + data_out_file = NULL; + return frs_return; + } + } while (strm.avail_out == 0); + skip = TRUE; + } +#endif + if (!skip) + { + frs_return = follow_show(buffer, + nchars, is_server, global_pos, + &server_packet_count, + &client_packet_count); + if(frs_return == FRS_PRINT_ERROR) { + fclose(data_out_file); + data_out_file = NULL; + return frs_return; + } + + } + } + } + + if (ferror(data_out_file)) { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, + "Error reading temporary file %s: %s", follow_info->data_out_filename, + g_strerror(errno)); + fclose(data_out_file); + data_out_file = NULL; + return FRS_READ_ERROR; + } + + fclose(data_out_file); + data_out_file = NULL; + return FRS_OK; +} + + + + +/* + * 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 +FollowStreamDialog::follow_read_udp_stream() +{ + 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( + buffer, + follow_record->data->len, + follow_record->is_server, + global_pos, + &server_packet_count, + &client_packet_count); + g_free(buffer); + if(frs_return == FRS_PRINT_ERROR) + return frs_return; + } + } + + return FRS_OK; +} + + +FollowStreamDialog::~FollowStreamDialog() +{ + delete ui; +} + +/* + * Editor modelines + * + * Local Variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * ex: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/ui/qt/follow_stream_dialog.h b/ui/qt/follow_stream_dialog.h new file mode 100644 index 0000000000..c977f0cfa6 --- /dev/null +++ b/ui/qt/follow_stream_dialog.h @@ -0,0 +1,161 @@ +/* follow_stream_dialog.h + * + * $Id$ + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * 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_STREAM_DIALOG_H +#define FOLLOW_STREAM_DIALOG_H + +#include <QDialog> +#include <QMessageBox> + +#include <unistd.h> + +#include "config.h" + +#include "qt_ui_utils.h" + +#include <epan/follow.h> +#include <epan/dissectors/packet-ipv6.h> +#include <epan/dissectors/packet-tcp.h> +#include <epan/prefs.h> +#include <epan/addr_resolv.h> +#include <epan/charsets.h> +#include <epan/epan_dissect.h> +#include <epan/filesystem.h> +#include <epan/ipproto.h> +#include <epan/charsets.h> +#include <epan/plugins.h> +#include <epan/tap.h> + +#include "../file.h" +#include "ui/alert_box.h" +#include "ui/simple_dialog.h" +#include "ui/utf8_entities.h" +#include "wsutil/tempfile.h" +#include <wsutil/file_util.h> +#include "ws_symbol_export.h" + + +#include "globals.h" +#include "file.h" + +#include "version_info.h" + + +#include <QtGui> +#include "../follow.h" + +WS_DLL_PUBLIC FILE *data_out_file; + +typedef struct { + follow_type_t follow_type; + show_stream_t show_stream; + show_type_t show_type; + char *data_out_filename; + gboolean is_ipv6; + char *filter_out_filter; + GList *payload; + guint bytes_written[2]; /* Index with FROM_CLIENT or FROM_SERVER for readability. */ + guint client_port; + address client_ip; +} follow_info_t; + +namespace Ui { +class FollowStreamDialog; +} + +class FollowStreamDialog : public QDialog +{ + Q_OBJECT + +public: + explicit FollowStreamDialog(QWidget *parent = 0); + ~FollowStreamDialog(); + + bool Follow(QString previous_filter_, follow_type_t type); + + frs_return_t + follow_show(char *buffer, size_t nchars, gboolean is_from_server, + guint32 *global_pos, guint32 *server_packet_count, + guint32 *client_packet_count); + + frs_return_t + follow_read_stream(); + + frs_return_t + follow_read_tcp_stream(); + + frs_return_t + follow_read_udp_stream(); + + frs_return_t + follow_read_ssl_stream(); + + void + follow_stream(); + + void add_text(char *buffer, size_t nchars, gboolean is_from_server); + +private slots: + void on_cbCharset_currentIndexChanged(int index); + void on_cbDirections_currentIndexChanged(int index); + void HelpButton(); + void FilterOut(); + void FindText(); + void SaveAs(); + void Print(); +// void on_bNext_clicked(); +// void on_bPrevious_clicked(); + +signals: + void updateFilter(QString &filter, bool force); + +private: + Ui::FollowStreamDialog *ui; + + QPushButton *bFilterOut; + QPushButton *bFind; + QPushButton *bPrint; + QPushButton *bSave; + + follow_info_t *follow_info; + + bool save_as; + QFile file; +}; + + + +#endif // FOLLOW_STREAM_DIALOG_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/qt/follow_stream_dialog.ui b/ui/qt/follow_stream_dialog.ui new file mode 100644 index 0000000000..bf9b1205ed --- /dev/null +++ b/ui/qt/follow_stream_dialog.ui @@ -0,0 +1,125 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>FollowStreamDialog</class> + <widget class="QDialog" name="FollowStreamDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>667</width> + <height>426</height> + </rect> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="windowTitle"> + <string>Dialog</string> + </property> + <widget class="QGroupBox" name="groupBox"> + <property name="geometry"> + <rect> + <x>10</x> + <y>0</y> + <width>651</width> + <height>381</height> + </rect> + </property> + <property name="title"> + <string>Stream contents</string> + </property> + <widget class="QComboBox" name="cbDirections"> + <property name="geometry"> + <rect> + <x>11</x> + <y>352</y> + <width>461</width> + <height>27</height> + </rect> + </property> + </widget> + <widget class="QTextEdit" name="teStreamContent"> + <property name="geometry"> + <rect> + <x>10</x> + <y>20</y> + <width>631</width> + <height>321</height> + </rect> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + <widget class="QWidget" name="layoutWidget"> + <property name="geometry"> + <rect> + <x>480</x> + <y>350</y> + <width>158</width> + <height>29</height> + </rect> + </property> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Charset</string> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="cbCharset"> + <property name="currentIndex"> + <number>4</number> + </property> + <item> + <property name="text"> + <string>ASCII</string> + </property> + </item> + <item> + <property name="text"> + <string>EBCDIC</string> + </property> + </item> + <item> + <property name="text"> + <string>C Arrays</string> + </property> + </item> + <item> + <property name="text"> + <string>Hex dump</string> + </property> + </item> + <item> + <property name="text"> + <string>Raw</string> + </property> + </item> + </widget> + </item> + </layout> + </widget> + </widget> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="geometry"> + <rect> + <x>20</x> + <y>390</y> + <width>631</width> + <height>27</height> + </rect> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Help</set> + </property> + </widget> + </widget> + <resources/> + <connections/> +</ui> diff --git a/ui/qt/main_window.cpp b/ui/qt/main_window.cpp index 8e5e6502fc..94a4b41d2b 100644 --- a/ui/qt/main_window.cpp +++ b/ui/qt/main_window.cpp @@ -111,6 +111,7 @@ MainWindow::MainWindow(QWidget *parent) : updateRecentFiles(); connect(&summary_dialog_, SIGNAL(captureCommentChanged()), this, SLOT(updateForUnsavedChanges())); + connect(&follow_stream_dialog_, SIGNAL(updateFilter(QString&, bool)), this, SLOT(filterPackets(QString&, bool))); const DisplayFilterEdit *df_edit = dynamic_cast<DisplayFilterEdit *>(df_combo_box_->lineEdit()); connect(df_edit, SIGNAL(pushFilterSyntaxStatus(QString&)), main_ui_->statusBar, SLOT(pushFilterStatus(QString&))); @@ -263,6 +264,8 @@ MainWindow::MainWindow(QWidget *parent) : this, SLOT(setMenusForSelectedPacket())); connect(packet_list_, SIGNAL(packetDissectionChanged()), this, SLOT(redissectPackets())); + connect(packet_list_, SIGNAL(setMenusFollowStream()), + this, SLOT(setMenusForFollowStream())); connect(proto_tree_, SIGNAL(protoItemSelected(QString&)), main_ui_->statusBar, SLOT(pushFieldStatus(QString&))); @@ -289,6 +292,11 @@ MainWindow::~MainWindow() delete main_ui_; } +QString MainWindow::getFilter() +{ + return df_combo_box_->itemText(df_combo_box_->count()); +} + void MainWindow::setPipeInputHandler(gint source, gpointer user_data, int *child_process, pipe_input_cb_t input_cb) { pipe_source_ = source; @@ -1272,6 +1280,34 @@ void MainWindow::setTitlebarForCaptureInProgress() // Menu state +void MainWindow::setMenusForFollowStream() +{ + if (!cap_file_) + return; + + if (!cap_file_->edt) + return; + + main_ui_->actionAnalyzeFollowTCPStream->setEnabled(false); + main_ui_->actionAnalyzeFollowUDPStream->setEnabled(false); + main_ui_->actionAnalyzeFollowSSLStream->setEnabled(false); + + if (cap_file_->edt->pi.ipproto == IP_PROTO_TCP) + { + main_ui_->actionAnalyzeFollowTCPStream->setEnabled(true); + } + + if (cap_file_->edt->pi.ipproto == IP_PROTO_UDP) + { + main_ui_->actionAnalyzeFollowUDPStream->setEnabled(true); + } + + if ( epan_dissect_packet_contains_field(cap_file_->edt, "ssl") ) + { + main_ui_->actionAnalyzeFollowSSLStream->setEnabled(true); + } +} + /* Enable or disable menu items based on whether you have a capture file you've finished reading and, if you have one, whether it's been saved and whether it could be saved except by copying the raw packet data. */ diff --git a/ui/qt/main_window.h b/ui/qt/main_window.h index bba37468ff..d73173247a 100644 --- a/ui/qt/main_window.h +++ b/ui/qt/main_window.h @@ -57,6 +57,7 @@ #include "file_set_dialog.h" #include "capture_file_dialog.h" #include "summary_dialog.h" +#include "follow_stream_dialog.h" class QAction; @@ -73,6 +74,8 @@ public: ~MainWindow(); void setPipeInputHandler(gint source, gpointer user_data, int *child_process, pipe_input_cb_t input_cb); + QString getFilter(); + protected: bool eventFilter(QObject *obj, QEvent *event); void keyPressEvent(QKeyEvent *event); @@ -108,6 +111,7 @@ private: SummaryDialog summary_dialog_; ByteViewTab *byte_view_tab_; QWidget *empty_pane_; + FollowStreamDialog follow_stream_dialog bool capture_stopping_; bool capture_filter_valid_; @@ -188,6 +192,7 @@ private slots: void updateRecentFiles(); void recentActionTriggered(); + void setMenusForFollowStream(); void setMenusForSelectedPacket(); void setMenusForSelectedTreeRow(field_info *fi = NULL); void interfaceSelectionChanged(); @@ -269,6 +274,9 @@ private slots: void on_actionAnalyzePAFOrSelected_triggered(); void on_actionAnalyzePAFAndNotSelected_triggered(); void on_actionAnalyzePAFOrNotSelected_triggered(); + void on_actionAnalyzeFollowTCPStream_triggered(); + void on_actionAnalyzeFollowUDPStream_triggered(); + void on_actionAnalyzeFollowSSLStream_triggered(); void on_actionHelpContents_triggered(); void on_actionHelpMPWireshark_triggered(); diff --git a/ui/qt/main_window.ui b/ui/qt/main_window.ui index 3998565593..f55d193fad 100644 --- a/ui/qt/main_window.ui +++ b/ui/qt/main_window.ui @@ -219,6 +219,7 @@ <addaction name="actionCaptureRestart"/> <addaction name="actionCaptureCaptureFilters"/> <addaction name="actionCaptureRefreshInterfaces"/> + <addaction name="separator"/> </widget> <widget class="QMenu" name="menuHelp"> <property name="title"> @@ -307,6 +308,10 @@ <addaction name="actionApplyAsColumn"/> <addaction name="menuApplyAsFilter"/> <addaction name="menuPrepareAFilter"/> + <addaction name="separator"/> + <addaction name="actionAnalyzeFollowTCPStream"/> + <addaction name="actionAnalyzeFollowUDPStream"/> + <addaction name="actionAnalyzeFollowSSLStream"/> </widget> <widget class="QMenu" name="menuStatistics"> <property name="enabled"> @@ -1283,6 +1288,30 @@ <string>TCP window scaling</string> </property> </action> + <action name="actionAnalyzeFollowTCPStream"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Follow TCP Stream</string> + </property> + </action> + <action name="actionAnalyzeFollowUDPStream"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Follow UDP Stream</string> + </property> + </action> + <action name="actionAnalyzeFollowSSLStream"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Follow SSL Stream</string> + </property> + </action> </widget> <layoutdefault spacing="6" margin="11"/> <customwidgets> diff --git a/ui/qt/main_window_slots.cpp b/ui/qt/main_window_slots.cpp index 9cef7b6442..b80e515477 100644 --- a/ui/qt/main_window_slots.cpp +++ b/ui/qt/main_window_slots.cpp @@ -199,8 +199,13 @@ void MainWindow::filterPackets(QString& new_filter, bool force) if (cf_status == CF_OK) { emit displayFilterSuccess(true); if (new_filter.length() > 0) { - if (df_combo_box_->findText(new_filter) < 0) { + int index = df_combo_box_->findText(new_filter); + if (index == -1) { df_combo_box_->insertItem(0, new_filter); + df_combo_box_->setCurrentIndex(0); + } + else { + df_combo_box_->setCurrentIndex(index); } } } else { @@ -463,6 +468,12 @@ void MainWindow::captureFileClosed(const capture_file *cf) { summary_dialog_.close(); + if (df_combo_box_) + { + df_combo_box_->lineEdit()->setText(""); + df_combo_box_->applyDisplayFilter(); + } + setTitlebarForSelectedTreeRow(); setMenusForSelectedTreeRow(); } @@ -1690,6 +1701,57 @@ void MainWindow::on_actionAnalyzePAFOrNotSelected_triggered() matchSelectedFilter(MatchSelectedOrNot, false, false); } +void MainWindow::on_actionAnalyzeFollowTCPStream_triggered() +{ + follow_stream_dialog_.Follow(getFilter(), FOLLOW_TCP); + + if (follow_stream_dialog_.isMinimized() == true) + { + follow_stream_dialog_.showNormal(); + } + else + { + follow_stream_dialog_.show(); + } + + follow_stream_dialog_.raise(); + follow_stream_dialog_.activateWindow(); +} + +void MainWindow::on_actionAnalyzeFollowUDPStream_triggered() +{ + follow_stream_dialog_.Follow(getFilter(), FOLLOW_UDP); + + if (follow_stream_dialog_.isMinimized() == true) + { + follow_stream_dialog_.showNormal(); + } + else + { + follow_stream_dialog_.show(); + } + + follow_stream_dialog_.raise(); + follow_stream_dialog_.activateWindow(); +} + +void MainWindow::on_actionAnalyzeFollowSSLStream_triggered() +{ + follow_stream_dialog_.Follow(getFilter(), FOLLOW_SSL); + + if (follow_stream_dialog_.isMinimized() == true) + { + follow_stream_dialog_.showNormal(); + } + else + { + follow_stream_dialog_.show(); + } + + follow_stream_dialog_.raise(); + follow_stream_dialog_.activateWindow(); +} + // Next / previous / first / last slots in packet_list // Statistics Menu diff --git a/ui/qt/packet_list.cpp b/ui/qt/packet_list.cpp index ca7f0ea33f..64fe9a940f 100644 --- a/ui/qt/packet_list.cpp +++ b/ui/qt/packet_list.cpp @@ -35,6 +35,7 @@ #include "packet_list.h" #include "proto_tree.h" #include "wireshark_application.h" +#include <epan/ipproto.h> #include "qt_ui_utils.h" @@ -247,6 +248,17 @@ PacketList::PacketList(QWidget *parent) : ctx_menu_.addAction(window()->findChild<QAction *>("actionEditPacketComment")); ctx_menu_.addSeparator(); + submenu = new QMenu(tr("Follow...")); + ctx_menu_.addMenu(submenu); + submenu->addAction(window()->findChild<QAction *>("actionAnalyzeFollowTCPStream")); + submenu->addAction(window()->findChild<QAction *>("actionAnalyzeFollowUDPStream")); + submenu->addAction(window()->findChild<QAction *>("actionAnalyzeFollowSSLStream")); + filter_actions_ << submenu->actions(); + // " <menuitem name='FollowTCPStream' action='/Follow TCP Stream'/>\n" + // " <menuitem name='FollowUDPStream' action='/Follow UDP Stream'/>\n" + // " <menuitem name='FollowSSLStream' action='/Follow SSL Stream'/>\n" + + ctx_menu_.addSeparator(); // " <menuitem name='ManuallyResolveAddress' action='/ManuallyResolveAddress'/>\n" ctx_menu_.addSeparator(); submenu = new QMenu(tr("Apply as Filter")); @@ -414,6 +426,7 @@ void PacketList::selectionChanged (const QItemSelection & selected, const QItemS int row = selected.first().top(); cf_select_packet(cap_file_, row); related_packet_delegate_.clear(); + emit setMenusFollowStream(); if (!cap_file_->edt) return; @@ -447,8 +460,50 @@ void PacketList::contextMenuEvent(QContextMenuEvent *event) bool fa_enabled = filter_actions_[0]->isEnabled(); QAction *act; - foreach (act, filter_actions_) { + + foreach (act, filter_actions_) + { act->setEnabled(true); + + + // check follow stream + if (act->text().contains("TCP")) + { + if (cap_file_->edt->pi.ipproto == IP_PROTO_TCP) + { + act->setEnabled(true); + } + else + { + act->setEnabled(false); + } + } + + + if (act->text().contains("UDP")) + { + if (cap_file_->edt->pi.ipproto == IP_PROTO_UDP) + { + act->setEnabled(true); + } + else + { + act->setEnabled(false); + } + } + + + if (act->text().contains("SSL")) + { + if (epan_dissect_packet_contains_field(cap_file_->edt, "ssl")) + { + act->setEnabled(true); + } + else + { + act->setEnabled(false); + } + } } ctx_column_ = columnAt(event->x()); ctx_menu_.exec(event->globalPos()); diff --git a/ui/qt/packet_list.h b/ui/qt/packet_list.h index d7c3978901..aef9508e4a 100644 --- a/ui/qt/packet_list.h +++ b/ui/qt/packet_list.h @@ -56,6 +56,7 @@ protected: void selectionChanged (const QItemSelection & selected, const QItemSelection & deselected); void contextMenuEvent(QContextMenuEvent *event); + private: PacketListModel *packet_list_model_; ProtoTree *proto_tree_; @@ -74,6 +75,7 @@ private: signals: void packetDissectionChanged(); + void setMenusFollowStream(); public slots: void setCaptureFile(capture_file *cf); |