As a bit of context on the issue I have, I am trying to compile my project using MSVC (Visual Studio 2022) on Windows and it produces a bunch of errors, which is surprising, considering the same code builds just fine on GNU-G++.
What I am trying to do is to write a packet spoofer using the libtins library. I am working on a multi-threaded approach, where one thread captures the packets and pushes them to a queue and the other pops out one packet at a time, does "the spoofing", and then forwards it somewhere else.
A construct is provided for capturing network packets in a loop, in the form of a template function.
/* Credits: M.Fontanini, libtins */
template <typename Functor>
void Tins::BaseSniffer::sniff_loop(Functor function, uint32_t max_packets) {
for(iterator it = begin(); it != end(); ++it) {
try {
// If the functor returns false, we're done
#if TINS_IS_CXX11 && !defined(_MSC_VER)
if (!Tins::Internals::invoke_loop_cb(function, *it)) {
return;
}
#else
if (!function(*it->pdu())) {
return;
}
#endif
}
catch(malformed_packet&) { }
catch(pdu_not_found&) { }
if (max_packets && --max_packets == 0) {
return;
}
}
}
This works by binding a callback function which will be called every time a packet is captured. I have tried to leverage this by creating a wrapper class, called PacketSniffer, where I bind a callback function to Sniffer::sniff_loop which pushes every captured packet to a queue.
bool
PacketSniffer::callback(Tins::Packet &packet,
ThreadSafeQueue<Tins::Packet> &packetq,
bool &running) {
packetq.push(packet);
return running;
}
void PacketSniffer::run(ThreadSafeQueue<Tins::Packet> &packetq, bool &running) {
try {
sniffer_->sniff_loop(std::bind(&PacketSniffer::callback, this,
std::placeholders::_1, std::ref(packetq),
std::ref(running)));
} catch (std::exception &ex) {
throw std::runtime_error(ex.what());
}
}
The actual call where I use this in my app:
// Packet capture
bool running = true;
std::thread capture([&pq = packetq_, st, &running,
&iface_value, &pcap_filter_value]() {
PacketSniffer ps(st, iface_value.data(),
pcap_filter_value.data());
ps.run(pq, running);
});
MSVC compiler error:
C:\Users\adrian\repos\src\spoofer\build\_deps\libtins-src\include\tins/sniffer.h(681,18): error C2672: 'operator __surrogate_func': no matchi
ng overloaded function found [C:\Users\adrian\repos\src\spoofer\build\src\spoofer.vcxproj]
C:\Users\adrian\repos\src\spoofer\src\sniffer.cpp(74): message : see reference to function template instantiation 'void Tins::BaseSniffer::sn
iff_loop<std::_Binder<std::_Unforced,bool (__cdecl spoofer::PacketSniffer::* )(Tins::Packet &,ThreadSafeQueue<Tins::Packet> &,bool &),spoofer::
PacketSniffer *,const std::_Ph<1> &,std::reference_wrapper<ThreadSafeQueue<Tins::Packet>>,std::reference_wrapper<bool>>>(Functor,uint32_t)' b
eing compiled [C:\Users\adrian\repos\src\spoofer\build\src\spoofer.vcxproj]
with
[
Functor=std::_Binder<std::_Unforced,bool (__cdecl spoofer::PacketSniffer::* )(Tins::Packet &,ThreadSafeQueue<Tins::Packet> &,boo
l &),spoofer::PacketSniffer *,const std::_Ph<1> &,std::reference_wrapper<ThreadSafeQueue<Tins::Packet>>,std::reference_wrapper<bool>>
]
C:\Users\adrian\repos\src\spoofer\build\_deps\libtins-src\include\tins/sniffer.h(681,1): error C2893: Failed to specialize function template
'unknown-type std::_Binder<std::_Unforced,bool (__cdecl spoofer::PacketSniffer::* )(Tins::Packet &,ThreadSafeQueue<Tins::Packet> &,bool &),spo
ofy::PacketSniffer *,const std::_Ph<1> &,std::reference_wrapper<ThreadSafeQueue<Tins::Packet>>,std::reference_wrapper<bool>>::operator ()(_Un
bound &&...) noexcept(<expr>) const' [C:\Users\adrian\repos\src\spoofer\build\src\spoofer.vcxproj]
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.32.31326\include\functional(2002): message : see declaration of 'std
::_Binder<std::_Unforced,bool (__cdecl spoofer::PacketSniffer::* )(Tins::Packet &,ThreadSafeQueue<Tins::Packet> &,bool &),spoofer::PacketSniffe
r *,const std::_Ph<1> &,std::reference_wrapper<ThreadSafeQueue<Tins::Packet>>,std::reference_wrapper<bool>>::operator ()' [C:\Users\adrian\r
epos\src\spoofer\build\src\spoofer.vcxproj]
C:\Users\adrian\repos\src\spoofer\build\_deps\libtins-src\include\tins/sniffer.h(681,1): message : With the following template arguments: [C:
\Users\adrian\repos\src\spoofer\build\src\spoofer.vcxproj]
C:\Users\adrian\repos\src\spoofer\build\_deps\libtins-src\include\tins/sniffer.h(681,1): message : '_Unbound={Tins::PDU &}' [C:\Users\adrian
\repos\src\spoofer\build\src\spoofer.vcxproj]
A minimal, single threaded example from one of my tests, producing the same error. Don't know how useful this is, as it needs to be compiled and linked against libtins, but maybe it can provide some additional context:
#include <memory>
#include <iostream>
#include <exception>
#include <functional>
#include <tins/tins.h>
#include "utils/queue.h"
enum class SnifferType { Sniffer, FileSniffer };
class PacketSniffer {
public:
PacketSniffer(SnifferType st, const char *iface, const char *capture_filter) {
setup(st, iface, capture_filter);
}
PacketSniffer() = delete;
void run(ThreadSafeQueue<Tins::Packet>& packetq, bool &running);
private:
void setup(SnifferType st, const char *iface, const char *capture_filter) {
Tins::SnifferConfiguration config;
config.set_promisc_mode(true);
config.set_filter(capture_filter);
try {
if (st == SnifferType::FileSniffer) {
sniffer_ = std::make_unique<Tins::FileSniffer>(iface, config);
} else {
sniffer_ = std::make_unique<Tins::Sniffer>(iface, config);
}
} catch (Tins::pcap_error &e) {
throw std::runtime_error(e.what());
} catch (std::exception &e) {
throw std::runtime_error(e.what());
}
}
bool callback(Tins::Packet& packet,
ThreadSafeQueue<Tins::Packet>& packetq,
bool &running){
packetq.push(packet);
return running;
}
std::unique_ptr<Tins::BaseSniffer> sniffer_;
};
struct TestContext {
TestContext(const char *file_path, const char *filter) :
sniffer_({ SnifferType::FileSniffer, file_path, filter}) {}
PacketSniffer sniffer_;
ThreadSafeQueue<Tins::Packet> queue_;
};
int main() {
TestContext ctx("packets.pcap", "");
bool running = true;
ctx.sniffer_.run(ctx.queue_, running);
return 0;
}
What am I missing here regarding std::bind, that produces these errors? I find it weird that the code compiles on G++ but not on MSVC and I think it's related to this somehow.