mfontanini / libtins

High-level, multiplatform C++ network packet sniffing and crafting library.
http://libtins.github.io/
BSD 2-Clause "Simplified" License
1.9k stars 375 forks source link

Cannot use class method as packet handler within the sniff_loop even with c++11 #395

Closed dvilelaf closed 4 years ago

dvilelaf commented 4 years ago

I'm working on an small UDP traffic sniffing example using libtins. Everything worked until I started encapsulating everythin into a class. Acording the tutorial (loop sniffing section), the snifer_loop takes a template functor as an argument, and:

The call to Sniffer::sniff_loop will make the sniffer start processing packets. The functor will be called using each processed packet as its argument. If at some point, you want to stop sniffing, then your functor should return false. Otherwise return true and the Sniffer object will keep looping.

The functor object will be copy constructed, so it must implement copy semantics. There is a helper template function which takes a pointer to an object of a template parameter type, and a member function, and returns a HandlerProxy. That object implements the required operator, in which it forwards the call to the member function pointer provided, using the object pointer given:

Everything works if I use PDU data type, or if I use Packet type with an external function, but once I use Packet type with a method, I get a compiler error:

c++ -Ihandler@sha -I. -I.. -I/usr/local/include/tins -fdiagnostics-color=always -pipe -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wnon-virtual-dtor -std=c++11 -g -fPIC -MD -MQ 'handler@sh
a/handler.cpp.o' -MF 'handler@sha/handler.cpp.o.d' -o 'handler@sha/handler.cpp.o' -c ../handler.cpp
../handler.cpp: In constructor 'Reader::Reader()':
../handler.cpp:24:77: error: cannot convert 'bool (Reader::*)(Tins::Packet&)' to 'Tins::HandlerProxy<Reader>::fun_type {aka bool (Reader::*)(Tins::PDU&)}' for argument '2' to 'Tins::Handl
erProxy<T> Tins::make_sniffer_handler(T*, typename Tins::HandlerProxy<T>::fun_type) [with T = Reader; typename Tins::HandlerProxy<T>::fun_type = bool (Reader::*)(Tins::PDU&)]'
         sniffer.sniff_loop(make_sniffer_handler(this, &Reader::handlerPacket));

The tutorial specifies:

Packets can also be accepted on the functor object used on Sniffer::sniff_loop, but only when you are compiling in C++11 mode.

And I am using the c++11 switch.

#include <tins/tins.h>

using namespace Tins;

bool callbackPDU(PDU &pdu) {

    return true;
}

bool callbackPacket(Packet &packet) {

    return true;
}

class Reader
{
    Reader()
    {
        Sniffer sniffer("wlp3s0");
        // sniffer.sniff_loop(callbackPDU); // This works
        // sniffer.sniff_loop(callbackPacket); // This also works
        // sniffer.sniff_loop(make_sniffer_handler(this, &Reader::handlerPDU)); // This also works
        sniffer.sniff_loop(make_sniffer_handler(this, &Reader::handlerPacket)); // This doesn't work
    }

    bool handlerPDU(PDU &pdu)
    {
        return true;
    }

    bool handlerPacket(Packet &packet)
    {
        return true;
    }
};

int main()
{
    Reader reader();
}

class Functor
{
    Functor(){}

    bool operator()(Packet &packet)
    {
        return true;
    }
};
mfontanini commented 4 years ago

You can't pass &Reader::handlerPacket as that's a member function pointer (it needs an instance of Reader to work). You can use std::bind or a lambda for this. e.g. something like

sniffer.sniff_loop(make_sniffer_handler(this, std::bind(&Reader::handlerPacket, this, std::placeholders::_1)));
dvilelaf commented 4 years ago

That was fast, thanks. But how does it work then with &Reader::handlerPDU? (it should also need an instance, shouldn't it?) I dind't know about std::bind nor std::placeholders, sorry. I've just tried your code and I still get a similar error:

../handler.cpp: In constructor 'Reader::Reader()':
../handler.cpp:25:117: error: cannot convert 'std::_Bind_helper<false, bool (Reader::*)(Tins::Packet&), Reader*, const std::_Placeholder<1>&>::type {aka std::_Bind<bool (Reader::*(Reader*, std::_Placeholder<1>))(Tins::Packet&)>}' to 'Tins::HandlerProxy<Reader>::fun_type {aka bool (Reader::*)(Tins::PDU&)}' for argument '2' to 'Tins::HandlerProxy<T> Tins::make_sniffer_handler(T*, typename Tins::HandlerProxy<T>::fun_type) [with T = Reader; typename Tins::HandlerProxy<T>::fun_type = bool (Reader::*)(Tins::PDU&)]'
         sniffer.sniff_loop(make_sniffer_handler(this, std::bind(&Reader::handlerPacket, this, std::placeholders::_1)));
mfontanini commented 4 years ago

Sorry, I didn't see you were using make_sniffer_handler. Don't use that, just do

sniffer.sniff_loop(std::bind(&Reader::handlerPacket, this, std::placeholders::_1));
dvilelaf commented 4 years ago

That worked great! Thank you so much for your time!

mfontanini commented 4 years ago

No problem!