Solved noobie - using WinPCap
-
I created a new project based on widgets (seems to be the default choice) and added in INCLUDEPATH += "F:\WinpCapDeveloperPack_4_1_2\WpdPack\Include" into the .pro file, added in #include <pcap> to the mainwindow.cpp file, added a QComboBox to the form, added 2 items into the box, ran the program and it works as expected.
I then added in several variables that came from one of the WinPCap console applications. Lots of complaints about unused variables which is to be expected. I then added in:
pcap_if_t *alldevs, *d;
char errbuf[PCAP_ERRBUF_SIZE];if(pcap_findalldevs(&alldevs, errbuf) == -1)
{}All of the 'typing' of this entry had the popup "stuff" show so I know that the includes are in the right place and that its finding the appropriate files and items. The problem comes from the "if" statement... it is 100% identical to the example code (which runs of course, in Visual Studio) saying there is an error of an undefined reference to pcap_findalldevs. Yet in typing that value in, the popups find it as a valid command.
I am not quite sure why... The basics of the function is to get a list of available network interfaces to choose from and its pretty simple.
Where am I going wrong? :)
Thanks
Gary
-
Hi, if try to the _ex-flavored version, does the compiler complain as well on that one, say like this:
if (pcap_findalldevs_ex(PCAP_SRC_IF_STRING,NULL,&alldevs, errbuf) == -1) {}
-
well, ex doesn't seem to exist in the command side, and the PCAP_SRC portion doesn't appear as an option either.
-
Sorry, forgot to mention, you need to add a #define before the pcap #include, like this:
#define HAVE_REMOTE #include "pcap.h"
-
getting closer it seems... (guessing you have played with winpcap)..
pcap_findalldevs_ex is now being seen, the PCAP_SRC... is being seen. When I made those changes and saved the file, it came back with an issue, and the 'light bulb' suggestion for a fix. So I applied that and ended up with:
if(pcap_findalldevs_ex(PCAP_SRC_IF_STRING, nullptr, &alldevs, errbuf) == -1)
After saving and doing a build, it still complains about an undefined reference to the function call, but it also goes on to say:
mainwindow.cpp:24: warning: ISO C++11 does not allow conversion from string literal to 'char *'
and when I mouse over the PCAP_SRC_IF_STRING it gives a Semantics Issue...
"full" code is:
ui->setupUi(this); pcap_if_t *alldevs, *d; pcap_t *fp; char errbuf[PCAP_ERRBUF_SIZE]; int res; struct pcap_pkthdr *header; const u_char *pkt_data; // stuff a couple of values into the combox just to see some data. // real data added later. QString s = "item 1"; ui->cbInterfaces->addItem(s); s = "item 2"; ui->cbInterfaces->addItem(s); if(pcap_findalldevs_ex(PCAP_SRC_IF_STRING, nullptr, &alldevs, errbuf) == -1) {
// //error
// exit(1);
}Really appreciate the assistance. Quite a few differences between QT (first time user) and Visual Studio style coding..
-
Hi @cmwDev,
"Undefined reference" is a linker error, no compiler error.
That means, the linker can't resolve the symbols you included from the pcap library.
You should look up in that archive, there should be a folder containing the DLLs and associated
.lib
files.Then add the following to your
.pro
file:LIBS += -L<Path-to-Libs> -llibrary
, for example:LIBS += L"F:/WinpCapDeveloperPack_4_1_2/WpdPack/Lib" -lpcap
. That would link against alibpcap.lib
. Note that I used forward slashes, as backslash is also an escape character, it may have unwanted side-effects.QtCreator scans these lines and picks up the DLLs from such a directory when you run your app. If you run it outside later, you have to make sure the DLLs are found at runtime.
One more note: you can define variables in QMake:
PCAP = "F:/WinpCapDeveloperPack_4_1_2/WpdPack INCLUDEPATH += $$PCAP/Include LIBS += $$PCAP/Lib -lpcap
-
@aha_1980 is 99% correct :-)
I managed to find some old code of mine (WinPCap is more than 10 years old you know). The C++ compilers were more forgiving in the old days, so first you need to insert a cast like this:
if(pcap_findalldevs_ex((char*)PCAP_SRC_IF_STRING, nullptr, &alldevs, errbuf) == -1) { }
Then to make the linker step, do as @aha_1980 says, but I remember the .lib file used to be called wpcap.lib, thus your .pro file should have something like:
PCAP = "F:/WinpCapDeveloperPack_4_1_2/WpdPack" INCLUDEPATH += $$PCAP/Include LIBS += -L$$PCAP/Lib -lwpcap
(Note that this is for a 32-bit compile which I assume you're doing)
Then, once you've built the program ok, you can try to print out the interfaces, like so:
int i = 0; for(d=alldevs; d != nullptr; d=d->next) { printf("%d. %s", ++i, d->name); if (d->description) printf(" (%s)\n", d->description); else printf(" (No description available)\n"); }
Good luck!
Edit: forgot to mention, the LIBS statement needs the -L prefix, it should read:
LIBS += -L$$PCAP/Lib -lwpcap
(Fixed above also)
-
@hskoglund I didn't state I know exactly which library to link, I said for example. I don't even know if the path is
Lib
, although there is a very high possibility. -
@aha_1980 @hskoglund Thanks for the suggestions.
I've been playing with this for a while. I used the 'add library' in right clicking the main project file. I also did the 'cast' side.
Tons of complaints about the cast and constants, so I went back to the original findalldevs code. Complaints on things are gone but.. (why does there always have to be a but? lol)
In the pcap folders, there is the Lib folder (totally forgot about that side of things) which contains libpacket.a libwpcap.a Packet.lib and wpcap.lib, as well as a x64 folder that only has the 2 lib's in it. I tried adding in the 2 different dot a files and the 2 lib files, one at a time. Always complained during compile that there was no "d" file for debug. I had a DOH moment, went back in and unchecked the debug version (since there are no debug files in the lib) and that got rid of those errors. But it still failed to compile.
So I used the wpcap.lib in the x64 directory and hot damn, if it didn't work. I ran it (of course, the findalldevs function hasn't been coded yet, just {} there) and there it was on the screen.
This is getting exciting! Yet I also have a concern. I am guessing that any code I make will only run in a win64 platform. There wasn't an option during install to select a plain 32 bit version of mingw...
The project is C:\Users\Gary\Documents\build-rcap-Desktop_Qt_5_12_0_MinGW_64_bit-Debug and the make is mingw32-make.exe -j8 in C:\Users\Gary\Documents\build-rcap-Desktop_Qt_5_12_0_MinGW_64_bit-Debug
The code the 'add library' put into the pro file was:
win32: LIBS += -LF:/WinpCapDeveloperPack_4_1_2/WpdPack/Lib/x64/ -lwpcapINCLUDEPATH += F:/WinpCapDeveloperPack_4_1_2/WpdPack/Lib/x64
DEPENDPATH += F:/WinpCapDeveloperPack_4_1_2/WpdPack/Lib/x64(which I am sure makes no difference to you but I thought I would post it in case someone else searches the forums)
-
Nice! And docs on how you built your app, useful for some other hapless soul that comes along.
Only want to add, re. adding code for the findalldevs() function, that the if statement tests for -1, which I remember meant "error", so that the next step, say to enumerate the interfaces, you could do (like in in my example above):
if(pcap_findalldevs(&alldevs, errbuf) == -1) { printf("Error"); exit(1); } int i = 0; for(d=alldevs; d != nullptr; d=d->next) { printf("%d. %s", ++i, d->name); if (d->description) printf(" (%s)\n", d->description); else printf(" (No description available)\n"); }
-
still working on things :) The main form goes out to collect all of the network devices and stuffs the english name into a combo box. The values there are identical to the pktdump_ex example file that runs in console mode in Visual Studio. In the example, it prints out the device ID and the name. Since the combo box only holds the one value, I do some code in a button (start capture) that loops through all of the devices again, matches up the english name, gets the device name, and then breaks out of the loop. All of that works fine. I know the device is accurate because I put that value into a label on the form and it matches exactly what the VS code uses.
VS code:
/* Open the adapter */ if ((fp = pcap_open_live(d->name, 65536, 1, 1000, errbuf)) == NULL) { fprintf(stderr,"\nError opening adapter\n"); return -1; }
my code:
if ((fp = pcap_open_live(d->name, 65536, 1, 1000, errbuf)) == nullptr) { QMessageBox::about(this,"Error", "Failed to open adapter"); return; }
QT complained about the NULL side but was happy with the nullptr. Anyway, I wanted to check the value "to be sure" so I set a break point on the IF statement and ran debug mode. Everything worked right, got the devices, selected the right device. When I went into the button click code (where my code is at) it hit the break point and on the right hand side of the QT interface the debug window was there with a listing of items on the form. Unlike VS where I can mouse over a variable and get the details, in QT all I seem to get is generics. So I went to the right hand window, and went to expand out the plus sign on "d".. little twirly thing came up, and then an error. Out of all of the things on "the right" this is the only one that causes this error:
With all of that said, I did/do have code after that, which loops through the interface waiting for data, and when it gets some I was trying to do a simple append text to a text box like "got data"... I could see stuff happening in my current router log program, but nothing was coming into the text box. Then after a few seconds I got the dreaded "the program is not responding" message at the top of the form. All of that lead me to doing this debug side so I could explore and compare with what VS was doing...Course, VS is a console program and all of the 'functions' happening are all within the base code itself so it knows about all of the variables defined. In doing the QT side, those variables are out of scope when I do a function call like the button click thing, so I moved them all up to the top of the code window and made them static.
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMessageBox>
#include <pcap.h>static pcap_if_t *alldevs, *d;
static pcap_t *fp;
static char errbuf[PCAP_ERRBUF_SIZE];
static int res;
static struct pcap_pkthdr *header;
static const u_char *pkt_data;Not sure if thats the right way but the values do seem available as each function runs. Anyway, I am kinda stuck with the debugging crashing and not being able to see what is happening.
-
oh, one other thing (cant seem to edit my post so)..
In debug mode sitting at the break point, I opened the Debugger Console at the bottom of the screen and tried "? d->name" but it came back and said "Can only evaluate during a debug session." ... seems strange, I was in debug but.. still learning the quirks of QT :) :)
-
I have been making some progress. My loop code is:
while((res = pcap_next_ex(fp, &header, &pkt_data)) >= 0) { QCoreApplication::processEvents(); if(res == 0) { // timeout elapsed continue; } // print packet timestamp and length sprintf(capBuff,"%ld:%ld (%ld)",header->ts.tv_sec, header->ts.tv_usec, header->len); sbuf = QString::fromLocal8Bit(capBuff); qDebug() << "CapString " << capBuff; ui->capText->appendPlainText(sbuf); sbuf = ""; for(i=1; (i < header->caplen + 1); i++) { sprintf(hexBuff,"%.2x ", pkt_data[i-1]); sbuf.append(QString::fromLocal8Bit(hexBuff)); if ((i % LINE_LEN) == 0){ qDebug() << sbuf; ui->capText->appendPlainText(sbuf); sbuf = ""; } } qDebug() << sbuf; ui->capText->appendPlainText(sbuf); qDebug() << ""; }
Adding in the event processing lets the text box show the right data, it matches 100% to what the VS example processes. I still have a lot of tweaking to do, work on decoding the actual packet into english stuff.
With that said, there is still the issue of running in debug mode and the 'd' value crashing the program. The new issue is most strange though. With the program running, I hit the 'x' close application at the top of the form, and as expected the form window terminates. Yet the program still runs, I still see information being displayed in the Application Output window. I had to click on the 'stop program' button in that window to actually get it to terminate.
Why would it keep running after the main form closes?
-
Hi,
You should have an additional condition that allows you to break the loop manually.
-
Yes, I do have that... I added it in after I posted the routine, its now while((res = pcap_next_ex(fp, &header, &pkt_data)) >= 0 && capRunning)
My "start capture" button changes to "stop capture" after the packets routine succeeds, and when I click that, it sets capRunning to false. The button is slow to respond, but the process does stop and I can "X" out of the program easily at that point.
QT is... strange. I can run the VS version and the QT code side by side, I can see the resting period when no packets are there and I've done the stop capture at that point, and the button is still slow to respond. Why it still runs after I X out (instead of pressing stop) is just weird. The form closes completely. Still runs though.
-
You might still be in the loop because neither of the stoping condition went
true
. -
I admit I have been spoiled by the VS interfaces and functions. And this is my first ever attempt at QT. I've tried to google the stuff I have problems with before asking, but it hasn't always worked. What is out there on the net can be quite outdated. I did look at a way to find an 'on closing' event for the form and that didn't work out well. In the UI, I have right clicked on the main menu, went to slots... no close function, but there is a destroy function. I tried that but it never gets called. Probably because the program is still running but the form has vanished. Never seen anything keep running after the X has been clicked. Just saying :)
-
more searching.. I found an interesting tidbit to disable the X button:
setWindowFlags(Qt::Window | Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint);
and then I finally came across...
in mainwindow.h I added in the close event
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();protected:
void closeEvent(QCloseEvent *event);and in mainwindow.cpp I added
void MainWindow::closeEvent(QCloseEvent *event)
{
capRunning = false;
event->accept();
}So at least it does properly stop now. QT is quite the challenge to get used to. Thanks for the assistance and suggestions.