Sniffing with pcap in a GUI application
-
Hi everyone,
I am trying to write an application than while it is sniffing packets using the pcap library, it plots the received data in a GUI.
I arrived to this point...
main.cpp#include <QApplication> #include "receiver.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); Receiver receiver; receiver.setMinimumSize(900, 600); receiver.show(); return app.exec(); }
receiver.h
#ifndef RECEIVER_H #define RECEIVER_H #include <QWidget> #include "qcustomplot.h" using namespace std; QT_BEGIN_NAMESPACE class QLabel; QT_END_NAMESPACE class Receiver : public QWidget { Q_OBJECT public: explicit Receiver(QWidget *parent = nullptr); private slots: void stampa(int res); private: QCustomPlot* plot = new QCustomPlot;; QLabel *statusLabel = nullptr; int sniff(); signals: void signalPacketReceived(int res); }; #endif
receiver.cpp
#include <pcap.h> #include <QLabel> #include <QPushButton> #include <QVBoxLayout> #include "qcustomplot.h" #include <sstream> #include "receiver.h" Receiver::Receiver(QWidget *parent) : QWidget(parent) { statusLabel = new QLabel(tr("Listening for broadcasted messages")); statusLabel->setWordWrap(true); auto quitButton = new QPushButton(tr("&Quit")); connect(quitButton, &QPushButton::clicked, this, &Receiver::close); connect(this, SIGNAL(signalPacketReceived(int)), this, SLOT(stampa(int))); //auto plot = new QCustomPlot; plot->addGraph(); plot->graph()->setScatterStyle(QCPScatterStyle::ssCircle); plot->graph()->setLineStyle(QCPGraph::LineStyle::lsNone); auto plotLayout = new QHBoxLayout; plotLayout->addWidget(plot); auto buttonLayout = new QHBoxLayout; buttonLayout->addStretch(1); buttonLayout->addWidget(quitButton); buttonLayout->addStretch(1); auto mainLayout = new QVBoxLayout; mainLayout->addWidget(statusLabel); mainLayout->addLayout(plotLayout, 1); mainLayout->addLayout(buttonLayout); setLayout(mainLayout); setWindowTitle(tr("Test")); sniff(); } int Receiver::sniff() { pcap_t* handle; /* Session handle */ char* dev; /* The device to sniff on */ char errbuf[PCAP_ERRBUF_SIZE]; /* Error string */ struct bpf_program fp; /* The compiled filter */ char filter_exp[] = "port 53"; /* The filter expression */ bpf_u_int32 mask; /* Our netmask */ bpf_u_int32 net; /* Our IP */ struct pcap_pkthdr* header; /* The header that pcap gives us */ const u_char* packet; /* The actual packet */ int num_packets = -1; /* number of packets to capture */ /* Define the device */ dev = pcap_lookupdev(errbuf); if (dev == NULL) { fprintf(stderr, "Couldn't find default device: %s\n", errbuf); return(2); } /* Find the properties for the device */ if (pcap_lookupnet(dev, &net, &mask, errbuf) == -1) { fprintf(stderr, "Couldn't get netmask for device %s: %s\n", dev, errbuf); net = 0; mask = 0; } /* Open the session in promiscuous mode */ handle = pcap_open_live(dev, BUFSIZ, 1, 1000, errbuf); if (handle == NULL) { fprintf(stderr, "Couldn't open device %s: %s\n", dev, errbuf); return(2); } /* Compile and apply the filter */ if (pcap_compile(handle, &fp, filter_exp, 0, net) == -1) { fprintf(stderr, "Couldn't parse filter %s: %s\n", filter_exp, pcap_geterr(handle)); return(2); } if (pcap_setfilter(handle, &fp) == -1) { fprintf(stderr, "Couldn't install filter %s: %s\n", filter_exp, pcap_geterr(handle)); return(2); } /* Grab a packets */ //pcap_loop(handle, num_packets, got_packet, reinterpret_cast<u_char *>(this)); statusLabel->setText(QString("Check 1")); while (true) { int res = pcap_next_ex(handle, &header, &packet); statusLabel->setText(QString::number(res)); if (res == 0) continue; // timeout elapsed emit signalPacketReceived(res); } /* cleanup */ pcap_freecode(&fp); pcap_close(handle); printf("\nCapture complete.\n"); return(0); } void Receiver::stampa(int res) { statusLabel->setText(QString::number(res)); }
The program compiles and runs, but it freezes forever in the loop at the end of the sniff() function. How can I modify this program in a way that for every received packeges the status label in the main GUI is updated and that the GUI stays responsive for every other button I will implement later?
-
@Mattia said in Sniffing with pcap in a GUI application:
it freezes forever in the loop at the end of the sniff() function
Oh, who could have expected that?
while (true) { int res = pcap_next_ex(handle, &header, &packet); statusLabel->setText(QString::number(res)); if (res == 0) continue; // timeout elapsed emit signalPacketReceived(res); }
How can I modify this program in a way that for every received packeges the status label in the main GUI is updated and that the GUI stays responsive for every other button I will implement later?
Use a proper multithreading technology or (if existing) an asynch API.
while(true)
in main GUI thread (and emitting signals from there) is deadly. It will always block/freeze your GUI. -
If pcap calls block/are synchronous then it would presumably have to go into its own thread.
If calls are non-blocking/asynchronous then you can use those from main thread. If it's suitable maybe you can make calls on a timer. E.g. call your code intermittently rather than in a blocking loop.