summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt10
-rw-r--r--mainwindow.cpp144
-rw-r--r--mainwindow.h19
-rw-r--r--pcapplusplus.cpp111
-rw-r--r--pcapplusplus.h4
5 files changed, 241 insertions, 47 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 88f7c74..5e47b74 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -45,6 +45,16 @@ if(ENABLE_SANITIZER)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fsanitize=undefined -fno-sanitize=alignment -fsanitize=enum -fsanitize=leak")
endif()
+option(ENABLE_THREAD_SANITIZER "Enable TSAN." OFF)
+if(ENABLE_THREAD_SANITIZER)
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=thread")
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread")
+endif()
+
+if (ENABLE_SANITIZER AND ENABLE_THREAD_SANITIZER)
+ message(FATAL_ERROR "ENABLE_SANITIZER and ENABLE_THREAD_SANITIZER must not be enabled at the same time!")
+endif()
+
set(PROJECT_SOURCES
main.cpp
diff --git a/mainwindow.cpp b/mainwindow.cpp
index ca4f175..7391403 100644
--- a/mainwindow.cpp
+++ b/mainwindow.cpp
@@ -3,19 +3,27 @@
#include <exception>
#include <qfiledialog.h>
-
-#include <iostream>
+#include <QMouseEvent>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, tableContextMenu()
, myHexEdit()
, ui(new Ui::MainWindow)
- , ppp(nullptr)
+ , statusbarMessage()
{
+ if (!ui)
+ throw std::runtime_error("UI - MainWindow: not enough memory available");
+
ui->setupUi(this);
ui->gridLayout->addWidget(&myHexEdit.editor);
ui->tableWidget->setContextMenuPolicy(Qt::CustomContextMenu);
+ ui->tableWidget->setMouseTracking(true);
+ qApp->installEventFilter(this);
+
+ myHexEdit.bytewindow = new ByteWindow(parent);
+ if (!myHexEdit.bytewindow)
+ throw std::runtime_error("UI - ByteWindow: not enough memory available");
const auto& enableTableButtons = [](MainWindow *mainwindow, bool enable) {
mainwindow->tableContextMenu.randomize.setEnabled(enable);
@@ -34,15 +42,23 @@ MainWindow::MainWindow(QWidget *parent)
enableMenuButtons(this, false);
enableHexEditButtons(this, false);
- const auto& isIpColumn = [this](const int& column) {
- switch (column) {
- case 8:
- return std::make_tuple(true, false);
- case 9:
- return std::make_tuple(false, true);
- default:
- return std::make_tuple(false, false);
- }
+ const auto& isEthernetSrcColumn = [this](const int& column) {
+ return column == 6;
+ };
+ const auto& isEthernetDstColumn = [this](const int& column) {
+ return column == 7;
+ };
+ const auto& isIpSrcColumn = [this](const int& column) {
+ return column == 8;
+ };
+ const auto& isIpDstColumn = [this](const int& column) {
+ return column == 9;
+ };
+ const auto& isPortSrcColumn = [this](const int& column) {
+ return column == 10;
+ };
+ const auto& isPortDstColumn = [this](const int& column) {
+ return column == 11;
};
tableContextMenu.randomize.setText("Randomize");
@@ -58,34 +74,55 @@ MainWindow::MainWindow(QWidget *parent)
myHexEdit.contextMenu.addAction(&myHexEdit.deleteBytes);
myHexEdit.contextMenu.addAction(&myHexEdit.deleteSelection);
- connect(&tableContextMenu.randomize, &QAction::triggered, this, [this, isIpColumn](bool checked __attribute__((unused))){
+ connect(&tableContextMenu.randomize, &QAction::triggered, this, [this, isEthernetSrcColumn, isEthernetDstColumn, isIpSrcColumn, isIpDstColumn, isPortSrcColumn, isPortDstColumn](bool checked __attribute__((unused))){
if (!ppp)
throw std::runtime_error("Can not randomize, no packets available");
- const auto & row = ui->tableWidget->currentRow();
- const auto & ipColumn = isIpColumn(ui->tableWidget->currentColumn() + 1);
+ const auto & y = ui->tableWidget->currentRow();
+ const auto & col = ui->tableWidget->currentColumn() + 1;
- if (std::get<0>(ipColumn) || std::get<1>(ipColumn)) {
- ppp->randomizeIp(row, std::get<0>(ipColumn));
- if (!MainWindow::updateTableRow(row))
- throw std::runtime_error("BUG: Could not change table row");
- } else throw std::runtime_error("BUG: No IP column selected");
+ if (isEthernetSrcColumn(col) || isEthernetDstColumn(col)) {
+ ppp->randomizeEth(y, isEthernetSrcColumn(col));
+ } else if (isIpSrcColumn(col) || isIpDstColumn(col)) {
+ ppp->randomizeIp(y, isIpSrcColumn(col));
+ } else if (isPortSrcColumn(col) || isPortDstColumn(col)) {
+ ppp->randomizePort(y, isPortSrcColumn(col));
+ } else throw std::runtime_error("BUG: No supported column selected");
+
+ if (!MainWindow::updateTableRow(y))
+ throw std::runtime_error("BUG: Could not update table row");
});
- connect(ui->tableWidget, &QTableWidget::itemChanged, this, [this, isIpColumn](QTableWidgetItem *item) {
- if (!item)
+ connect(ui->tableWidget, &QTableWidget::itemChanged, this, [this, isEthernetSrcColumn, isEthernetDstColumn, isIpSrcColumn, isIpDstColumn, isPortSrcColumn, isPortDstColumn](QTableWidgetItem *item) {
+ if (!item || !ppp)
return;
- const auto & ipColumn = isIpColumn(ui->tableWidget->currentColumn() + 1);
- if (std::get<0>(ipColumn) || std::get<1>(ipColumn))
- ppp->setIp(ui->tableWidget->currentRow(), item->text().toStdString(), std::get<0>(ipColumn));
+ const auto & y = ui->tableWidget->currentRow();
+ const auto & col = ui->tableWidget->currentColumn() + 1;
+ const auto & text = item->text().toStdString();
+
+ if (isEthernetSrcColumn(col) || isEthernetDstColumn(col)) {
+ ppp->setEth(y, text, isEthernetSrcColumn(col));
+ } else if (isIpSrcColumn(col) || isIpDstColumn(col)) {
+ ppp->setIp(y, text, isIpSrcColumn(col));
+ } else if (isPortSrcColumn(col) || isPortDstColumn(col)) {
+ ppp->setPort(y, text, isPortSrcColumn(col));
+ }
+
+ if (isProcessing)
+ return;
+ if (!MainWindow::updateTableRow(y))
+ throw std::runtime_error("BUG: Could not update table row");
});
- connect(ui->tableWidget, &QTableWidget::customContextMenuRequested, this, [this, enableTableButtons, isIpColumn](const QPoint& pos){
+ connect(ui->tableWidget, &QTableWidget::customContextMenuRequested, this, [this, enableTableButtons, isEthernetSrcColumn, isEthernetDstColumn, isIpSrcColumn, isIpDstColumn, isPortSrcColumn, isPortDstColumn](const QPoint& pos){
const auto & globalPos = ui->tableWidget->viewport()->mapToGlobal(pos);
- const auto & ipColumn = isIpColumn(ui->tableWidget->columnAt(pos.x()) + 1);
+ const auto & col = ui->tableWidget->currentColumn() + 1;
- if ((std::get<0>(ipColumn) || std::get<1>(ipColumn)) && ppp)
+ if (ppp
+ && (isEthernetSrcColumn(col) || isEthernetDstColumn(col)
+ || isIpSrcColumn(col) || isIpDstColumn(col)
+ || isPortSrcColumn(col) || isPortDstColumn(col)))
enableTableButtons(this, true);
else
enableTableButtons(this, false);
@@ -93,13 +130,13 @@ MainWindow::MainWindow(QWidget *parent)
tableContextMenu.menu.popup(globalPos);
});
- connect(&myHexEdit.bytewindow, &QDialog::finished, this, [&](int result) {
+ connect(myHexEdit.bytewindow, &QDialog::finished, this, [&](int result) {
if (result == 0)
return;
- const auto option = myHexEdit.bytewindow.getOption();
- const auto offset = myHexEdit.bytewindow.getOffset();
- const auto size = myHexEdit.bytewindow.getSize();
+ const auto option = myHexEdit.bytewindow->getOption();
+ const auto offset = myHexEdit.bytewindow->getOffset();
+ const auto size = myHexEdit.bytewindow->getSize();
const auto rawPacket = currentSelectedPacket();
if (!rawPacket)
return;
@@ -136,8 +173,8 @@ MainWindow::MainWindow(QWidget *parent)
const auto& selectionBegin = myHexEdit.editor.getSelectionBegin();
const auto& selectedLength = myHexEdit.editor.getSelectionEnd() - selectionBegin;
const auto& showByteWindow = [this, selectedLength](const ByteWindowOption& opt, int offset) {
- myHexEdit.bytewindow.set(opt, offset, selectedLength > 0 ? selectedLength : 1);
- myHexEdit.bytewindow.show();
+ myHexEdit.bytewindow->set(opt, offset, selectedLength > 0 ? selectedLength : 1);
+ myHexEdit.bytewindow->show();
};
if (selectedItem == &myHexEdit.prependBytes) {
@@ -249,17 +286,21 @@ MainWindow::MainWindow(QWidget *parent)
pcpp::Packet packet;
if (!ppp) throw std::runtime_error("PcapPlusPlus was not initialized.");
firstPacketTs = 0;
+
+ isProcessing = true;
while (ppp->processPacket(packet)) {
if (!firstPacketTs)
firstPacketTs = packet.getRawPacket()->getPacketTimeStamp().tv_sec;
emit onPacketAvailable();
}
+ isProcessing = false;
+
pcpp::PcapFileReaderDevice::PcapStats stats;
if (ppp->getPcapStatistics(stats))
- ui->statusbar->showMessage("PCAP loaded. Packets: " + QString::fromStdString(std::to_string(stats.packetsRecv))
- + ", Dropped: " + QString::fromStdString(std::to_string(stats.packetsDrop)));
+ updateStatusBarMessage("PCAP loaded. Packets: " + QString::fromStdString(std::to_string(stats.packetsRecv))
+ + ", Dropped: " + QString::fromStdString(std::to_string(stats.packetsDrop)));
else
- ui->statusbar->showMessage("No PCAP statistics available.");
+ updateStatusBarMessage("No PCAP statistics available.");
});
connect(this, &MainWindow::onPacketAvailable, this, [&]() {
@@ -304,7 +345,13 @@ MainWindow::MainWindow(QWidget *parent)
MainWindow::~MainWindow()
{
delete ui;
+ ui = nullptr;
+
+ delete myHexEdit.bytewindow;
+ myHexEdit.bytewindow = nullptr;
+
delete ppp;
+ ppp = nullptr;
}
pcpp::RawPacket* MainWindow::currentSelectedPacket()
@@ -369,11 +416,6 @@ bool MainWindow::addTableRow()
ui->tableWidget->item(ui->tableWidget->rowCount() - 1, 2)->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren);
ui->tableWidget->item(ui->tableWidget->rowCount() - 1, 3)->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren);
ui->tableWidget->item(ui->tableWidget->rowCount() - 1, 4)->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren);
- ui->tableWidget->item(ui->tableWidget->rowCount() - 1, 5)->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren);
- ui->tableWidget->item(ui->tableWidget->rowCount() - 1, 6)->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren);
-
- ui->tableWidget->item(ui->tableWidget->rowCount() - 1, 9)->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren);
- ui->tableWidget->item(ui->tableWidget->rowCount() - 1, 10)->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren);
ui->tableWidget->item(ui->tableWidget->rowCount() - 1, 11)->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren);
return updateTableRow(ui->tableWidget->rowCount() - 1);
@@ -410,3 +452,21 @@ bool MainWindow::updateTableRow(size_t index)
return true;
}
+
+bool MainWindow::eventFilter(QObject *obj __attribute__((unused)), QEvent *event)
+{
+ if (event->type() == QEvent::MouseMove)
+ {
+ QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
+
+ mouse.x = mouseEvent->pos().x();
+ mouse.y = mouseEvent->pos().y();
+ ui->statusbar->showMessage(tr("[x: %1 | y: %2] %3").arg(mouse.x, 4).arg(mouse.y, 4).arg(statusbarMessage));
+ }
+ return false;
+}
+
+void MainWindow::updateStatusBarMessage(const QString & message) {
+ statusbarMessage = message;
+ ui->statusbar->showMessage(tr("[x: %1 | y: %2] %3").arg(mouse.x, 4).arg(mouse.y, 4).arg(message));
+}
diff --git a/mainwindow.h b/mainwindow.h
index 9ccf159..a7d3a37 100644
--- a/mainwindow.h
+++ b/mainwindow.h
@@ -5,6 +5,7 @@
#include "pcapplusplus.h"
#include "qhexedit2/src/qhexedit.h"
+#include <atomic>
#include <optional>
#include <QMainWindow>
#include <QMenu>
@@ -25,6 +26,9 @@ public:
bool updateTableRow(size_t index);
private:
+ bool eventFilter(QObject *obj, QEvent *event);
+ void updateStatusBarMessage(const QString & message);
+
struct {
QMenu menu;
QAction randomize;
@@ -37,12 +41,19 @@ private:
QAction deleteBytes;
QAction deleteSelection;
QHexEdit editor;
- ByteWindow bytewindow;
+ ByteWindow *bytewindow = nullptr;
} myHexEdit;
- time_t firstPacketTs;
- Ui::MainWindow *ui;
- PcapPlusPlus *ppp;
+ struct {
+ int x;
+ int y;
+ } mouse;
+
+ time_t firstPacketTs = 0;
+ Ui::MainWindow *ui = nullptr;
+ QString statusbarMessage;
+ PcapPlusPlus *ppp = nullptr;
+ std::atomic<bool> isProcessing = false;
signals:
void processPcap();
diff --git a/pcapplusplus.cpp b/pcapplusplus.cpp
index 97042f9..ea0acb7 100644
--- a/pcapplusplus.cpp
+++ b/pcapplusplus.cpp
@@ -6,8 +6,9 @@
#include <IPLayer.h>
#include <IPv4Layer.h>
#include <IPv6Layer.h>
-#include <TcpLayer.h>
#include <random>
+#include <SystemUtils.h>
+#include <TcpLayer.h>
#include <tuple>
#include <UdpLayer.h>
@@ -121,6 +122,30 @@ std::vector<pcpp::Packet>::iterator PcapPlusPlus::parsedPacketsEnd()
return parsedPackets.end();
}
+bool PcapPlusPlus::randomizeEth(size_t index, bool isSourceEth)
+{
+ std::default_random_engine generator(std::chrono::system_clock::now().time_since_epoch().count());
+ auto retval = false;
+ auto parsedPacket = pcpp::Packet(&getRawPacket(index));
+ auto * ethLayer = parsedPacket.getLayerOfType<pcpp::EthLayer>();
+
+ if (ethLayer) {
+ std::uniform_int_distribution<unsigned long long int> ethDistribution(0, 0xFFFFFFFFFFFFFFFF);
+ unsigned long long int newEthAddr = ethDistribution(generator);
+
+ if (isSourceEth)
+ std::memcpy(ethLayer->getEthHeader()->srcMac, reinterpret_cast<uint8_t *>(&newEthAddr), 6);
+ else
+ std::memcpy(ethLayer->getEthHeader()->dstMac, reinterpret_cast<uint8_t *>(&newEthAddr), 6);
+
+ retval = true;
+ }
+
+ parsedPackets[index] = parsedPacket;
+
+ return retval;
+}
+
bool PcapPlusPlus::randomizeIp(size_t index, bool isSourceIp)
{
std::default_random_engine generator(std::chrono::system_clock::now().time_since_epoch().count());
@@ -157,6 +182,60 @@ bool PcapPlusPlus::randomizeIp(size_t index, bool isSourceIp)
return retval;
}
+bool PcapPlusPlus::randomizePort(size_t index, bool isSourcePort)
+{
+ std::default_random_engine generator(std::chrono::system_clock::now().time_since_epoch().count());
+ auto retval = false;
+ auto parsedPacket = pcpp::Packet(&getRawPacket(index));
+ auto * udpLayer = parsedPacket.getLayerOfType<pcpp::UdpLayer>();
+ auto * tcpLayer = parsedPacket.getLayerOfType<pcpp::TcpLayer>();
+ std::uniform_int_distribution<unsigned short> portDistribution(0, 0xFFFF);
+
+ if (udpLayer) {
+ if (isSourcePort)
+ udpLayer->getUdpHeader()->portSrc = portDistribution(generator);
+ else
+ udpLayer->getUdpHeader()->portDst = portDistribution(generator);
+
+ retval = true;
+ }
+
+ if (tcpLayer) {
+ if (isSourcePort)
+ tcpLayer->getTcpHeader()->portSrc = portDistribution(generator);
+ else
+ tcpLayer->getTcpHeader()->portDst = portDistribution(generator);
+
+ retval = true;
+ }
+
+ parsedPackets[index] = parsedPacket;
+
+ return retval;
+}
+
+bool PcapPlusPlus::setEth(size_t index, const std::string & eth, bool isSourceEth)
+{
+ auto retval = false;
+ auto parsedPacket = pcpp::Packet(&getRawPacket(index));
+ auto * ethLayer = parsedPacket.getLayerOfType<pcpp::EthLayer>();
+
+ if (ethLayer) {
+ pcpp::MacAddress mac(eth);
+
+ if (isSourceEth)
+ std::memcpy(ethLayer->getEthHeader()->srcMac, mac.getRawData(), 6);
+ else
+ std::memcpy(ethLayer->getEthHeader()->dstMac, mac.getRawData(), 6);
+
+ retval = true;
+ }
+
+ parsedPackets[index] = parsedPacket;
+
+ return retval;
+}
+
bool PcapPlusPlus::setIp(size_t index, const std::string & ip, bool isSourceIp)
{
auto retval = false;
@@ -191,6 +270,36 @@ bool PcapPlusPlus::setIp(size_t index, const std::string & ip, bool isSourceIp)
return retval;
}
+bool PcapPlusPlus::setPort(size_t index, const std::string & port, bool isSourcePort)
+{
+ auto retval = false;
+ auto parsedPacket = pcpp::Packet(&getRawPacket(index));
+ auto * udpLayer = parsedPacket.getLayerOfType<pcpp::UdpLayer>();
+ auto * tcpLayer = parsedPacket.getLayerOfType<pcpp::TcpLayer>();
+ const unsigned short portNum = pcpp::hostToNet16(std::stoi(port));
+
+ if (udpLayer) {
+ if (isSourcePort)
+ udpLayer->getUdpHeader()->portSrc = portNum;
+ else
+ udpLayer->getUdpHeader()->portDst = portNum;
+
+ retval = true;
+ }
+
+ if (tcpLayer) {
+ if (isSourcePort)
+ tcpLayer->getTcpHeader()->portSrc = portNum;
+ else
+ tcpLayer->getTcpHeader()->portDst = portNum;
+
+ retval = true;
+ }
+
+ parsedPackets[index] = parsedPacket;
+
+ return retval;
+}
bool PcapPlusPlus::getPcapStatistics(pcpp::IFileDevice::PcapStats & stats)
{
diff --git a/pcapplusplus.h b/pcapplusplus.h
index 9b18351..e2ab070 100644
--- a/pcapplusplus.h
+++ b/pcapplusplus.h
@@ -21,8 +21,12 @@ public:
std::vector<pcpp::RawPacket>::iterator rawPacketsEnd();
std::vector<pcpp::Packet>::iterator parsedPacketsBegin();
std::vector<pcpp::Packet>::iterator parsedPacketsEnd();
+ bool randomizeEth(size_t index, bool isSourceEth);
bool randomizeIp(size_t index, bool isSourceIp);
+ bool randomizePort(size_t index, bool isSourcePort);
+ bool setEth(size_t index, const std::string & eth, bool isSourceIp);
bool setIp(size_t index, const std::string & ip, bool isSourceIp);
+ bool setPort(size_t index, const std::string & port, bool isSourceIp);
bool getPcapStatistics(pcpp::IFileDevice::PcapStats & stats);
static const pcpp::Layer *getFirstLayer(const pcpp::Packet & packet);