1
0
mirror of https://github.com/mfontanini/libtins synced 2026-01-23 02:35:57 +01:00

Merge pull request #51 from EricMCornelius/master

Use type_traits to enable Packet& sniff_loop callback variant in C++11
This commit is contained in:
Matias Fontanini
2015-03-06 09:42:25 -08:00
2 changed files with 128 additions and 83 deletions

View File

@@ -5,14 +5,14 @@
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are
* met: * met:
* *
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above * * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer * copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the * in the documentation and/or other materials provided with the
* distribution. * distribution.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@@ -26,10 +26,14 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* *
*/ */
#ifndef TINS_INTERNALS_H #ifndef TINS_INTERNALS_H
#define TINS_INTERNALS_H #define TINS_INTERNALS_H
#if TINS_IS_CXX11
#include <type_traits>
#endif
#include <sstream> #include <sstream>
#include <string> #include <string>
#include <stdint.h> #include <stdint.h>
@@ -50,16 +54,16 @@ class byte_array {
public: public:
typedef uint8_t* iterator; typedef uint8_t* iterator;
typedef const uint8_t* const_iterator; typedef const uint8_t* const_iterator;
byte_array() { byte_array() {
std::fill(begin(), end(), 0); std::fill(begin(), end(), 0);
} }
template<typename InputIterator> template<typename InputIterator>
byte_array(InputIterator start, InputIterator last) { byte_array(InputIterator start, InputIterator last) {
std::copy(start, last, data); std::copy(start, last, data);
} }
template<typename InputIterator> template<typename InputIterator>
byte_array(InputIterator start) { byte_array(InputIterator start) {
std::copy(start, n, data); std::copy(start, n, data);
@@ -72,23 +76,23 @@ public:
uint8_t operator[](size_t i) const{ uint8_t operator[](size_t i) const{
return data[i]; return data[i];
} }
iterator begin() { iterator begin() {
return data; return data;
} }
iterator end() { iterator end() {
return data + n; return data + n;
} }
const_iterator begin() const { const_iterator begin() const {
return data; return data;
} }
const_iterator end() const { const_iterator end() const {
return data + n; return data + n;
} }
size_t size() const { size_t size() const {
return n; return n;
} }
@@ -106,14 +110,14 @@ struct enable_if {
template<typename T> template<typename T>
struct enable_if<false, T> { struct enable_if<false, T> {
}; };
PDU *pdu_from_flag(Constants::Ethernet::e flag, const uint8_t *buffer, PDU *pdu_from_flag(Constants::Ethernet::e flag, const uint8_t *buffer,
uint32_t size, bool rawpdu_on_no_match = true); uint32_t size, bool rawpdu_on_no_match = true);
PDU *pdu_from_flag(Constants::IP::e flag, const uint8_t *buffer, PDU *pdu_from_flag(Constants::IP::e flag, const uint8_t *buffer,
uint32_t size, bool rawpdu_on_no_match = true); uint32_t size, bool rawpdu_on_no_match = true);
PDU *pdu_from_dlt_flag(int flag, const uint8_t *buffer, PDU *pdu_from_dlt_flag(int flag, const uint8_t *buffer,
uint32_t size, bool rawpdu_on_no_match = true); uint32_t size, bool rawpdu_on_no_match = true);
PDU *pdu_from_flag(PDU::PDUType type, const uint8_t *buffer, uint32_t size); PDU *pdu_from_flag(PDU::PDUType type, const uint8_t *buffer, uint32_t size);
@@ -200,6 +204,39 @@ template<>
struct is_unsigned_integral<uint64_t> { struct is_unsigned_integral<uint64_t> {
static const bool value = true; static const bool value = true;
}; };
#if TINS_IS_CXX11
// Part of C++14 standard library
template<bool B, class T = void>
using enable_if_t = typename std::enable_if<B,T>::type;
// Template metaprogramming trait to determine if a functor can accept another parameter as an argument
template <class T, class P, class=void>
struct accepts_type : std::false_type { };
template <class T, class P>
struct accepts_type<T, P, enable_if_t<
std::is_same< decltype( std::declval<T>()(std::declval<P>()) ), bool>::value
>> : std::true_type { };
// use enable_if to invoke the Packet&& version of the sniff_loop handler if possible - otherwise fail to old behavior
template <class Functor, class Packet>
bool invoke_loop_cb(Functor& f, Packet& p, typename std::enable_if<accepts_type<Functor, Packet>::value, bool>::type* = 0) {
return f(std::move(p));
}
template <class Functor, class Packet>
bool invoke_loop_cb(Functor& f, Packet& p, typename std::enable_if<!accepts_type<Functor, Packet>::value && accepts_type<Functor, Packet&>::value, bool>::type* = 0) {
return f(p);
}
template <class Functor, class Packet>
bool invoke_loop_cb(Functor& f, Packet& p, typename std::enable_if<!accepts_type<Functor, Packet>::value && !accepts_type<Functor, Packet&>::value, bool>::type* = 0) {
return f(*p.pdu());
}
#endif
} // namespace Internals } // namespace Internals
} // namespace Tins } // namespace Tins
/** /**

View File

@@ -5,14 +5,14 @@
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are * modification, are permitted provided that the following conditions are
* met: * met:
* *
* * Redistributions of source code must retain the above copyright * * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer. * notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above * * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer * copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the * in the documentation and/or other materials provided with the
* distribution. * distribution.
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
@@ -50,11 +50,11 @@ namespace Tins {
/** /**
* \class BaseSniffer * \class BaseSniffer
* \brief Base class for sniffers. * \brief Base class for sniffers.
* *
* This class implements the basic sniffing operations. Subclasses * This class implements the basic sniffing operations. Subclasses
* should only initialize this object using a pcap_t pointer, which * should only initialize this object using a pcap_t pointer, which
* will be used to extract packets. * will be used to extract packets.
* *
* Initialization must be done using the BaseSniffer::init method. * Initialization must be done using the BaseSniffer::init method.
*/ */
class BaseSniffer { class BaseSniffer {
@@ -69,17 +69,17 @@ namespace Tins {
* \brief Move constructor. * \brief Move constructor.
* This constructor is available only in C++11. * This constructor is available only in C++11.
*/ */
BaseSniffer(BaseSniffer &&rhs) TINS_NOEXCEPT BaseSniffer(BaseSniffer &&rhs) TINS_NOEXCEPT
: handle(nullptr), mask() : handle(nullptr), mask()
{ {
*this = std::move(rhs); *this = std::move(rhs);
} }
/** /**
* \brief Move assignment operator. * \brief Move assignment operator.
* This operator is available only in C++11. * This operator is available only in C++11.
*/ */
BaseSniffer& operator=(BaseSniffer &&rhs) TINS_NOEXCEPT BaseSniffer& operator=(BaseSniffer &&rhs) TINS_NOEXCEPT
{ {
using std::swap; using std::swap;
swap(handle, rhs.handle); swap(handle, rhs.handle);
@@ -87,99 +87,102 @@ namespace Tins {
return *this; return *this;
} }
#endif #endif
/** /**
* \brief Sniffer destructor. * \brief Sniffer destructor.
* This frees all memory used by the pcap handle. * This frees all memory used by the pcap handle.
*/ */
virtual ~BaseSniffer(); virtual ~BaseSniffer();
/** /**
* \brief Compiles a filter and uses it to capture one packet. * \brief Compiles a filter and uses it to capture one packet.
* *
* This method returns the first valid sniffed packet that matches the * This method returns the first valid sniffed packet that matches the
* sniffer's filter, or the first sniffed packet if no filter has * sniffer's filter, or the first sniffed packet if no filter has
* been set. * been set.
* *
* The return type is a thin wrapper over a PDU* and a Timestamp * The return type is a thin wrapper over a PDU* and a Timestamp
* object. This wrapper can be both implicitly converted to a * object. This wrapper can be both implicitly converted to a
* PDU* and a Packet object. So doing this: * PDU* and a Packet object. So doing this:
* *
* \code * \code
* Sniffer s(...); * Sniffer s(...);
* std::unique_ptr<PDU> pdu(s.next_packet()); * std::unique_ptr<PDU> pdu(s.next_packet());
* // Packet takes care of the PDU*. * // Packet takes care of the PDU*.
* Packet packet(s.next_packet()); * Packet packet(s.next_packet());
* \endcode * \endcode
* *
* Is fine, but this: * Is fine, but this:
* *
* \code * \code
* // bad!! * // bad!!
* PtrPacket p = s.next_packet(); * PtrPacket p = s.next_packet();
* \endcode * \endcode
* *
* Is not, since PtrPacket can't be copy constructed. * Is not, since PtrPacket can't be copy constructed.
* *
* \sa Packet::release_pdu * \sa Packet::release_pdu
* *
* \return A captured packet. If an error occured, PtrPacket::pdu * \return A captured packet. If an error occured, PtrPacket::pdu
* will return 0. Caller takes ownership of the PDU pointer stored in * will return 0. Caller takes ownership of the PDU pointer stored in
* the PtrPacket. * the PtrPacket.
*/ */
PtrPacket next_packet(); PtrPacket next_packet();
/** /**
* \brief Starts a sniffing loop, using a callback functor for every * \brief Starts a sniffing loop, using a callback functor for every
* sniffed packet. * sniffed packet.
* *
* The functor must implement an operator with one of the * The functor must implement an operator with one of the
* following signatures: * following signatures:
* *
* \code * \code
* bool(PDU&); * bool(PDU&);
* bool(const PDU&); * bool(const PDU&);
* bool(const Packet&);
* bool(Packet&&);
* bool(Packet);
* \endcode * \endcode
* *
* This functor will be called using the each of the sniffed packets * This functor will be called using the each of the sniffed packets
* as its argument. Using PDU member functions that modify the PDU, * as its argument. Using PDU member functions that modify the PDU,
* such as PDU::release_inner_pdu, is perfectly valid. * such as PDU::release_inner_pdu, is perfectly valid.
* *
* Note that if you're using a functor object, it will be copied using * Note that if you're using a functor object, it will be copied using
* its copy constructor, so it should be some kind of proxy to * its copy constructor, so it should be some kind of proxy to
* another object which will process the packets(e.g. std::bind). * another object which will process the packets(e.g. std::bind).
* *
* Sniffing will stop when either max_packets are sniffed(if it is != 0), * Sniffing will stop when either max_packets are sniffed(if it is != 0),
* or when the functor returns false. * or when the functor returns false.
* *
* This method catches both malformed_packet and pdu_not_found exceptions, * This method catches both malformed_packet and pdu_not_found exceptions,
* which allows writing much cleaner code, since you can call PDU::rfind_pdu * which allows writing much cleaner code, since you can call PDU::rfind_pdu
* without worrying about catching the exception that can be thrown. This * without worrying about catching the exception that can be thrown. This
* allows writing code such as the following: * allows writing code such as the following:
* *
* \code * \code
* bool callback(const PDU& pdu) { * bool callback(const PDU& pdu) {
* // If either RawPDU is not found, or construction of the DNS * // If either RawPDU is not found, or construction of the DNS
* // object fails, the BaseSniffer object will trap the exceptions, * // object fails, the BaseSniffer object will trap the exceptions,
* // so we don't need to worry about it. * // so we don't need to worry about it.
* DNS dns = pdu.rfind_pdu<RawPDU>().to<DNS>(); * DNS dns = pdu.rfind_pdu<RawPDU>().to<DNS>();
* return true; * return true;
* } * }
* \endcode * \endcode
* *
* \param function The callback handler object which should process packets. * \param function The callback handler object which should process packets.
* \param max_packets The maximum amount of packets to sniff. 0 == infinite. * \param max_packets The maximum amount of packets to sniff. 0 == infinite.
*/ */
template<class Functor> template<class Functor>
void sniff_loop(Functor function, uint32_t max_packets = 0); void sniff_loop(Functor function, uint32_t max_packets = 0);
/** /**
* \brief Sets a filter on this sniffer. * \brief Sets a filter on this sniffer.
* \param filter The filter to be set. * \param filter The filter to be set.
* \return True iif it was possible to apply the filter. * \return True iif it was possible to apply the filter.
*/ */
bool set_filter(const std::string &filter); bool set_filter(const std::string &filter);
/** /**
* \brief Stops sniffing loops. * \brief Stops sniffing loops.
* *
@@ -206,13 +209,13 @@ namespace Tins {
* *
* By default, packets will be parsed starting from link layer. * By default, packets will be parsed starting from link layer.
* However, if you're parsing a lot of traffic, then you might * However, if you're parsing a lot of traffic, then you might
* want to extract packets and push them into a queue, * want to extract packets and push them into a queue,
* so a consumer can parse them when they're popped. * so a consumer can parse them when they're popped.
* *
* This method allows doing that. If the parameter is true, * This method allows doing that. If the parameter is true,
* then packets taken from this BaseSniffer will only contain * then packets taken from this BaseSniffer will only contain
* a RawPDU which will have to entire contents of the packet. * a RawPDU which will have to entire contents of the packet.
* *
* \param value Whether to extract RawPDUs or not. * \param value Whether to extract RawPDUs or not.
*/ */
void set_extract_raw_pdus(bool value); void set_extract_raw_pdus(bool value);
@@ -258,13 +261,13 @@ namespace Tins {
private: private:
BaseSniffer(const BaseSniffer&); BaseSniffer(const BaseSniffer&);
BaseSniffer &operator=(const BaseSniffer&); BaseSniffer &operator=(const BaseSniffer&);
pcap_t *handle; pcap_t *handle;
bpf_u_int32 mask; bpf_u_int32 mask;
bool extract_raw; bool extract_raw;
}; };
/** /**
* \class Sniffer * \class Sniffer
* \brief Sniffs packets from a network interface. * \brief Sniffs packets from a network interface.
*/ */
@@ -288,7 +291,7 @@ namespace Tins {
* when constructing a Sniffer object. * when constructing a Sniffer object.
* *
* \sa SnifferConfiguration * \sa SnifferConfiguration
* *
* \param device The device which will be sniffed. * \param device The device which will be sniffed.
* \param configuration The configuration object to use to setup the sniffer. * \param configuration The configuration object to use to setup the sniffer.
*/ */
@@ -300,7 +303,7 @@ namespace Tins {
* By default the interface won't be put into promiscuous mode, and won't * By default the interface won't be put into promiscuous mode, and won't
* be put into monitor mode. * be put into monitor mode.
* *
* \deprecated Use the Sniffer(const std::string&, const SnifferConfiguration&) * \deprecated Use the Sniffer(const std::string&, const SnifferConfiguration&)
* constructor. * constructor.
* \param device The device which will be sniffed. * \param device The device which will be sniffed.
* \param max_packet_size The maximum packet size to be read. * \param max_packet_size The maximum packet size to be read.
@@ -317,14 +320,14 @@ namespace Tins {
* The maximum capture size is set to 65535. By default the interface won't * The maximum capture size is set to 65535. By default the interface won't
* be put into promiscuous mode, and won't be put into monitor mode. * be put into promiscuous mode, and won't be put into monitor mode.
* *
* \deprecated Use the Sniffer(const std::string&, const SnifferConfiguration&) * \deprecated Use the Sniffer(const std::string&, const SnifferConfiguration&)
* constructor. * constructor.
* \param device The device which will be sniffed. * \param device The device which will be sniffed.
* \param promisc Indicates if the interface should be put in promiscuous mode. * \param promisc Indicates if the interface should be put in promiscuous mode.
* \param filter A capture filter to be used on the sniffing session.(optional); * \param filter A capture filter to be used on the sniffing session.(optional);
* \param rfmon Indicates if the interface should be put in monitor mode.(optional); * \param rfmon Indicates if the interface should be put in monitor mode.(optional);
*/ */
Sniffer(const std::string &device, promisc_type promisc = NON_PROMISC, Sniffer(const std::string &device, promisc_type promisc = NON_PROMISC,
const std::string &filter = "", bool rfmon = false); const std::string &filter = "", bool rfmon = false);
private: private:
@@ -338,11 +341,11 @@ namespace Tins {
void set_rfmon(bool rfmon_enabled); void set_rfmon(bool rfmon_enabled);
}; };
/** /**
* \class FileSniffer * \class FileSniffer
* \brief Reads pcap files and interprets the packets in it. * \brief Reads pcap files and interprets the packets in it.
* *
* This class acts exactly in the same way that Sniffer, but reads * This class acts exactly in the same way that Sniffer, but reads
* packets from a pcap file instead of an interface. * packets from a pcap file instead of an interface.
*/ */
@@ -364,16 +367,16 @@ namespace Tins {
*/ */
FileSniffer(const std::string &file_name, const std::string &filter = ""); FileSniffer(const std::string &file_name, const std::string &filter = "");
}; };
template<class T> template<class T>
class HandlerProxy { class HandlerProxy {
public: public:
typedef T* ptr_type; typedef T* ptr_type;
typedef bool (T::*fun_type)(PDU&) ; typedef bool (T::*fun_type)(PDU&) ;
HandlerProxy(ptr_type ptr, fun_type function) HandlerProxy(ptr_type ptr, fun_type function)
: object(ptr), fun(function) {} : object(ptr), fun(function) {}
bool operator()(PDU &pdu) { bool operator()(PDU &pdu) {
return (object->*fun)(pdu); return (object->*fun)(pdu);
} }
@@ -381,9 +384,9 @@ namespace Tins {
ptr_type object; ptr_type object;
fun_type fun; fun_type fun;
}; };
template<class T> template<class T>
HandlerProxy<T> make_sniffer_handler(T *ptr, typename HandlerProxy<T>::fun_type function) HandlerProxy<T> make_sniffer_handler(T *ptr, typename HandlerProxy<T>::fun_type function)
{ {
return HandlerProxy<T>(ptr, function); return HandlerProxy<T>(ptr, function);
} }
@@ -391,7 +394,7 @@ namespace Tins {
/** /**
* \brief Iterates over packets sniffed by a BaseSniffer. * \brief Iterates over packets sniffed by a BaseSniffer.
*/ */
class SnifferIterator : public std::iterator<std::forward_iterator_tag, PDU> { class SnifferIterator : public std::iterator<std::forward_iterator_tag, Packet> {
public: public:
/** /**
* Constructs a SnifferIterator. * Constructs a SnifferIterator.
@@ -425,15 +428,15 @@ namespace Tins {
* Dereferences the iterator. * Dereferences the iterator.
* \return reference to the current packet. * \return reference to the current packet.
*/ */
PDU &operator*() { Packet &operator*() {
return *pkt.pdu(); return pkt;
} }
/** /**
* Dereferences the iterator. * Dereferences the iterator.
* \return pointer to the current packet. * \return pointer to the current packet.
*/ */
PDU *operator->() { Packet *operator->() {
return &(**this); return &(**this);
} }
@@ -463,15 +466,15 @@ namespace Tins {
Packet pkt; Packet pkt;
}; };
/** /**
* \class SnifferConfiguration * \class SnifferConfiguration
* \brief Represents the configuration of a BaseSniffer object. * \brief Represents the configuration of a BaseSniffer object.
* *
* This class can be used as an easy way to configure a Sniffer * This class can be used as an easy way to configure a Sniffer
* or FileSniffer object. * or FileSniffer object.
* *
* It can be used by constructing an object of this type, * It can be used by constructing an object of this type,
* setting the desired values and then passing it to the * setting the desired values and then passing it to the
* Sniffer or FileSniffer object's constructor. This sets * Sniffer or FileSniffer object's constructor. This sets
* default values for some attributes: * default values for some attributes:
* *
@@ -481,8 +484,8 @@ namespace Tins {
* *
* For any of the attributes not listed above, the associated * For any of the attributes not listed above, the associated
* pcap function which is used to set them on a pcap handle * pcap function which is used to set them on a pcap handle
* won't be called at all. * won't be called at all.
* *
* This class can be used to configure a Sniffer object, * This class can be used to configure a Sniffer object,
* like this: * like this:
* *
@@ -491,7 +494,7 @@ namespace Tins {
* SnifferConfiguration config; * SnifferConfiguration config;
* config.set_filter("ip and port 80"); * config.set_filter("ip and port 80");
* config.set_promisc_mode(true); * config.set_promisc_mode(true);
* *
* // Use it on a Sniffer object. * // Use it on a Sniffer object.
* Sniffer sniffer("eth0", config); * Sniffer sniffer("eth0", config);
* \endcode * \endcode
@@ -578,8 +581,13 @@ namespace Tins {
for(iterator it = begin(); it != end(); ++it) { for(iterator it = begin(); it != end(); ++it) {
try { try {
// If the functor returns false, we're done // If the functor returns false, we're done
if(!function(*it)) #if TINS_IS_CXX11
if (!Tins::Internals::invoke_loop_cb(function, *it))
return; return;
#else
if(!function(*it->pdu()))
return;
#endif
} }
catch(malformed_packet&) { } catch(malformed_packet&) { }
catch(pdu_not_found&) { } catch(pdu_not_found&) { }
@@ -588,5 +596,5 @@ namespace Tins {
} }
} }
} }
#endif // TINS_SNIFFER_H #endif // TINS_SNIFFER_H