wireshark/ui/qt/packet_dialog.cpp

271 lines
8.6 KiB
C++
Raw Normal View History

/* packet_dialog.cpp
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "packet_dialog.h"
#include <ui_packet_dialog.h>
#include "file.h"
#include "epan/column.h"
#include "epan/ftypes/ftypes.h"
#include "epan/prefs.h"
#include "epan/prefs-int.h"
#include "ui/preference_utils.h"
#include "frame_tvbuff.h"
#include <wsutil/utf8_entities.h>
#include "byte_view_tab.h"
#include "proto_tree.h"
#include "main_application.h"
#include <ui/qt/utils/field_information.h>
#include <QTreeWidgetItemIterator>
Q_DECLARE_METATYPE(splitter_layout_e)
// To do:
// - Copy over experimental packet editing code.
// - Fix ElidedText width.
PacketDialog::PacketDialog(QWidget &parent, CaptureFile &cf, frame_data *fdata) :
WiresharkDialog(parent, cf),
ui(new Ui::PacketDialog),
proto_tree_(NULL),
byte_view_tab_(NULL)
{
ui->setupUi(this);
loadGeometry(parent.width() * 4 / 5, parent.height() * 4 / 5);
ui->hintLabel->setSmallText();
ui->prefsLayout->insertSpacing(1, 20);
ui->prefsLayout->addStretch();
wtap_rec_init(&rec_);
ws_buffer_init(&buf_, 1514);
edt_.session = NULL;
edt_.tvb = NULL;
edt_.tree = NULL;
memset(&edt_.pi, 0x0, sizeof(edt_.pi));
setWindowSubtitle(tr("Packet %1").arg(fdata->num));
if (!cf_read_record(cap_file_.capFile(), fdata, &rec_, &buf_)) {
reject();
return;
}
/* proto tree, visible. We need a proto tree if there are custom columns */
epan_dissect_init(&edt_, cap_file_.capFile()->epan, TRUE, TRUE);
col_custom_prime_edt(&edt_, &(cap_file_.capFile()->cinfo));
epan_dissect_run(&edt_, cap_file_.capFile()->cd_t, &rec_,
frame_tvbuff_new_buffer(&cap_file_.capFile()->provider, fdata, &buf_),
fdata, &(cap_file_.capFile()->cinfo));
epan_dissect_fill_in_columns(&edt_, TRUE, TRUE);
proto_tree_ = new ProtoTree(ui->packetSplitter, &edt_);
// Do not call proto_tree_->setCaptureFile, ProtoTree only needs the
// dissection context.
proto_tree_->setRootNode(edt_.tree);
byte_view_tab_ = new ByteViewTab(ui->packetSplitter, &edt_);
byte_view_tab_->setCaptureFile(cap_file_.capFile());
byte_view_tab_->selectedFrameChanged(QList<int>() << 0);
// We have to load the splitter state after adding the proto tree
// and byte view.
loadSplitterState(ui->packetSplitter);
module_t *gui_module = prefs_find_module("gui");
if (gui_module != nullptr) {
pref_packet_dialog_layout_ = prefs_find_preference(gui_module, "packet_dialog_layout");
if (pref_packet_dialog_layout_ != nullptr) {
for (const enum_val_t *ev = prefs_get_enumvals(pref_packet_dialog_layout_); ev && ev->description; ev++) {
ui->layoutComboBox->addItem(ev->description, QVariant(ev->value));
}
}
}
ui->layoutComboBox->setCurrentIndex(ui->layoutComboBox->findData(QVariant(prefs.gui_packet_dialog_layout)));
Qt::Orientation pref_orientation = Qt::Vertical;
switch(prefs.gui_packet_dialog_layout) {
case(layout_vertical):
pref_orientation = Qt::Vertical;
break;
case(layout_horizontal):
pref_orientation = Qt::Horizontal;
break;
}
if (ui->packetSplitter->orientation() != pref_orientation) {
ui->packetSplitter->setOrientation(pref_orientation);
// If the orientation is different than the restore one,
// reset the sizes to 50-50.
QList<int> sizes = ui->packetSplitter->sizes();
int totalsize = sizes.at(0) + sizes.at(1);
sizes[0] = totalsize / 2;
sizes[1] = totalsize / 2;
ui->packetSplitter->setSizes(sizes);
}
QStringList col_parts;
for (int i = 0; i < cap_file_.capFile()->cinfo.num_cols; ++i) {
// ElidedLabel doesn't support rich text / HTML
col_parts << QString("%1: %2")
.arg(get_column_title(i))
.arg(get_column_text(&cap_file_.capFile()->cinfo, i));
}
col_info_ = col_parts.join(" " UTF8_MIDDLE_DOT " ");
ui->hintLabel->setText(col_info_);
/* Handle preference value correctly */
Qt::CheckState state = Qt::Checked;
if (!prefs.gui_packet_details_show_byteview) {
state = Qt::Unchecked;
byte_view_tab_->setVisible(false);
}
ui->chkShowByteView->setCheckState(state);
ui->layoutComboBox->setEnabled(state);
connect(mainApp, SIGNAL(zoomMonospaceFont(QFont)),
proto_tree_, SLOT(setMonospaceFont(QFont)));
connect(byte_view_tab_, SIGNAL(fieldSelected(FieldInformation *)),
proto_tree_, SLOT(selectedFieldChanged(FieldInformation *)));
connect(proto_tree_, SIGNAL(fieldSelected(FieldInformation *)),
byte_view_tab_, SLOT(selectedFieldChanged(FieldInformation *)));
connect(byte_view_tab_, SIGNAL(fieldHighlight(FieldInformation *)),
this, SLOT(setHintText(FieldInformation *)));
connect(byte_view_tab_, &ByteViewTab::fieldSelected,
this, &PacketDialog::setHintTextSelected);
connect(proto_tree_, &ProtoTree::fieldSelected,
this, &PacketDialog::setHintTextSelected);
connect(proto_tree_, SIGNAL(showProtocolPreferences(QString)),
this, SIGNAL(showProtocolPreferences(QString)));
connect(proto_tree_, SIGNAL(editProtocolPreference(preference*,pref_module*)),
this, SIGNAL(editProtocolPreference(preference*,pref_module*)));
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
connect(ui->layoutComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &PacketDialog::layoutChanged);
#else
connect(ui->layoutComboBox, &QComboBox::currentIndexChanged, this, &PacketDialog::layoutChanged, Qt::AutoConnection);
#endif
connect(ui->chkShowByteView, &QCheckBox::stateChanged, this, &PacketDialog::viewVisibilityStateChanged);
}
PacketDialog::~PacketDialog()
{
delete ui;
epan_dissect_cleanup(&edt_);
wtap_rec_cleanup(&rec_);
ws_buffer_free(&buf_);
}
void PacketDialog::captureFileClosing()
{
QString closed_title = tr("[%1 closed] " UTF8_MIDDLE_DOT " %2")
.arg(cap_file_.fileName())
.arg(col_info_);
ui->hintLabel->setText(closed_title);
Qt: Fix PacketDialog secondary data sources crash after closing file The first data source tvb associated with a packet is always freed, along with its data, at the same time as the associated pinfo->pool scoped data and the tree, if any, in epan_dissect_cleanup() or epan_dissect_reset(). Not so for secondary data sources; while the vast majority of data source tvbs are chained to the first data source and so also freed at the same time, and almost all of the others are never freed and just leak (sometimes because of an exception before being set as a child), it's not uncommon to have a tvb whose real data is at file scope, with the assumption that this will outlive any packet scope. (Note that the real data is not copied, for speed and memory usage.) This is, for example, how epan/reassemble.c works, with the data freed when a file is closed (although it is not managed by wmem.) When a PacketDialog persists after the capture file closes, this assumption is falsified. As we do not have a perfect way to detect the scope of the real data (we could introduce a function to check if the free_cb is NULL, which would be suggestive but not absolute), deep copy the data for secondary data sources when the parent PacketDialog indicates that the capture file is about to close. Also, avoid calling API functions thacker examine the real data. Looking at the offsets is OK, as proto_find_field_from_offset() does. While it could be possible to clone the data source tvbs located in edt->pi.data_src, the pointers would need to be updated in each of the field_infos in the edt's tree as well. Currently no dissectors (or other code) attach data source tvbs where the tvb itself, not just the real data, is freed with a file is closed. If they did, that could still cause a crash. Fix #14363
2023-06-10 01:05:35 +00:00
byte_view_tab_->captureFileClosing();
WiresharkDialog::captureFileClosing();
}
void PacketDialog::on_buttonBox_helpRequested()
{
mainApp->helpTopicAction(HELP_NEW_PACKET_DIALOG);
}
void PacketDialog::setHintText(FieldInformation * finfo)
{
QString hint;
if (finfo)
{
FieldInformation::Position pos = finfo->position();
QString field_str;
if (pos.length < 2) {
hint = QString(tr("Byte %1")).arg(pos.start);
} else {
hint = QString(tr("Bytes %1-%2")).arg(pos.start).arg(pos.start + pos.length - 1);
}
hint += QString(": %1 (%2)")
.arg(finfo->headerInfo().name)
.arg(finfo->headerInfo().abbreviation);
}
else {
hint = col_info_;
}
ui->hintLabel->setText(hint);
}
void PacketDialog::setHintTextSelected(FieldInformation* finfo)
{
QString hint;
if (finfo)
{
FieldInformation::HeaderInfo hInfo = finfo->headerInfo();
if (hInfo.isValid)
{
if (hInfo.description.length() > 0) {
hint.append(hInfo.description);
}
else {
hint.append(hInfo.name);
}
}
if (!hint.isEmpty()) {
int finfo_length;
if (hInfo.isValid)
hint.append(" (" + hInfo.abbreviation + ")");
finfo_length = finfo->position().length + finfo->appendix().length;
if (finfo_length > 0) {
hint.append(", " + tr("%Ln byte(s)", "", finfo_length));
}
}
}
else {
hint = col_info_;
}
ui->hintLabel->setText(hint);
}
void PacketDialog::viewVisibilityStateChanged(int state)
{
byte_view_tab_->setVisible(state == Qt::Checked);
ui->layoutComboBox->setEnabled(state == Qt::Checked);
prefs.gui_packet_details_show_byteview = (state == Qt::Checked ? TRUE : FALSE);
prefs_main_write();
}
void PacketDialog::layoutChanged(int index _U_)
{
splitter_layout_e layout = ui->layoutComboBox->currentData().value<splitter_layout_e>();
switch(layout) {
case(layout_vertical):
ui->packetSplitter->setOrientation(Qt::Vertical);
break;
case(layout_horizontal):
ui->packetSplitter->setOrientation(Qt::Horizontal);
break;
}
prefs_set_enum_value(pref_packet_dialog_layout_, layout, pref_current);
}