From 18750fe18a65204d67af785f68fbdee968541155 Mon Sep 17 00:00:00 2001 From: Matias Fontanini Date: Thu, 18 Aug 2011 20:36:55 -0300 Subject: [PATCH] Added Sniffer class. Added a constructor to eery PDU subclass which creates an instance of the PDU from a byte array. --- include/arp.h | 12 +++- include/dhcp.h | 25 +++++++- include/ethernetII.h | 21 ++++++- include/icmp.h | 12 +++- include/ip.h | 10 +++- include/pdu.h | 4 +- include/rawpdu.h | 17 ++++-- include/sniffer.h | 111 ++++++++++++++++++++++++++++++++++++ include/tcp.h | 20 ++++++- include/udp.h | 11 +++- include/utils.h | 2 +- src/arp.cpp | 132 +++++++++++++++++++++++-------------------- src/dhcp.cpp | 1 - src/ethernetII.cpp | 25 +++++++- src/icmp.cpp | 21 +++++-- src/ip.cpp | 31 +++++++++- src/pdu.cpp | 5 +- src/rawpdu.cpp | 12 ++-- src/sniffer.cpp | 64 +++++++++++++++++++++ src/tcp.cpp | 45 +++++++++++++++ src/udp.cpp | 14 ++++- 21 files changed, 492 insertions(+), 103 deletions(-) create mode 100644 include/sniffer.h create mode 100644 src/sniffer.cpp diff --git a/include/arp.h b/include/arp.h index c7e3386..9a27dfc 100644 --- a/include/arp.h +++ b/include/arp.h @@ -52,6 +52,14 @@ namespace Tins { */ ARP(uint32_t target_ip = 0, uint32_t sender_ip = 0, const uint8_t *target_hw = 0, const uint8_t *sender_hw = 0); + /** + * \brief Constructor which creates an TCP object from a buffer and adds all identifiable + * PDUs found in the buffer as children of this one. + * \param buffer The buffer from which this PDU will be constructed. + * \param total_sz The total size of the buffer. + */ + ARP(const uint8_t *buffer, uint32_t total_sz); + /* Getters */ /** * \brief Getter for the sender's hardware address. @@ -305,7 +313,7 @@ namespace Tins { * \return The cloned PDU. * \sa PDU::clone_packet */ - PDU *clone_packet(uint8_t *ptr, uint32_t total_sz); + PDU *clone_packet(const uint8_t *ptr, uint32_t total_sz); private: struct arphdr { @@ -325,7 +333,7 @@ namespace Tins { * * \param arp_ptr The pointer to the arphdr. */ - ARP(arphdr *arp_ptr); + ARP(const arphdr *arp_ptr); void write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *parent); diff --git a/include/dhcp.h b/include/dhcp.h index 3294d57..7239f12 100644 --- a/include/dhcp.h +++ b/include/dhcp.h @@ -131,9 +131,28 @@ namespace Tins { * \brief DHCP options struct. */ struct DHCPOption { - uint8_t option, length; + /** + * \brief The option number. + */ + uint8_t option; + /** + * \brief The value's length in bytes. + */ + uint8_t length; + /** + * \brief The option's value. + */ uint8_t *value; - + + /** + * \brief Creates an instance of DHCPOption. + * + * The option's value is copied, therefore the user should + * manually free any memory pointed by the "val" parameter. + * \param opt The option number. + * \param len The length of the option's value in bytes. + * \param val The option's value. + */ DHCPOption(uint8_t opt, uint8_t len, const uint8_t *val); }; @@ -201,7 +220,7 @@ namespace Tins { /** * \brief Adds a domain name servers option. - * \param routers A list of ip addresses in integer notation. + * \param dns A list of ip addresses in integer notation. * \return True if the option was added successfully. \sa DHCP::add_option */ bool add_dns_options(const std::list &dns); diff --git a/include/ethernetII.h b/include/ethernetII.h index 285a06b..a5ae816 100644 --- a/include/ethernetII.h +++ b/include/ethernetII.h @@ -66,6 +66,14 @@ namespace Tins { */ EthernetII(uint32_t iface_index, const uint8_t* mac_dst = 0, const uint8_t* mac_src = 0, PDU* child = 0); + /** + * \brief Constructor which creates an EthernetII object from a buffer and adds all identifiable + * PDUs found in the buffer as children of this one. + * \param buffer The buffer from which this PDU will be constructed. + * \param total_sz The total size of the buffer. + */ + EthernetII(const uint8_t *buffer, uint32_t total_sz); + /* Getters */ /** * \brief Getter for the destination's mac address. @@ -88,7 +96,14 @@ namespace Tins { */ inline uint32_t iface() const { return this->_iface_index; } + /** + * \brief Getter for the payload_type + * \return The payload type. + */ + uint16_t payload_type() const; + /* Setters */ + /** * \brief Setter for the destination's MAC. * @@ -177,11 +192,11 @@ namespace Tins { */ EthernetII(ethhdr *eth_ptr); + void write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *parent); + + ethhdr _eth; uint32_t _iface_index; - - void write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *parent); - }; }; diff --git a/include/icmp.h b/include/icmp.h index 379c507..e63dcf6 100644 --- a/include/icmp.h +++ b/include/icmp.h @@ -57,6 +57,14 @@ namespace Tins { */ ICMP(Flags flag = ECHO_REQUEST); + /** + * \brief Constructor which creates an ICMP object from a buffer and adds all identifiable + * PDUs found in the buffer as children of this one. + * \param buffer The buffer from which this PDU will be constructed. + * \param total_sz The total size of the buffer. + */ + ICMP(const uint8_t *buffer, uint32_t total_sz); + /** * \brief Sets the code field. * @@ -273,7 +281,7 @@ namespace Tins { * \return The cloned PDU. * \sa PDU::clone_packet */ - PDU *clone_packet(uint8_t *ptr, uint32_t total_sz); + PDU *clone_packet(const uint8_t *ptr, uint32_t total_sz); private: static uint16_t global_id, global_seq; @@ -298,7 +306,7 @@ namespace Tins { * * \param ptr The icmphdr to clone. */ - ICMP(icmphdr *ptr); + ICMP(const icmphdr *ptr); /** \brief Serialices this ICMP PDU. * \param buffer The buffer in which the PDU will be serialized. diff --git a/include/ip.h b/include/ip.h index 26ac3a5..606c710 100644 --- a/include/ip.h +++ b/include/ip.h @@ -100,6 +100,14 @@ namespace Tins { */ IP(uint32_t ip_dst = 0, uint32_t ip_src = 0, PDU *child = 0); + /** + * \brief Constructor which creates an IP object from a buffer and adds all identifiable + * PDUs found in the buffer as children of this one. + * \param buffer The buffer from which this PDU will be constructed. + * \param total_sz The total size of the buffer. + */ + IP(const uint8_t *buffer, uint32_t total_sz); + /** * \brief Destructor for IP objects. * @@ -340,7 +348,7 @@ namespace Tins { * \return The cloned PDU. * \sa PDU::clone_packet */ - PDU *clone_packet(uint8_t *ptr, uint32_t total_sz); + PDU *clone_packet(const uint8_t *ptr, uint32_t total_sz); private: static const uint8_t DEFAULT_TTL; diff --git a/include/pdu.h b/include/pdu.h index 48420f0..078af65 100644 --- a/include/pdu.h +++ b/include/pdu.h @@ -164,7 +164,7 @@ namespace Tins { * \param total_sz The size of the buffer. * \return The cloned PDU. */ - virtual PDU *clone_packet(uint8_t *ptr, uint32_t total_sz) { return 0; } + virtual PDU *clone_packet(const uint8_t *ptr, uint32_t total_sz) { return 0; } protected: /** \brief Serializes this PDU and propagates this action to child PDUs. * @@ -181,7 +181,7 @@ namespace Tins { * \param total_sz The total size of the buffer. * \return Returns the cloned PDU. Will be 0 if cloning failed. */ - PDU *clone_inner_pdu(uint8_t *ptr, uint32_t total_sz); + PDU *clone_inner_pdu(const uint8_t *ptr, uint32_t total_sz); /** \brief Serializes this TCP PDU. * diff --git a/include/rawpdu.h b/include/rawpdu.h index c093542..5d4069e 100644 --- a/include/rawpdu.h +++ b/include/rawpdu.h @@ -37,14 +37,21 @@ namespace Tins { public: /** \brief Creates an instance of RawPDU. * - * The payload is not copied by default, therefore it must be - * manually freed by the user. If the payload was to be copied, - * then the copy flag must be set to true. + * The payload is copied, therefore the original payload's memory + * must be freed by the user. * \param pload The payload which the RawPDU will contain. * \param size The size of the payload. - * \param copy Flag indicating wether to copy the payload. */ - RawPDU(uint8_t *pload, uint32_t size, bool copy = false); + RawPDU(const uint8_t *pload, uint32_t size); + + /** \brief Creates an instance of RawPDU. + * + * The payload is not copied in this constructor, therefore + * it must be manually freed by the user. + * \param pload The payload which the RawPDU will contain. + * \param size The size of the payload. + */ + RawPDU(uint8_t *pload, uint32_t size); /** \brief RawPDU destructor. * diff --git a/include/sniffer.h b/include/sniffer.h new file mode 100644 index 0000000..6f9b3a4 --- /dev/null +++ b/include/sniffer.h @@ -0,0 +1,111 @@ +#ifndef __SNIFFER_H +#define __SNIFFER_H + + +#include +#include +#include "pdu.h" + +namespace Tins { + + /** + * \brief Abstract sniffed packet handler. + * + * Base class to handle sniffed packets when using Sniffer::sniff_loop. + * Users should either inherit this class, or use the template class + * SnifferHandler to provide their own handlers. + */ + class AbstractSnifferHandler { + public: + /** + * \brief AbstractSnifferHandler destructor. + */ + virtual ~AbstractSnifferHandler() { } + /** + * \brief Handle a captured PDU. + * \return Should return false if no more sniffing is required. + */ + virtual bool handle(PDU *pdu) = 0; + }; + + /** + * \brief Sniffer class can be used to sniff packets using filters. + */ + class Sniffer { + public: + /** + * \brief Creates an instance of sniffer. + * \param device The device which will be sniffed. + * \param max_packet_size The maximum packet size to be read. + */ + Sniffer(const std::string &device, unsigned max_packet_size); + + /** + * \brief Sniffer destructor. + * This frees all memory used by the pcap handle. + */ + ~Sniffer(); + + /** + * \brief Compiles a filter and uses it to capture one packet. + * + * This method should be used only when expecting few packets. + * It's innefficient since it recompiles the filter every time it + * is called. To reuse a filter and sniff more efficiently, use + * Sniffer::sniff_loop. + * \param filter The filter which will be used while sniffing. + * \return The captured packet, matching the given filter, 0 if an + * error occured(probably compiling the filter). + */ + PDU *next_pdu(const std::string &filter); + + /** + * \brief Starts a sniffing loop, using a callback object for every + * sniffed packet. + * + * Handlers could be user-provided classes which inherit AbstractSnifferHandler, + * or it could be a specific SnifferHandler specialization. This method deletes + * packets after they are handled, therefore the handlers MUST NOT delete them. + * \param filter The filter to use when sniffing. + * \param cback_handler The callback handler object which should process packets. + * \param max_packets The maximum amount of packets to sniff. 0 == infinite. + */ + void sniff_loop(const std::string &filter, AbstractSnifferHandler *cback_handler, uint32_t max_packets = 0); + private: + bool compile_set_filter(const std::string &filter, bpf_program &prog); + + static void callback_handler(u_char *args, const struct pcap_pkthdr *header, const u_char *packet); + + pcap_t *handle; + bpf_u_int32 ip, mask; + }; + + /** + * \brief Concrete implementation of AbstractSnifferHandler. + * + * This class is instantiated using a pointer to the actual handler. + * Every time a packet is sniffed, operator() (PDU*) will be called on + * the given pointer. \sa AbstractSnifferHandler + */ + template class SnifferHandler : public AbstractSnifferHandler { + public: + /** + * Creates an instance of SnifferHandler. + * \param ptr The pointer to the actual handler. + */ + SnifferHandler(T *ptr) : handler(ptr) { } + + /** + * \brief The overriden AbstractSnifferHandler::handle. + * \param pdu The sniffed PDU. + * \return False if no more sniffing is required, otherwise true. + */ + bool handle(PDU *pdu) { + return (*handler)(pdu); + } + private: + T *handler; + }; +}; + +#endif diff --git a/include/tcp.h b/include/tcp.h index 14dc375..05061d2 100644 --- a/include/tcp.h +++ b/include/tcp.h @@ -78,15 +78,21 @@ namespace Tins { * \param dport Destination port. * \param sport Source port. * */ - TCP(uint16_t dport = 0, uint16_t sport = 0); + /** + * \brief Constructor which creates an TCP object from a buffer and adds all identifiable + * PDUs found in the buffer as children of this one. + * \param buffer The buffer from which this PDU will be constructed. + * \param total_sz The total size of the buffer. + */ + TCP(const uint8_t *buffer, uint32_t total_sz); + /** * \brief TCP destructor. * * Destructs the TCP instance. Does not free the payload. * */ - ~TCP(); /** @@ -145,6 +151,16 @@ namespace Tins { */ inline uint8_t data_offset() const { return this->_tcp.doff; } + /** + * \brief Gets the value of a flag. + * + * \param tcp_flag The polled flag. + * \return The value of the flag. + */ + uint8_t get_flag(Flags tcp_flag); + + /* Setters */ + /** * \brief Setter for the destination port field. * diff --git a/include/udp.h b/include/udp.h index 477f582..e3baeaa 100644 --- a/include/udp.h +++ b/include/udp.h @@ -34,15 +34,24 @@ namespace Tins { */ class UDP : public PDU { public: - /** \brief UDP constructor. + /** + * \brief UDP constructor. * * Creates an instance of UDP. Destination and source port can * be provided, otherwise both will be 0. * \param dport Destination port. * \param sport Source port. + * \param child The child PDU(optional). * */ UDP(uint16_t dport = 0, uint16_t sport = 0, PDU *child = 0); + /** + * \brief Constructor which creates an UDP object from a buffer and adds all identifiable + * PDUs found in the buffer as children of this one. + * \param buffer The buffer from which this PDU will be constructed. + * \param total_sz The total size of the buffer. + */ + UDP(const uint8_t *buffer, uint32_t total_sz); /** \brief Returns the destination port */ diff --git a/include/utils.h b/include/utils.h index dc7866f..55b4318 100644 --- a/include/utils.h +++ b/include/utils.h @@ -118,7 +118,7 @@ namespace Tins { * * If the lookup fails, false will be returned, true otherwise. * \param iface The interface from which to extract the identifier. - * \param flag The interface id will be returned in this parameter. + * \param id The interface id will be returned in this parameter. */ bool interface_id(const std::string &iface, uint32_t &id); diff --git a/src/arp.cpp b/src/arp.cpp index 1f6f171..d0d9cb7 100644 --- a/src/arp.cpp +++ b/src/arp.cpp @@ -31,66 +31,9 @@ using namespace std; -Tins::PDU* Tins::ARP::make_arp_request(const std::string& iface, - const std::string& target, - const std::string& sender, - const uint8_t* hw_snd) { - uint32_t target_ip = Tins::Utils::resolve_ip(target); - uint32_t sender_ip = Tins::Utils::resolve_ip(sender); - return make_arp_request(iface, target_ip, sender_ip, hw_snd); -} - -Tins::PDU* Tins::ARP::make_arp_request(const std::string& iface, - uint32_t target, - uint32_t sender, - const uint8_t* hw_snd) { - - /* Create ARP packet and set its attributes */ - ARP* arp = new ARP(); - arp->target_ip_addr(target); - arp->sender_ip_addr(sender); - if (hw_snd) { - arp->sender_hw_addr(hw_snd); - } - arp->opcode(REQUEST); - - /* Create the EthernetII PDU with the ARP PDU as its inner PDU */ - EthernetII* eth = new EthernetII(iface, Tins::EthernetII::BROADCAST, hw_snd, arp); - return eth; -} - -Tins::PDU* Tins::ARP::make_arp_reply(const string& iface, - const string& target, - const string& sender, - const uint8_t* hw_tgt, - const uint8_t* hw_snd) { - - uint32_t target_ip = Tins::Utils::resolve_ip(target); - uint32_t sender_ip = Tins::Utils::resolve_ip(sender); - return make_arp_reply(iface, target_ip, sender_ip, hw_tgt, hw_snd); -} - -Tins::PDU* Tins::ARP::make_arp_reply(const string& iface, - uint32_t target, - uint32_t sender, - const uint8_t* hw_tgt, - const uint8_t* hw_snd) { - - /* Create ARP packet and set its attributes */ - ARP* arp = new ARP(); - arp->target_ip_addr(target); - arp->sender_ip_addr(sender); - arp->target_hw_addr(hw_tgt); - arp->sender_hw_addr(hw_snd); - arp->opcode(REPLY); - - /* Create the EthernetII PDU with the ARP PDU as its inner PDU */ - EthernetII* eth = new EthernetII(iface, hw_tgt, hw_snd, arp); - return eth; -} Tins::ARP::ARP(uint32_t target_ip, uint32_t sender_ip, const uint8_t *target_hw, const uint8_t *sender_hw) : PDU(0x0608) { - std::memset(&_arp, 0, sizeof(arphdr)); + memset(&_arp, 0, sizeof(arphdr)); hw_addr_format(1); prot_addr_format(0x0800); hw_addr_length(6); @@ -103,7 +46,16 @@ Tins::ARP::ARP(uint32_t target_ip, uint32_t sender_ip, const uint8_t *target_hw, target_hw_addr(target_hw); } -Tins::ARP::ARP(arphdr *arp_ptr) : PDU(Utils::net_to_host_s(0x0806)) { +Tins::ARP::ARP(const uint8_t *buffer, uint32_t total_sz) : PDU(0x0608) { + if(total_sz < sizeof(arphdr)) + throw std::runtime_error("Not enought size for an ARP header in the buffer."); + memcpy(&_arp, buffer, sizeof(arphdr)); + total_sz -= sizeof(arphdr); + if(total_sz) + inner_pdu(new RawPDU(buffer + sizeof(arphdr), total_sz)); +} + +Tins::ARP::ARP(const arphdr *arp_ptr) : PDU(Utils::net_to_host_s(0x0806)) { memcpy(&_arp, arp_ptr, sizeof(arphdr)); } @@ -188,10 +140,10 @@ bool Tins::ARP::matches_response(uint8_t *ptr, uint32_t total_sz) { return arp_ptr->ar_sip == _arp.ar_tip && arp_ptr->ar_tip == _arp.ar_sip; } -Tins::PDU *Tins::ARP::clone_packet(uint8_t *ptr, uint32_t total_sz) { +Tins::PDU *Tins::ARP::clone_packet(const uint8_t *ptr, uint32_t total_sz) { if(total_sz < sizeof(arphdr)) return 0; - arphdr *arp_ptr = (arphdr*)ptr; + const arphdr *arp_ptr = (arphdr*)ptr; PDU *child = 0, *cloned; if(total_sz > sizeof(arphdr)) { if((child = PDU::clone_inner_pdu(ptr + sizeof(arphdr), total_sz - sizeof(arphdr))) == 0) @@ -201,3 +153,61 @@ Tins::PDU *Tins::ARP::clone_packet(uint8_t *ptr, uint32_t total_sz) { cloned->inner_pdu(child); return cloned; } + +Tins::PDU* Tins::ARP::make_arp_request(const string& iface, + const string& target, + const string& sender, + const uint8_t* hw_snd) { + uint32_t target_ip = Tins::Utils::resolve_ip(target); + uint32_t sender_ip = Tins::Utils::resolve_ip(sender); + return make_arp_request(iface, target_ip, sender_ip, hw_snd); +} + +Tins::PDU* Tins::ARP::make_arp_request(const std::string& iface, + uint32_t target, + uint32_t sender, + const uint8_t* hw_snd) { + + /* Create ARP packet and set its attributes */ + ARP* arp = new ARP(); + arp->target_ip_addr(target); + arp->sender_ip_addr(sender); + if (hw_snd) { + arp->sender_hw_addr(hw_snd); + } + arp->opcode(REQUEST); + + /* Create the EthernetII PDU with the ARP PDU as its inner PDU */ + EthernetII* eth = new EthernetII(iface, Tins::EthernetII::BROADCAST, hw_snd, arp); + return eth; +} + +Tins::PDU* Tins::ARP::make_arp_reply(const string& iface, + const string& target, + const string& sender, + const uint8_t* hw_tgt, + const uint8_t* hw_snd) { + + uint32_t target_ip = Tins::Utils::resolve_ip(target); + uint32_t sender_ip = Tins::Utils::resolve_ip(sender); + return make_arp_reply(iface, target_ip, sender_ip, hw_tgt, hw_snd); +} + +Tins::PDU* Tins::ARP::make_arp_reply(const string& iface, + uint32_t target, + uint32_t sender, + const uint8_t* hw_tgt, + const uint8_t* hw_snd) { + + /* Create ARP packet and set its attributes */ + ARP* arp = new ARP(); + arp->target_ip_addr(target); + arp->sender_ip_addr(sender); + arp->target_hw_addr(hw_tgt); + arp->sender_hw_addr(hw_snd); + arp->opcode(REPLY); + + /* Create the EthernetII PDU with the ARP PDU as its inner PDU */ + EthernetII* eth = new EthernetII(iface, hw_tgt, hw_snd, arp); + return eth; +} diff --git a/src/dhcp.cpp b/src/dhcp.cpp index fe6c4da..cb42878 100644 --- a/src/dhcp.cpp +++ b/src/dhcp.cpp @@ -21,7 +21,6 @@ #include #include -#include //borrame #include "utils.h" #include "dhcp.h" diff --git a/src/ethernetII.cpp b/src/ethernetII.cpp index f5d954b..85f9d25 100644 --- a/src/ethernetII.cpp +++ b/src/ethernetII.cpp @@ -21,7 +21,7 @@ #include #include - +#include #ifndef WIN32 #include #include @@ -29,6 +29,8 @@ #endif #include "ethernetII.h" #include "rawpdu.h" +#include "ip.h" +#include "arp.h" #include "utils.h" const uint8_t* Tins::EthernetII::BROADCAST = (const uint8_t*)"\xff\xff\xff\xff\xff\xff"; @@ -54,10 +56,31 @@ Tins::EthernetII::EthernetII(uint32_t iface_index, const uint8_t* mac_dst, const this->_eth.payload_type = 0; } +Tins::EthernetII::EthernetII(const uint8_t *buffer, uint32_t total_sz) : PDU(ETHERTYPE_IP) { + if(total_sz < sizeof(ethhdr)) + throw std::runtime_error("Not enought size for an ethernetII header in the buffer."); + memcpy(&_eth, buffer, sizeof(ethhdr)); + PDU *next = 0; + switch(payload_type()) { + case ETHERTYPE_IP: + next = new Tins::IP(buffer + sizeof(ethhdr), total_sz - sizeof(ethhdr)); + break; + case ETHERTYPE_ARP: + next = new Tins::ARP(buffer + sizeof(ethhdr), total_sz - sizeof(ethhdr)); + break; + // Other protos plz + } + inner_pdu(next); +} + Tins::EthernetII::EthernetII(ethhdr *eth_ptr) : PDU(ETHERTYPE_IP) { memcpy(&_eth, eth_ptr, sizeof(ethhdr)); } +uint16_t Tins::EthernetII::payload_type() const { + return Utils::net_to_host_s(_eth.payload_type); +} + void Tins::EthernetII::dst_mac(const uint8_t* new_dst_mac) { memcpy(this->_eth.dst_mac, new_dst_mac, 6); } diff --git a/src/icmp.cpp b/src/icmp.cpp index 774e753..e3ca194 100644 --- a/src/icmp.cpp +++ b/src/icmp.cpp @@ -19,12 +19,12 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - +#include +#include +#include #ifndef WIN32 #include #endif -#include -#include #include "icmp.h" #include "rawpdu.h" #include "utils.h" @@ -48,7 +48,16 @@ Tins::ICMP::ICMP(Flags flag) : PDU(IPPROTO_ICMP) { }; } -Tins::ICMP::ICMP(icmphdr *ptr) : PDU(IPPROTO_ICMP) { +Tins::ICMP::ICMP(const uint8_t *buffer, uint32_t total_sz) : PDU(IPPROTO_ICMP) { + if(total_sz < sizeof(icmphdr)) + throw std::runtime_error("Not enought size for an ICMP header in the buffer."); + std::memcpy(&_icmp, buffer, sizeof(icmphdr)); + total_sz -= sizeof(icmphdr); + if(total_sz) + inner_pdu(new RawPDU(buffer + sizeof(icmphdr), total_sz)); +} + +Tins::ICMP::ICMP(const icmphdr *ptr) : PDU(IPPROTO_ICMP) { std::memcpy(&_icmp, ptr, sizeof(icmphdr)); } @@ -177,10 +186,10 @@ bool Tins::ICMP::matches_response(uint8_t *ptr, uint32_t total_sz) { return false; } -Tins::PDU *Tins::ICMP::clone_packet(uint8_t *ptr, uint32_t total_sz) { +Tins::PDU *Tins::ICMP::clone_packet(const uint8_t *ptr, uint32_t total_sz) { if(total_sz < sizeof(icmphdr)) return 0; - icmphdr *icmp_ptr = (icmphdr*)ptr; + const icmphdr *icmp_ptr = (icmphdr*)ptr; PDU *child = 0, *cloned; if(total_sz > sizeof(icmphdr)) { if((child = PDU::clone_inner_pdu(ptr + sizeof(icmphdr), total_sz - sizeof(icmphdr))) == 0) diff --git a/src/ip.cpp b/src/ip.cpp index b54de5c..c513ae5 100644 --- a/src/ip.cpp +++ b/src/ip.cpp @@ -19,12 +19,16 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include #include #include #ifndef WIN32 #include #endif #include "ip.h" +#include "tcp.h" +#include "udp.h" +#include "icmp.h" #include "rawpdu.h" #include "utils.h" @@ -42,6 +46,29 @@ Tins::IP::IP(const string &ip_dst, const string &ip_src, PDU *child) : PDU(IPPRO } +Tins::IP::IP(const uint8_t *buffer, uint32_t total_sz) : PDU(IPPROTO_IP) { + if(total_sz < sizeof(iphdr)) + throw std::runtime_error("Not enought size for an IP header in the buffer."); + std::memcpy(&_ip, buffer, sizeof(iphdr)); + /* Options... */ + buffer += head_len() * sizeof(uint32_t); + total_sz -= head_len() * sizeof(uint32_t); + switch(_ip.protocol) { + case IPPROTO_TCP: + inner_pdu(new Tins::TCP(buffer, total_sz)); + break; + case IPPROTO_UDP: + inner_pdu(new Tins::UDP(buffer, total_sz)); + break; + case IPPROTO_ICMP: + inner_pdu(new Tins::ICMP(buffer, total_sz)); + break; + default: + inner_pdu(new Tins::RawPDU(buffer, total_sz)); + break; + } +} + Tins::IP::IP(const iphdr *ptr) : PDU(IPPROTO_IP) { std::memcpy(&_ip, ptr, sizeof(iphdr)); /* Options... */ @@ -241,10 +268,10 @@ bool Tins::IP::matches_response(uint8_t *ptr, uint32_t total_sz) { return false; } -Tins::PDU *Tins::IP::clone_packet(uint8_t *ptr, uint32_t total_sz) { +Tins::PDU *Tins::IP::clone_packet(const uint8_t *ptr, uint32_t total_sz) { if(total_sz < sizeof(iphdr)) return 0; - iphdr *ip_ptr = (iphdr*)ptr; + const iphdr *ip_ptr = (iphdr*)ptr; uint32_t sz = ip_ptr->ihl * sizeof(uint32_t); if(total_sz < sz) return 0; diff --git a/src/pdu.cpp b/src/pdu.cpp index cadae31..07e1b92 100644 --- a/src/pdu.cpp +++ b/src/pdu.cpp @@ -20,7 +20,6 @@ */ #include -#include //borrame #include "utils.h" #include "pdu.h" #include "rawpdu.h" @@ -69,7 +68,7 @@ void Tins::PDU::serialize(uint8_t *buffer, uint32_t total_sz, const PDU *parent) write_serialization(buffer, total_sz, parent); } -Tins::PDU *Tins::PDU::clone_inner_pdu(uint8_t *ptr, uint32_t total_sz) { +Tins::PDU *Tins::PDU::clone_inner_pdu(const uint8_t *ptr, uint32_t total_sz) { PDU *child = 0; if(inner_pdu()) { child = inner_pdu()->clone_packet(ptr, total_sz); @@ -77,7 +76,7 @@ Tins::PDU *Tins::PDU::clone_inner_pdu(uint8_t *ptr, uint32_t total_sz) { return 0; } else - child = new RawPDU(ptr, total_sz, true); + child = new RawPDU(ptr, total_sz); return child; } diff --git a/src/rawpdu.cpp b/src/rawpdu.cpp index 27028d1..f2946e0 100644 --- a/src/rawpdu.cpp +++ b/src/rawpdu.cpp @@ -24,11 +24,13 @@ #include "rawpdu.h" -Tins::RawPDU::RawPDU(uint8_t *pload, uint32_t size, bool copy) : PDU(255), _payload(pload), _payload_size(size), _owns_payload(copy) { - if(copy) { - _payload = new uint8_t[size]; - std::memcpy(_payload, pload, size); - } +Tins::RawPDU::RawPDU(const uint8_t *pload, uint32_t size) : PDU(255), _payload_size(size), _owns_payload(true) { + _payload = new uint8_t[size]; + std::memcpy(_payload, pload, size); +} + +Tins::RawPDU::RawPDU(uint8_t *pload, uint32_t size) : PDU(255), _payload(pload), _payload_size(size), _owns_payload(false) { + } Tins::RawPDU::~RawPDU() { diff --git a/src/sniffer.cpp b/src/sniffer.cpp new file mode 100644 index 0000000..ed074cb --- /dev/null +++ b/src/sniffer.cpp @@ -0,0 +1,64 @@ +#include +#include "sniffer.h" +#include "ethernetII.h" + + +using namespace std; + +Tins::Sniffer::Sniffer(const string &device, unsigned max_packet_size) { + char error[PCAP_ERRBUF_SIZE]; + if (pcap_lookupnet(device.c_str(), &ip, &mask, error) == -1) + throw runtime_error(error); + handle = pcap_open_live(device.c_str(), max_packet_size, 0, 0, error); + if(!handle) + throw runtime_error(error); +} + +Tins::Sniffer::~Sniffer() { + if(handle) + pcap_close(handle); +} + +bool Tins::Sniffer::compile_set_filter(const string &filter, bpf_program &prog) { + return (pcap_compile(handle, &prog, filter.c_str(), 0, ip) != -1 && pcap_setfilter(handle, &prog) != -1); +} + +Tins::PDU *Tins::Sniffer::next_pdu(const string &filter) { + bpf_program prog; + if(!compile_set_filter(filter, prog)) + return 0; + pcap_pkthdr header; + PDU *ret = 0; + while(!ret) { + const u_char *content = pcap_next(handle, &header); + try { + ret = new EthernetII((const uint8_t*)content, header.caplen); + } + catch(...) { + ret = 0; + } + } + pcap_freecode(&prog); + return ret; +} + +void Tins::Sniffer::sniff_loop(const std::string &filter, AbstractSnifferHandler *cback_handler, uint32_t max_packets) { + bpf_program prog; + if(compile_set_filter(filter, prog)) { + pcap_loop(handle, max_packets, Sniffer::callback_handler, (u_char*)cback_handler); + pcap_freecode(&prog); + } +} + +// Static +void Tins::Sniffer::callback_handler(u_char *args, const struct pcap_pkthdr *header, const u_char *packet) { + try { + PDU *pdu = new EthernetII((const uint8_t*)packet, header->caplen); + reinterpret_cast(args)->handle(pdu); + delete pdu; + } + catch(...) { + + } +} + diff --git a/src/tcp.cpp b/src/tcp.cpp index fea0d5e..421bd98 100644 --- a/src/tcp.cpp +++ b/src/tcp.cpp @@ -19,6 +19,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include #include #include #ifndef WIN32 @@ -41,6 +42,18 @@ Tins::TCP::TCP(uint16_t dport, uint16_t sport) : PDU(IPPROTO_TCP), _options_size this->check(0); } +Tins::TCP::TCP(const uint8_t *buffer, uint32_t total_sz) : PDU(IPPROTO_TCP) { + if(total_sz < sizeof(tcphdr)) + throw std::runtime_error("Not enought size for an TCP header in the buffer."); + std::memcpy(&_tcp, buffer, sizeof(tcphdr)); + + /* Options... */ + + total_sz -= sizeof(tcphdr); + if(total_sz) + inner_pdu(new RawPDU(buffer + sizeof(tcphdr), total_sz)); +} + Tins::TCP::~TCP() { for(unsigned i(0); i < _options.size(); ++i) delete[] _options[i].data; @@ -92,6 +105,38 @@ void Tins::TCP::set_timestamp(uint32_t value, uint32_t reply) { add_option(TSOPT, 8, (uint8_t*)&buffer); } +uint8_t Tins::TCP::get_flag(Flags tcp_flag) { + switch(tcp_flag) { + case FIN: + return _tcp.fin; + break; + case SYN: + return _tcp.syn; + break; + case RST: + return _tcp.rst; + break; + case PSH: + return _tcp.psh; + break; + case ACK: + return _tcp.ack; + break; + case URG: + return _tcp.urg; + break; + case ECE: + return _tcp.ece; + break; + case CWR: + return _tcp.cwr; + break; + default: + return 0; + break; + }; +} + void Tins::TCP::set_flag(Flags tcp_flag, uint8_t value) { switch(tcp_flag) { case FIN: diff --git a/src/udp.cpp b/src/udp.cpp index 8199927..0f6aa44 100644 --- a/src/udp.cpp +++ b/src/udp.cpp @@ -19,11 +19,12 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include +#include +#include #ifndef WIN32 #include #endif -#include -#include #include "utils.h" #include "udp.h" #include "ip.h" @@ -36,6 +37,15 @@ Tins::UDP::UDP(uint16_t dport, uint16_t sport, PDU *child) : PDU(IPPROTO_UDP, ch _udp.len = 0; } +Tins::UDP::UDP(const uint8_t *buffer, uint32_t total_sz) : PDU(IPPROTO_UDP) { + if(total_sz < sizeof(udphdr)) + throw std::runtime_error("Not enought size for an UDP header in the buffer."); + std::memcpy(&_udp, buffer, sizeof(udphdr)); + total_sz -= sizeof(udphdr); + if(total_sz) + inner_pdu(new RawPDU(buffer + sizeof(udphdr), total_sz)); +} + void Tins::UDP::payload(uint8_t *new_payload, uint32_t new_payload_size) { inner_pdu(new RawPDU(new_payload, new_payload_size)); }