/* * Copyright (c) 2017, Matias Fontanini * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ #ifndef TINS_SNIFFER_H #define TINS_SNIFFER_H #include #include #include #include #include #include #include #include #include #ifdef TINS_HAVE_PCAP #include namespace Tins { class SnifferIterator; class SnifferConfiguration; /** * \class BaseSniffer * \brief Base class for sniffers. * * This class implements the basic sniffing operations. Subclasses * should only initialize this object using a pcap_t pointer, which * will be used to extract packets. * * Initialization must be done using the BaseSniffer::init method. */ class TINS_API BaseSniffer { public: /** * The iterator type. */ typedef SnifferIterator iterator; #if TINS_IS_CXX11 /** * \brief Move constructor. * This constructor is available only in C++11. */ BaseSniffer(BaseSniffer &&rhs) TINS_NOEXCEPT : handle_(0), mask_(), extract_raw_(false), pcap_sniffing_method_(pcap_loop) { *this = std::move(rhs); } /** * \brief Move assignment operator. * This operator is available only in C++11. */ BaseSniffer& operator=(BaseSniffer &&rhs) TINS_NOEXCEPT { using std::swap; swap(handle_, rhs.handle_); swap(mask_, rhs.mask_); swap(extract_raw_, rhs.extract_raw_); swap(pcap_sniffing_method_, rhs.pcap_sniffing_method_); return* this; } #endif /** * \brief Sniffer destructor. * This frees all memory used by the pcap handle. */ virtual ~BaseSniffer(); /** * \brief Compiles a filter and uses it to capture one packet. * * This method returns the first valid sniffed packet that matches the * sniffer's filter, or the first sniffed packet if no filter has * been set. * * The return type is a thin wrapper over a PDU* and a Timestamp * object. This wrapper can be both implicitly converted to a * PDU* and a Packet object. So doing this: * * \code * Sniffer s(...); * std::unique_ptr pdu(s.next_packet()); * // Packet takes care of the PDU*. * Packet packet(s.next_packet()); * \endcode * * Is fine, but this: * * \code * // bad!! * PtrPacket p = s.next_packet(); * \endcode * * Is not, since PtrPacket can't be copy constructed. * * \sa Packet::release_pdu * * \return A captured packet. If an error occured, PtrPacket::pdu * will return 0. Caller takes ownership of the PDU pointer stored in * the PtrPacket. */ PtrPacket next_packet(); /** * \brief Starts a sniffing loop, using a callback functor for every * sniffed packet. * * The functor must implement an operator with one of the * following signatures: * * \code * bool(PDU&); * bool(const PDU&); * * // These two are only allowed when compiling in C++11 mode * bool(Packet&); * bool(const Packet&); * \endcode * * This functor will be called using the each of the sniffed packets * as its argument. Using PDU member functions that modify the PDU, * such as PDU::release_inner_pdu, is perfectly valid. * * 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 * another object which will process the packets(e.g. std::bind). * * Sniffing will stop when either max_packets are sniffed(if it is != 0), * or when the functor returns false. * * Note that the pcap handle stored in a BaseSniffer will always be the * same. This means that if you start sniffing using sniff_loop, then stop * and at some point in the future you call sniff_loop again, you will keep * iterating over the same handle. If the handle points to a pcap file, then * you will continue processing packets from it. If the handle points to * a network device, you will keep sniffing from it. * * This method catches both malformed_packet and pdu_not_found exceptions, * which allows writing much cleaner code, since you can call PDU::rfind_pdu * without worrying about catching the exception that can be thrown. This * allows writing code such as the following: * * \code * bool callback(const PDU& pdu) { * // If either RawPDU is not found, or construction of the DNS * // object fails, the BaseSniffer object will trap the exceptions, * // so we don't need to worry about it. * DNS dns = pdu.rfind_pdu().to(); * return true; * } * \endcode * * \param function The callback handler object which should process packets. * \param max_packets The maximum amount of packets to sniff. 0 == infinite. */ template void sniff_loop(Functor function, uint32_t max_packets = 0); /** * \brief Sets a filter on this sniffer. * \param filter The filter to be set. * \return True iif it was possible to apply the filter. */ bool set_filter(const std::string& filter); /** * \brief Stops sniffing loops. * * This method must be called from the same thread from which * BaseSniffer::sniff_loop was called. */ void stop_sniff(); /** * \brief Gets the file descriptor associated with the sniffer. */ int get_fd(); /** * \brief Sets direction for the sniffer. * * This calls pcap_setdirection using the provided parameter. * \param d The direction for the sniffer. */ bool set_direction(pcap_direction_t d); /** * \brief Sets the read timeout for this sniffer. * * This calls pcap_set_timeout using the provided parameter. * \param ms The amount of milliseconds. */ void set_timeout(int ms); /** * \brief Sets whether to extract RawPDUs or fully parsed packets. * * By default, packets will be parsed starting from link layer. * However, if you're parsing a lot of traffic, then you might * want to extract packets and push them into a queue, * so a consumer can parse them when they're popped. * * This method allows doing that. If the parameter is true, * then packets taken from this BaseSniffer will only contain * a RawPDU which will have to entire contents of the packet. * * \param value Whether to extract RawPDUs or not. */ void set_extract_raw_pdus(bool value); /** * \brief function pointer for the sniffing method * * By default, libtins uses `pcap_loop` to sniff packets. With * `set_pcap_sniffing_method` it is possible to specify an alternative * sniffing method, for example `pcap_dispatch`, or a custom function. * This function pointer has the same interface as `pcap_loop` and * `pcap_dispatch`. * * \sa set_pcap_sniffing_method */ typedef int(*PcapSniffingMethod)(pcap_t*, int, pcap_handler, u_char*); /** * \brief set sniffing method to either pcap_loop or pcap_dispatch. * * By default, packets are sniffed with `pcap_loop`, which only returns if * a packet is received, thus ignoring timeout expiration, if any is set. * With this method it is possible to pass an alternative sniffer function, * e.g. `pcap_dispatch`, that honors timeouts, or a custom function with * the same signature. * * See the relevant manual pages for pcap_loop and pcap_dispatch for more * information on their behavior * * \sa PcapSniffingMethod */ void set_pcap_sniffing_method(PcapSniffingMethod method); /** * \brief Retrieves this sniffer's link type. * * This calls pcap_datalink on the stored pcap handle and * returns its result. */ int link_type() const; /** * Retrieves an iterator to the next packet in this sniffer. */ iterator begin(); /** * Retrieves an end iterator. */ iterator end(); /** * Retrieves the pcap handle used by this sniffer. */ pcap_t* get_pcap_handle(); /** * Retrieves the pcap handle used by this sniffer. */ const pcap_t* get_pcap_handle() const; protected: /** * Default constructor. */ BaseSniffer(); void set_pcap_handle(pcap_t* pcap_handle); void set_if_mask(bpf_u_int32 if_mask); bpf_u_int32 get_if_mask() const; private: BaseSniffer(const BaseSniffer&); BaseSniffer& operator=(const BaseSniffer&); pcap_t* handle_; bpf_u_int32 mask_; bool extract_raw_; PcapSniffingMethod pcap_sniffing_method_; }; /** * \class Sniffer * \brief Sniffs packets from a network interface. */ class TINS_API Sniffer : public BaseSniffer { public: /** * \deprecated This enum is no longer necessary. You should use the * Sniffer(const std::string&, const SnifferConfiguration&) constructor. */ enum promisc_type { NON_PROMISC, PROMISC }; /** * \brief Constructs an instance of Sniffer * * \param device The device from which to capture packets */ Sniffer(const std::string& device); /** * \brief Constructs an instance of Sniffer using the provided configuration. * * Use the SnifferConfiguration object to specify how you want to configure * the properties of this sniffer * * \sa SnifferConfiguration * * \param device The device which will be sniffed. * \param configuration The configuration object to use to setup the sniffer. */ Sniffer(const std::string& device, const SnifferConfiguration& configuration); /** * \brief Constructs an instance of Sniffer. * * By default the interface won't be put into promiscuous mode, and won't * be put into monitor mode. * * \deprecated Use the Sniffer(const std::string&, const SnifferConfiguration&) * constructor. * \param device The device which will be sniffed. * \param max_packet_size The maximum packet size to be read. * \param promisc bool indicating whether to put the interface in promiscuous mode.(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); */ TINS_DEPRECATED(Sniffer(const std::string& device, unsigned max_packet_size, bool promisc = false, const std::string& filter = "", bool rfmon = false)); /** * \brief Constructs an instance of Sniffer. * * 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. * * \deprecated Use the Sniffer(const std::string&, const SnifferConfiguration&) * constructor. * \param device The device which will be sniffed. * \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 rfmon Indicates if the interface should be put in monitor mode.(optional); */ TINS_DEPRECATED(Sniffer(const std::string& device, promisc_type promisc, const std::string& filter = "", bool rfmon = false)); private: friend class SnifferConfiguration; void init(const std::string& device, const SnifferConfiguration& configuration); void set_snap_len(unsigned snap_len); void set_buffer_size(unsigned buffer_size); void set_promisc_mode(bool promisc_enabled); void set_rfmon(bool rfmon_enabled); void set_immediate_mode(bool enabled); void set_timestamp_precision(int value); }; /** * \class FileSniffer * \brief Reads pcap files and interprets the packets in it. * * This class acts exactly in the same way that Sniffer, but reads * packets from a pcap file instead of an interface. */ class TINS_API FileSniffer : public BaseSniffer { public: /** * \brief Constructs an instance of FileSniffer. * \param fp The pcap file which will be parsed. * \param configuration A SnifferConfiguration to be used on the file. */ FileSniffer(FILE *fp, const SnifferConfiguration& configuration); /** * \brief Constructs an instance of FileSniffer. * \param file_name The pcap file which will be parsed. * \param configuration A SnifferConfiguration to be used on the file. */ FileSniffer(const std::string& file_name, const SnifferConfiguration& configuration); /** * \deprecated Use the constructor that takes a SnifferConfiguration instead. * * \brief Constructs an instance of FileSniffer. * \param file_name The pcap file which will be parsed. * \param filter A capture filter to be used on the file. (optional) */ FileSniffer(const std::string& file_name, const std::string& filter = ""); /** * \deprecated Use the constructor that takes a SnifferConfiguration instead. * * \brief Constructs an instance of FileSniffer. * \param fp The pcap file which will be parsed. * \param filter A capture filter to be used on the file. (optional) */ FileSniffer(FILE *fp, const std::string& filter = ""); }; template class HandlerProxy { public: typedef T* ptr_type; typedef bool (T::*fun_type)(PDU&) ; HandlerProxy(ptr_type ptr, fun_type function) : object_(ptr), fun_(function) {} bool operator()(PDU& pdu) { return (object_->*fun_)(pdu); } private: ptr_type object_; fun_type fun_; }; template HandlerProxy make_sniffer_handler(T* ptr, typename HandlerProxy::fun_type function) { return HandlerProxy(ptr, function); } /** * \brief Iterates over packets sniffed by a BaseSniffer. */ class SnifferIterator { public: typedef std::forward_iterator_tag iterator_category; typedef Packet value_type; typedef std::ptrdiff_t difference_type; typedef Packet* pointer; typedef Packet& reference; /** * Constructs a SnifferIterator. * \param sniffer The sniffer to iterate. */ SnifferIterator(BaseSniffer* sniffer = 0) : sniffer_(sniffer) { if (sniffer_) { advance(); } } /** * Advances the iterator. */ SnifferIterator& operator++() { advance(); return* this; } /** * Advances the iterator. */ SnifferIterator operator++(int) { SnifferIterator other(*this); advance(); return other; } /** * Dereferences the iterator. * \return reference to the current packet. */ Packet& operator*() { return pkt_; } /** * Dereferences the iterator. * \return pointer to the current packet. */ Packet* operator->() { return &(**this); } /** * Compares this iterator for equality. * \param rhs The iterator to be compared to. */ bool operator==(const SnifferIterator& rhs) const { return sniffer_ == rhs.sniffer_; } /** * Compares this iterator for in-equality. * \param rhs The iterator to be compared to. */ bool operator!=(const SnifferIterator& rhs) const { return !(*this == rhs); } private: void advance() { pkt_ = sniffer_->next_packet(); if (!pkt_) { sniffer_ = 0; } } BaseSniffer* sniffer_; Packet pkt_; }; /** * \class SnifferConfiguration * \brief Represents the configuration of a BaseSniffer object. * * This class can be used as an easy way to configure a Sniffer * or FileSniffer object. * * It can be used by constructing an object of this type, * setting the desired values and then passing it to the * Sniffer or FileSniffer object's constructor. This sets * default values for some attributes: * * - Snapshot length: 65535 bytes (64 KB). * - Timeout: 1000 milliseconds. * - Promiscuous mode: false. * * For any of the attributes not listed above, the associated * pcap function which is used to set them on a pcap handle * won't be called at all. * * This class can be used to configure a Sniffer object, * like this: * * \code * // Initialize the configuration. * SnifferConfiguration config; * config.set_filter("ip and port 80"); * config.set_promisc_mode(true); * * // Use it on a Sniffer object. * Sniffer sniffer("eth0", config); * \endcode */ class TINS_API SnifferConfiguration { public: /** * \brief The default snapshot length. * * This is 65535 by default. */ static const unsigned DEFAULT_SNAP_LEN; /** * \brief The default timeout. * * This is 1000 by default. */ static const unsigned DEFAULT_TIMEOUT; /** * Default constructs a SnifferConfiguration. */ SnifferConfiguration(); /** * Sets the snapshot length option. * \param snap_len The snapshot length to be set. */ void set_snap_len(unsigned snap_len); /** * Sets the buffer size option. * \param buffer_size The buffer size to be set. */ void set_buffer_size(unsigned buffer_size); /** * Sets the promiscuous mode option. * \param enabled The promiscuous mode value. */ void set_promisc_mode(bool enabled); /** * Sets a pcap filter to use on the sniffer. * \param filter The pcap filter to be used. */ void set_filter(const std::string& filter); /** * Sets the pcap sniffing method to use. * \param method The sniffing method to be used. */ void set_pcap_sniffing_method(BaseSniffer::PcapSniffingMethod method); /** * Sets the rfmon option. * \param enabled The rfmon option value. */ void set_rfmon(bool enabled); /** * Sets the timeout option. * \param timeout The timeout to be set. */ void set_timeout(unsigned timeout); /** * Sets the direction option. * \param direction The direction to be set. */ void set_direction(pcap_direction_t direction); /** * Sets the immediate mode option. * \param enabled The immediate mode option value. */ void set_immediate_mode(bool enabled); /** * Sets the timestamp precision value * \param value The timestamp option value. */ void set_timestamp_precision(int value); protected: friend class Sniffer; friend class FileSniffer; enum Flags { BUFFER_SIZE = 1, PROMISCUOUS = 2, RFMON = 4, PACKET_FILTER = 8, IMMEDIATE_MODE = 16, DIRECTION = 32, TIMESTAMP_PRECISION = 64, PCAP_SNIFFING_METHOD = 128, }; void configure_sniffer_pre_activation(Sniffer& sniffer) const; void configure_sniffer_pre_activation(FileSniffer& sniffer) const; void configure_sniffer_post_activation(Sniffer& sniffer) const; uint32_t flags_; unsigned snap_len_; unsigned buffer_size_; std::string filter_; BaseSniffer::PcapSniffingMethod pcap_sniffing_method_; unsigned timeout_; bool promisc_; bool rfmon_; bool immediate_mode_; pcap_direction_t direction_; int timestamp_precision_; }; template 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; } } } } // Tins #endif // TINS_HAVE_PCAP #endif // TINS_SNIFFER_H