1 /* address_editor_frame.cpp
3 * Wireshark - Network traffic analyzer
4 * By Gerald Combs <gerald@wireshark.org>
5 * Copyright 1998 Gerald Combs
7 * SPDX-License-Identifier: GPL-2.0-or-later
13 #include "frame_tvbuff.h"
15 #include "epan/addr_resolv.h"
16 #include "epan/epan_dissect.h"
17 #include "epan/frame_data.h"
19 #include "main_application.h"
21 #include "address_editor_frame.h"
22 #include <ui_address_editor_frame.h>
24 #include <QPushButton>
27 #include <ui/qt/utils/qt_ui_utils.h>
30 // - Fill in currently resolved address.
31 // - Allow editing other kinds of addresses.
33 AddressEditorFrame::AddressEditorFrame(QWidget
*parent
) :
34 AccordionFrame(parent
),
35 ui(new Ui::AddressEditorFrame
),
39 ui
->addressComboBox
->setSizeAdjustPolicy(QComboBox::AdjustToContents
);
42 foreach (QWidget
*w
, findChildren
<QWidget
*>()) {
43 w
->setAttribute(Qt::WA_MacSmallSize
, true);
48 AddressEditorFrame::~AddressEditorFrame()
53 QString
AddressEditorFrame::addressToString(const FieldInformation
& finfo
)
57 const ipv4_addr_and_mask
*ipv4
;
58 const ipv6_addr_and_prefix
*ipv6
;
60 if (!finfo
.isValid()) {
64 switch (finfo
.headerInfo().type
) {
67 // FieldInformation.toString gives us the result of
68 // proto_item_fill_display_label, but that gives us
69 // the currently resolved version, if resolution is
70 // available and enabled. We want the unresolved string.
71 ipv4
= fvalue_get_ipv4(finfo
.fieldInfo()->value
);
72 set_address_ipv4(&addr
, ipv4
);
73 addr_str
= gchar_free_to_qstring(address_to_str(NULL
, &addr
));
77 ipv6
= fvalue_get_ipv6(finfo
.fieldInfo()->value
);
78 set_address_ipv6(&addr
, ipv6
);
79 addr_str
= gchar_free_to_qstring(address_to_str(NULL
, &addr
));
88 // NOLINTNEXTLINE(misc-no-recursion)
89 void AddressEditorFrame::addAddresses(const ProtoNode
& node
, QStringList
& addresses
)
91 QString addrString
= addressToString(FieldInformation(&node
));
92 if (!addrString
.isEmpty()) {
93 addresses
<< addrString
;
95 ProtoNode::ChildIterator kids
= node
.children();
96 while (kids
.element().isValid()) {
97 // We recurse here, but we're limited by tree depth checks in epan
98 addAddresses(kids
.element(), addresses
);
103 void AddressEditorFrame::editAddresses(CaptureFile
&cf
, int column
)
105 cap_file_
= cf
.capFile();
107 if (!cap_file_
->current_frame
) {
108 on_buttonBox_rejected();
112 if (!cf_read_current_record(cap_file_
)) {
113 on_buttonBox_rejected();
114 return; // error reading the frame
118 QStringList addresses
;
119 QString selectedString
;
121 ui
->addressComboBox
->clear();
123 // Dissect the record with a visible tree and fill in the custom
124 // columns. We don't really need to have a visible tree (we should
125 // have one in cap_file_->edt->tree as we have a current frame), but
126 // this is only a single frame that's previously been dissected so
127 // the performance hit is slight anyway.
128 epan_dissect_init(&edt
, cap_file_
->epan
, true, true);
129 col_custom_prime_edt(&edt
, &cap_file_
->cinfo
);
131 epan_dissect_run(&edt
, cap_file_
->cd_t
, &cap_file_
->rec
,
132 frame_tvbuff_new_buffer(&cap_file_
->provider
, cap_file_
->current_frame
, &cap_file_
->buf
),
133 cap_file_
->current_frame
, &cap_file_
->cinfo
);
134 epan_dissect_fill_in_columns(&edt
, true, true);
136 addAddresses(ProtoNode(edt
.tree
), addresses
);
139 // Check selected column
140 if (isAddressColumn(&cap_file_
->cinfo
, column
)) {
141 // This always gets the unresolved value.
142 // XXX: For multifield custom columns, we don't have a good
143 // function to return each string separately before joining
144 // them. Since we know that IP addresses don't include commas,
145 // we could split on commas here, and check each field value
146 // to find the first one that is an IP address in our list.
147 selectedString
= cap_file_
->cinfo
.col_expr
.col_expr_val
[column
];
149 } else if (cap_file_
->finfo_selected
) {
150 selectedString
= addressToString(FieldInformation(cap_file_
->finfo_selected
));
153 epan_dissect_cleanup(&edt
);
155 displayPreviousUserDefinedHostname();
157 addresses
.removeDuplicates();
158 ui
->addressComboBox
->addItems(addresses
);
159 int index
= ui
->addressComboBox
->findText(selectedString
);
161 ui
->addressComboBox
->setCurrentIndex(index
);
163 ui
->nameLineEdit
->setFocus();
167 void AddressEditorFrame::showEvent(QShowEvent
*event
)
169 ui
->nameLineEdit
->setFocus();
170 ui
->nameLineEdit
->selectAll();
172 AccordionFrame::showEvent(event
);
175 void AddressEditorFrame::keyPressEvent(QKeyEvent
*event
)
177 if (event
->modifiers() == Qt::NoModifier
) {
178 if (event
->key() == Qt::Key_Escape
) {
179 on_buttonBox_rejected();
180 } else if (event
->key() == Qt::Key_Enter
|| event
->key() == Qt::Key_Return
) {
181 if (ui
->buttonBox
->button(QDialogButtonBox::Ok
)->isEnabled()) {
182 on_buttonBox_accepted();
187 AccordionFrame::keyPressEvent(event
);
190 void AddressEditorFrame::displayPreviousUserDefinedHostname()
192 QString addr
= ui
->addressComboBox
->currentText();
193 // XXX: If there's a resolved name that wasn't manually entered,
194 // we should probably display that too. Possibly even if network
195 // name resolution is off globally, as get_edited_resolved_name() does.
196 // It's possible to have such names from DNS lookups if the global is
197 // turned on then turned back off, from NRBs, or from DNS packets.
198 // There's no clean API call to always get the resolved name, but
199 // we could access the hash tables directly the way that
200 // models/resolved_addresses_models.cpp does.
201 resolved_name_t
* previous_entry
= get_edited_resolved_name(addr
.toUtf8().constData());
204 ui
->nameLineEdit
->setText(previous_entry
->name
);
208 ui
->nameLineEdit
->setText("");
212 void AddressEditorFrame::updateWidgets()
214 bool ok_enable
= false;
215 if (ui
->addressComboBox
->count() > 0) {
219 ui
->buttonBox
->button(QDialogButtonBox::Ok
)->setEnabled(ok_enable
);
222 void AddressEditorFrame::on_nameResolutionPreferencesToolButton_clicked()
224 on_buttonBox_rejected();
225 emit
showNameResolutionPreferences("nameres");
228 void AddressEditorFrame::on_addressComboBox_currentIndexChanged(int)
230 displayPreviousUserDefinedHostname();
234 void AddressEditorFrame::on_nameLineEdit_textEdited(const QString
&)
239 void AddressEditorFrame::on_buttonBox_accepted()
241 if (ui
->addressComboBox
->count() < 1) {
244 QString addr
= ui
->addressComboBox
->currentText();
245 QString name
= ui
->nameLineEdit
->text();
246 if (!cf_add_ip_name_from_string(cap_file_
, addr
.toUtf8().constData(), name
.toUtf8().constData())) {
247 QString error_msg
= tr("Can't assign %1 to %2.").arg(name
).arg(addr
);
248 mainApp
->pushStatus(MainApplication::TemporaryStatus
, error_msg
);
249 ui
->buttonBox
->button(QDialogButtonBox::Ok
)->setEnabled(false);
252 on_buttonBox_rejected();
253 // There's no point in redissecting packets if the network resolution
254 // global is off. There is a use case for editing several names before
255 // turning on the preference to avoid a lot of expensive redissects.
256 // (Statistics->Resolved Addresses still displays them even when
257 // resolution is disabled, so the user can check what has been input.)
259 // XXX: Can entering a new name but having nothing happen because
260 // network name resolution is off be confusing to the user? The GTK
261 // dialog had a simple checkbox, the "Name Resolution Preferences..."
262 // is a little more complicated but hopefully obvious enough.
263 if (gbl_resolv_flags
.network_name
) {
264 emit
redissectPackets();
268 void AddressEditorFrame::on_buttonBox_rejected()
270 ui
->addressComboBox
->clear();
271 ui
->nameLineEdit
->clear();
275 bool AddressEditorFrame::isAddressColumn(epan_column_info
*cinfo
, int column
)
277 if (!cinfo
|| column
< 0 || column
>= cinfo
->num_cols
) return false;
279 if (((cinfo
->columns
[column
].col_fmt
== COL_DEF_SRC
) ||
280 (cinfo
->columns
[column
].col_fmt
== COL_RES_SRC
) ||
281 (cinfo
->columns
[column
].col_fmt
== COL_UNRES_SRC
) ||
282 (cinfo
->columns
[column
].col_fmt
== COL_DEF_DST
) ||
283 (cinfo
->columns
[column
].col_fmt
== COL_RES_DST
) ||
284 (cinfo
->columns
[column
].col_fmt
== COL_UNRES_DST
) ||
285 (cinfo
->columns
[column
].col_fmt
== COL_DEF_NET_SRC
) ||
286 (cinfo
->columns
[column
].col_fmt
== COL_RES_NET_SRC
) ||
287 (cinfo
->columns
[column
].col_fmt
== COL_UNRES_NET_SRC
) ||
288 (cinfo
->columns
[column
].col_fmt
== COL_DEF_NET_DST
) ||
289 (cinfo
->columns
[column
].col_fmt
== COL_RES_NET_DST
) ||
290 (cinfo
->columns
[column
].col_fmt
== COL_UNRES_NET_DST
)) &&
291 strlen(cinfo
->col_expr
.col_expr_val
[column
]))
296 if ((cinfo
->columns
[column
].col_fmt
== COL_CUSTOM
) &&
297 cinfo
->columns
[column
].col_custom_fields
) {
298 // We could cycle through all the col_custom_fields_ids and
299 // see if proto_registrar_get_ftype() says that any of them
300 // are FT_IPv4 or FT_IPv6, but let's just check the string
301 // against all the addresses we found from the tree.