Qt: Better sorting for traffic dialogs
Allow the traffic columns to automatically sort on secondary columns if applicable. e.g. the address and port column for TCP and UDP, or the secondary address for conversations
This commit is contained in:
parent
b3a102eb46
commit
0cfe7a0d56
|
@ -33,6 +33,10 @@ wsbuglink:17779[]
|
|||
- Adding/Removing tabs will keep them in the same order all the time
|
||||
- If a filter is applied, two columns are shown in either dialog detailing the difference between
|
||||
unmatched and matched packets
|
||||
- Columns are now sorted via secondary properties if an identical entry is found.
|
||||
- Conversations will be sorted via second address and first port number
|
||||
- Endpoints will be sorted via port numbers
|
||||
- IPv6 addresses are sorted correctly after IPv4 addresses
|
||||
|
||||
* The PCRE2 library (https://www.pcre.org/) is now a required dependency to build Wireshark.
|
||||
|
||||
|
|
|
@ -107,7 +107,7 @@ bool ATapDataModel::enableTap()
|
|||
|
||||
/* The errorString is ignored. If this is not working, there is nothing really the user may do about
|
||||
* it, so the error is only interesting to the developer.*/
|
||||
GString * errorString = register_tap_listener(tap().toUtf8().constData(), hash(), _filter.toUtf8().constData(),
|
||||
GString * errorString = register_tap_listener(tap().toUtf8().constData(), hash(), _filter.toUtf8().constData(),
|
||||
TL_IGNORE_DISPLAY_FILTER, &ATapDataModel::tapReset, conversationPacketHandler(), &ATapDataModel::tapDraw, nullptr);
|
||||
if (errorString && errorString->len > 0) {
|
||||
_disableTap = true;
|
||||
|
@ -497,6 +497,21 @@ QVariant EndpointDataModel::data(const QModelIndex &idx, int role) const
|
|||
} else if (role == ATapDataModel::ROW_IS_FILTERED) {
|
||||
return (bool)item->filtered && showTotalColumn();
|
||||
}
|
||||
else if (column == EndpointDataModel::ENDP_COLUMN_ADDR) {
|
||||
if (role == ATapDataModel::DATA_ADDRESS_TYPE)
|
||||
return (int)item->myaddress.type;
|
||||
else if (role == ATapDataModel::DATA_IPV4_INTEGER && item->myaddress.type == AT_IPv4) {
|
||||
const ws_in4_addr * ip4 = (const ws_in4_addr *) item->myaddress.data;
|
||||
return (quint32) GUINT32_TO_BE(*ip4);
|
||||
}
|
||||
else if (role == ATapDataModel::DATA_IPV6_VECTOR && item->myaddress.type == AT_IPv6) {
|
||||
const ws_in6_addr * ip6 = (const ws_in6_addr *) item->myaddress.data;
|
||||
QVector<quint8> result;
|
||||
result.reserve(16);
|
||||
std::copy(ip6->bytes + 0, ip6->bytes + 16, std::back_inserter(result));
|
||||
return QVariant::fromValue(result);
|
||||
}
|
||||
}
|
||||
#ifdef HAVE_MAXMINDDB
|
||||
else if (role == ATapDataModel::GEODATA_AVAILABLE) {
|
||||
return (bool)(mmdb_lookup && maxmind_db_has_coords(mmdb_lookup));
|
||||
|
@ -718,7 +733,7 @@ QVariant ConversationDataModel::data(const QModelIndex &idx, int role) const
|
|||
QDateTime abs_dt = QDateTime::fromMSecsSinceEpoch(nstime_to_msec(abs_time));
|
||||
return role == Qt::DisplayRole ? abs_dt.toString("hh:mm:ss.zzzz") : (QVariant)abs_dt;
|
||||
} else {
|
||||
return role == Qt::DisplayRole ?
|
||||
return role == Qt::DisplayRole ?
|
||||
QString::number(nstime_to_sec(&conv_item->start_time), 'f', width) :
|
||||
(QVariant)((double) nstime_to_sec(&conv_item->start_time));
|
||||
}
|
||||
|
@ -737,7 +752,7 @@ QVariant ConversationDataModel::data(const QModelIndex &idx, int role) const
|
|||
qlonglong packets = 0;
|
||||
if (showTotalColumn())
|
||||
packets = conv_item->tx_frames_total + conv_item->rx_frames_total;
|
||||
|
||||
|
||||
return role == Qt::DisplayRole ? QString("%L1").arg(packets) : (QVariant)packets;
|
||||
}
|
||||
case 15:
|
||||
|
@ -777,6 +792,22 @@ QVariant ConversationDataModel::data(const QModelIndex &idx, int role) const
|
|||
} else if (role == ATapDataModel::ROW_IS_FILTERED) {
|
||||
return (bool)conv_item->filtered && showTotalColumn();
|
||||
}
|
||||
else if (column == ConversationDataModel::CONV_COLUMN_SRC_ADDR || column == ConversationDataModel::CONV_COLUMN_DST_ADDR) {
|
||||
address tst_address = column == ConversationDataModel::CONV_COLUMN_SRC_ADDR ? conv_item->src_address : conv_item->dst_address;
|
||||
if (role == ATapDataModel::DATA_ADDRESS_TYPE)
|
||||
return (int)tst_address.type;
|
||||
else if (role == ATapDataModel::DATA_IPV4_INTEGER && tst_address.type == AT_IPv4) {
|
||||
const ws_in4_addr * ip4 = (const ws_in4_addr *) tst_address.data;
|
||||
return (quint32) GUINT32_TO_BE(*ip4);
|
||||
}
|
||||
else if (role == ATapDataModel::DATA_IPV6_VECTOR && tst_address.type == AT_IPv6) {
|
||||
const ws_in6_addr * ip6 = (const ws_in6_addr *) tst_address.data;
|
||||
QVector<quint8> result;
|
||||
result.reserve(16);
|
||||
std::copy(ip6->bytes + 0, ip6->bytes + 16, std::back_inserter(result));
|
||||
return QVariant::fromValue(result);
|
||||
}
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
|
|
@ -47,7 +47,10 @@ public:
|
|||
TIMELINE_DATA,
|
||||
ENDPOINT_DATATYPE,
|
||||
CONVERSATION_ID,
|
||||
ROW_IS_FILTERED
|
||||
ROW_IS_FILTERED,
|
||||
DATA_ADDRESS_TYPE,
|
||||
DATA_IPV4_INTEGER,
|
||||
DATA_IPV6_VECTOR,
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
|
|
|
@ -45,7 +45,6 @@
|
|||
#include <QUrl>
|
||||
#include <QTemporaryFile>
|
||||
#include <QHBoxLayout>
|
||||
#include <QRegularExpressionMatch>
|
||||
|
||||
TabData::TabData() :
|
||||
_name(QString()),
|
||||
|
@ -91,41 +90,72 @@ bool TrafficDataFilterProxy::lessThan(const QModelIndex &source_left, const QMod
|
|||
if (! source_right.isValid() || ! qobject_cast<const ATapDataModel *>(source_right.model()))
|
||||
return false;
|
||||
|
||||
ATapDataModel * model = qobject_cast<ATapDataModel *>(sourceModel());
|
||||
|
||||
if (! model || source_left.model() != model || source_right.model() != model)
|
||||
return false;
|
||||
|
||||
QVariant datA = source_left.data(ATapDataModel::UNFORMATTED_DISPLAYDATA);
|
||||
QVariant datB = source_right.data(ATapDataModel::UNFORMATTED_DISPLAYDATA);
|
||||
|
||||
QString strA = datA.toString().toLower();
|
||||
QString strB = datB.toString().toLower();
|
||||
int addressTypeA = model->data(source_left, ATapDataModel::DATA_ADDRESS_TYPE).toInt();
|
||||
int addressTypeB = model->data(source_right, ATapDataModel::DATA_ADDRESS_TYPE).toInt();
|
||||
if ((addressTypeA != 0 || addressTypeB != 0) && addressTypeA != addressTypeB) {
|
||||
return addressTypeA < addressTypeB;
|
||||
} else if (addressTypeA == addressTypeB) {
|
||||
bool result = false;
|
||||
bool identical = false;
|
||||
|
||||
QRegularExpression re = QRegularExpression(QString("[a-f\\d]{2}:[a-f\\d]{2}:[a-f\\d]{2}:[a-f\\d]{2}:[a-f\\d]{2}:[a-f\\d]{2}"));
|
||||
QRegularExpressionMatch match = re.match(strA);
|
||||
if (match.hasMatch())
|
||||
return strA < strB;
|
||||
if (addressTypeA == AT_IPv4) {
|
||||
quint32 valA = model->data(source_left, ATapDataModel::DATA_IPV4_INTEGER).value<quint32>();
|
||||
quint32 valB = model->data(source_right, ATapDataModel::DATA_IPV4_INTEGER).value<quint32>();
|
||||
|
||||
QRegularExpression reIp("(\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3})");
|
||||
match = reIp.match(strA);
|
||||
QRegularExpressionMatch matchB = reIp.match(strB);
|
||||
QStringList listA = strA.split('.');
|
||||
QStringList listB = strB.split('.');
|
||||
if (match.hasMatch() && listA.count() == 4 && ! matchB.hasMatch())
|
||||
return true;
|
||||
else if (match.hasMatch() && listA.count() == 4 && matchB.hasMatch() && listB.count() == 4 ) {
|
||||
quint32 ipA = (listA.at(0).toInt() << 24) + (listA.at(1).toInt() << 16) + (listA.at(2).toInt() << 8) + listA.at(3).toInt();
|
||||
quint32 ipB = (listB.at(0).toInt() << 24) + (listB.at(1).toInt() << 16) + (listB.at(2).toInt() << 8) + listB.at(3).toInt();
|
||||
return ipA < ipB;
|
||||
result = valA < valB;
|
||||
identical = valA == valB;
|
||||
} else {
|
||||
result = QString::compare(datA.toString(), datB.toString(), Qt::CaseInsensitive) < 0;
|
||||
identical = QString::compare(datA.toString(), datB.toString(), Qt::CaseInsensitive) == 0;
|
||||
}
|
||||
|
||||
int portColumn = EndpointDataModel::ENDP_COLUMN_PORT;
|
||||
if (identical && qobject_cast<ConversationDataModel *>(model)) {
|
||||
QModelIndex tstA, tstB;
|
||||
if (source_left.column() == ConversationDataModel::CONV_COLUMN_SRC_ADDR) {
|
||||
portColumn = ConversationDataModel::CONV_COLUMN_SRC_PORT;
|
||||
int col = ConversationDataModel::CONV_COLUMN_DST_ADDR;
|
||||
if (model->portsAreHidden())
|
||||
col -= 1;
|
||||
tstA = model->index(source_left.row(), col);
|
||||
tstB = model->index(source_right.row(), col);
|
||||
} else if (source_left.column() == ConversationDataModel::CONV_COLUMN_DST_ADDR) {
|
||||
portColumn = ConversationDataModel::CONV_COLUMN_DST_PORT;
|
||||
int col = ConversationDataModel::CONV_COLUMN_SRC_ADDR;
|
||||
if (model->portsAreHidden())
|
||||
col -= 1;
|
||||
tstA = model->index(source_left.row(), col);
|
||||
tstB = model->index(source_right.row(), col);
|
||||
}
|
||||
|
||||
if (addressTypeA == AT_IPv4) {
|
||||
quint32 valX = model->data(tstA, ATapDataModel::DATA_IPV4_INTEGER).value<quint32>();
|
||||
quint32 valY = model->data(tstB, ATapDataModel::DATA_IPV4_INTEGER).value<quint32>();
|
||||
|
||||
result = valX < valY;
|
||||
identical = valX == valY;
|
||||
} else {
|
||||
result = QString::compare(model->data(tstA).toString().toLower(), model->data(tstB).toString(), Qt::CaseInsensitive) < 0;
|
||||
identical = QString::compare(model->data(tstA).toString().toLower(), model->data(tstB).toString(), Qt::CaseInsensitive) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (! result && identical && ! model->portsAreHidden()) {
|
||||
int portA = model->data(model->index(source_left.row(), portColumn)).toInt();
|
||||
int portB = model->data(model->index(source_right.row(), portColumn)).toInt();
|
||||
return portA < portB;
|
||||
} else
|
||||
return result;
|
||||
}
|
||||
|
||||
QString iPv6Pattern("(([\\da-f]{1,4}:){7,7}[\\da-f]{1,4}|([\\da-f]{1,4}:){1,7}:|([\\da-f]{1,4}:){1,6}:[\\da-f]{1,4}|"
|
||||
"([\\da-f]{1,4}:){1,5}(:[\\da-f]{1,4}){1,2}|([\\da-f]{1,4}:){1,4}(:[\\da-f]{1,4}){1,3}|([\\da-f]{1,4}:){1,3}(:"
|
||||
"[\\da-f]{1,4}){1,4}|([\\da-f]{1,4}:){1,2}(:[\\da-f]{1,4}){1,5}|[\\da-f]{1,4}:((:[\\da-f]{1,4}){1,6})|:((:[\\da"
|
||||
"-f]{1,4}){1,7}|:)|fe80:(:[\\da-f]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}"
|
||||
"\\d){0,1}\\d)\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}\\d){0,1}\\d)|([\\da-f]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}\\d){0,1}"
|
||||
"\\d)\\.){3,3}(25[0-5]|(2[0-4]|1{0,1}\\d){0,1}\\d))");
|
||||
QRegularExpression reIPv6(iPv6Pattern);
|
||||
match = reIPv6.match(strA);
|
||||
if (match.hasMatch())
|
||||
return strA < strB;
|
||||
|
||||
if (datA.canConvert<double>() && datB.canConvert<double>())
|
||||
return datA.toDouble() < datB.toDouble();
|
||||
|
||||
|
|
Loading…
Reference in New Issue