/* * Copyright (c) 2014, 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 "pdu.h" #include "packet.h" #include "cxxstd.h" #include "exceptions.h" #include "internals.h" namespace Tins { class SnifferIterator; /** * \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 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) NOEXCEPT : handle(nullptr), mask() { *this = std::move(rhs); } /** * \brief Move assignment operator. * This operator is available only in C++11. */ BaseSniffer& operator=(BaseSniffer &&rhs) NOEXCEPT { using std::swap; swap(handle, rhs.handle); swap(mask, rhs.mask); 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&); * \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. * * 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 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 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(); protected: /** * Default constructor. */ BaseSniffer(); /** * \brief Initialices this BaseSniffer. * * \param phandle The pcap handle to be used for sniffing. * \param filter The pcap filter which will be applied to the * stream. * \param if_mask The interface's subnet mask. If 0 is provided, * then some IP broadcast tests won't work correctly. */ void init(pcap_t *phandle, const std::string &filter, bpf_u_int32 if_mask); private: BaseSniffer(const BaseSniffer&); BaseSniffer &operator=(const BaseSniffer&); pcap_t *handle; bpf_u_int32 mask; }; /** * \class Sniffer * \brief Sniffs packets from a network interface. */ class Sniffer : public BaseSniffer { public: enum promisc_type { NON_PROMISC, PROMISC }; /** * Constructs an instance of Sniffer. * * By default the interface won't be put into promiscuous mode, and won't * be put into monitor mode. * * \param device The device which will be sniffed. * \param max_packet_size The maximum packet size to be read. * \param promisc bool indicating wether 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); */ 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. * * \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); */ Sniffer(const std::string &device, promisc_type promisc = NON_PROMISC, const std::string &filter = "", bool rfmon = false); private: void init_sniffer(const std::string &device, unsigned max_packet_size, bool promisc = false, const std::string &filter = "", bool rfmon = false); }; /** * \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 FileSniffer : public BaseSniffer { public: /** * \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 = ""); }; 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 std::iterator { public: /** * 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. */ PDU &operator*() { return *pkt.pdu(); } /** * Dereferences the iterator. * \return pointer to the current packet. */ PDU *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; }; 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(!function(*it)) return; } catch(malformed_packet&) { } catch(pdu_not_found&) { } if(max_packets && --max_packets == 0) return; } } } #endif // TINS_SNIFFER_H