--- /dev/null
+/* 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:
+ */