diff --git a/include/hwaddress.h b/include/hwaddress.h index 9a485b2..5a2892f 100644 --- a/include/hwaddress.h +++ b/include/hwaddress.h @@ -39,12 +39,11 @@ public: typedef const storage_type* const_iterator; static const size_t address_size = n; - HWAddress() { - std::fill(begin(), end(), storage_type()); - } - - HWAddress(const storage_type* ptr) { - std::copy(ptr, ptr + address_size, buffer); + HWAddress(const storage_type* ptr = 0) { + if(ptr) + std::copy(ptr, ptr + address_size, buffer); + else + std::fill(begin(), end(), storage_type()); } HWAddress(const std::string &address) { @@ -97,14 +96,20 @@ public: return address_size; } + std::string to_string() const { + std::ostringstream oss; + oss << *this; + return oss.str(); + } + friend std::ostream &operator<<(std::ostream &os, const HWAddress &addr) { std::transform( - addr.buffer, - addr.buffer + HWAddress::address_size - 1, + addr.begin(), + addr.end() - 1, std::ostream_iterator(os, ":"), - &HWAddress::to_string + &HWAddress::storage_to_string ); - return os << to_string(addr.buffer[HWAddress::address_size-1]); + return os << storage_to_string(addr.buffer[HWAddress::address_size-1]); } template @@ -115,7 +120,7 @@ private: template static void convert(const std::string &hw_addr, OutputIterator output); - static std::string to_string(storage_type element) { + static std::string storage_to_string(storage_type element) { std::ostringstream oss; oss << std::hex; if(element < 0x10) diff --git a/include/sniffer.h b/include/sniffer.h index 3b3e521..f8fccd6 100644 --- a/include/sniffer.h +++ b/include/sniffer.h @@ -28,29 +28,10 @@ #include #include #include "pdu.h" +#include "ethernetII.h" +#include "radiotap.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. * @@ -69,7 +50,8 @@ namespace Tins { * \param promisc bool indicating wether to put the interface in promiscuous mode. * \param filter A capture filter to compile and use for sniffing sessions.(optional); */ - Sniffer(const std::string &device, unsigned max_packet_size, bool promisc = false, const std::string &filter = "") throw(std::runtime_error); + Sniffer(const std::string &device, unsigned max_packet_size, + bool promisc = false, const std::string &filter = ""); /** * \brief Sniffer destructor. @@ -92,13 +74,24 @@ namespace Tins { * \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. + * The callback object must implement an operator with the + * following(or compatible) signature: + * + * bool operator()(PDU*); + * + * This operator will be called using the sniffed packets + * as arguments. The callback object must not delete the + * PDU parameter. + * + * Note that the Functor object will be copied using its copy + * constructor, so that object should be some kind of proxy to + * another object which will process the packets(e.g. std::bind). + * * \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(AbstractSnifferHandler *cback_handler, uint32_t max_packets = 0); + template + void sniff_loop(const Functor &function, uint32_t max_packets = 0); /** * \brief Sets a filter on this sniffer. @@ -112,10 +105,22 @@ namespace Tins { */ void stop_sniff(); private: + template + struct LoopData { + pcap_t *handle; + Functor c_handler; + bool wired; + + LoopData(pcap_t *_handle, const Functor _handler, + bool is_wired) + : handle(_handle), c_handler(_handler), wired(is_wired) { } + }; + Sniffer(const Sniffer&); Sniffer &operator=(const Sniffer&); bool compile_set_filter(const std::string &filter, bpf_program &prog); + template static void callback_handler(u_char *args, const struct pcap_pkthdr *header, const u_char *packet); pcap_t *handle; @@ -123,33 +128,31 @@ namespace Tins { bpf_program actual_filter; bool wired; }; - - /** - * \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); + template + void Tins::Sniffer::sniff_loop(const Functor &function, uint32_t max_packets) { + LoopData data(handle, function, wired); + pcap_loop(handle, max_packets, &Sniffer::callback_handler, (u_char*)&data); + } + + template + void Tins::Sniffer::callback_handler(u_char *args, const struct pcap_pkthdr *header, const u_char *packet) { + try { + PDU *pdu = 0; + LoopData *data = reinterpret_cast*>(args); + if(data->wired) + pdu = new Tins::EthernetII((const uint8_t*)packet, header->caplen); + else + pdu = new Tins::RadioTap((const uint8_t*)packet, header->caplen); + bool ret_val = data->c_handler(pdu); + delete pdu; + if(!ret_val) + pcap_breakloop(data->handle); } - private: - T *handler; - }; + catch(...) { + + } + } }; #endif // TINS_SNIFFER_H diff --git a/include/utils.h b/include/utils.h index da7ee37..e2109eb 100644 --- a/include/utils.h +++ b/include/utils.h @@ -34,6 +34,7 @@ #include "packetsender.h" #include "ipaddress.h" #include "hwaddress.h" +#include "network_interface.h" namespace Tins { /** \brief Network utils namespace. @@ -71,24 +72,6 @@ namespace Tins { */ std::string ip_to_string(uint32_t ip); - /** \brief Converts a hardware address string into a byte array. - * - * The hardware address must be formatted using the notation 'HH:HH:HH:HH:HH:HH'. - * Where H is a hexadecimal character(0-9, a-f). - * - * \param hw_addr The harware address string. - * \param array The output buffer. It must be at least 6 bytes long. - */ - bool hwaddr_to_byte(const std::string &hw_addr, uint8_t *array); - - /** \brief Converts a byte array representing a hardware address - * into a string. - * - * The input buffer must be at least 6 bytes long. - * \param array The input buffer. - */ - std::string hwaddr_to_string(const uint8_t *array); - /** \brief Resolves a domain name and returns its corresponding ip address. * * If an ip address is given, its integer representation is returned. @@ -96,7 +79,7 @@ namespace Tins { * * \param to_resolve The domain name/ip address to resolve. */ - uint32_t resolve_ip(const std::string &to_resolve) throw (std::runtime_error); + uint32_t resolve_ip(const std::string &to_resolve); /** * \brief Pings an ip address. @@ -113,7 +96,7 @@ namespace Tins { * \return PDU * containing either 0 if no response was received, * or the ICMP response otherwise. */ - PDU *ping_address(uint32_t ip, PacketSender *sender, IPv4Address ip_src = 0); + PDU *ping_address(IPv4Address ip, PacketSender *sender, IPv4Address ip_src = 0); /** \brief Resolves the hardware address for a given ip. * @@ -124,7 +107,7 @@ namespace Tins { * \return Returns true if the hardware address was resolved successfully, * false otherwise. */ - bool resolve_hwaddr(const std::string &iface, IPv4Address ip, + bool resolve_hwaddr(const NetworkInterface &iface, IPv4Address ip, HWAddress<6> *address, PacketSender *sender); /** \brief List all network interfaces. @@ -134,63 +117,6 @@ namespace Tins { * interface for Utils::interface_ip, Utils::interface_hwaddr, etc. */ std::set network_interfaces(); - - /** - * \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. - * \param ip The ip address found will be returned in this param. - * - * \return bool indicating wether the operation was successfull. - */ - bool interface_ip(const std::string &iface, IPv4Address &ip); - - /** - * \brief Lookup the ip/hw/netmask/broadcast 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. - * \param info The InterfaceInfo in which the information will - * be stored. - * - * \return bool indicating wether the operation was successfull. - */ - bool interface_info(const std::string &iface, InterfaceInfo &info); - - /** - * \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. - * \param buffer The hw address will be stored in this buffer. It must - * be at least 6 bytes long. - * - * \return bool indicating wether the operation was successfull. - */ - bool interface_hwaddr(const std::string &iface, HWAddress<6> *address); - - /** - * \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. - * \param id The interface id will be returned in this parameter. - * - * \return bool indicating wether the operation was successfull. - */ - bool interface_id(const std::string &iface, uint32_t &id); - - /** - * \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(IPv4Address ip); /** * \brief Finds the gateway's IP address for the given IP diff --git a/src/sniffer.cpp b/src/sniffer.cpp index 0daa047..4e25a77 100644 --- a/src/sniffer.cpp +++ b/src/sniffer.cpp @@ -21,26 +21,13 @@ #include "sniffer.h" -#include "ethernetII.h" -#include "radiotap.h" using namespace std; -/** \cond */ - -struct LoopData { - pcap_t *handle; - Tins::AbstractSnifferHandler *c_handler; - bool wired; - - LoopData(pcap_t *_handle, Tins::AbstractSnifferHandler *_handler, bool is_wired) : handle(_handle), c_handler(_handler), wired(is_wired) { } -}; - -/** \endcond */ - - -Tins::Sniffer::Sniffer(const string &device, unsigned max_packet_size, bool promisc, const string &filter) throw(std::runtime_error) { +Tins::Sniffer::Sniffer(const string &device, unsigned max_packet_size, +bool promisc, const string &filter) +{ char error[PCAP_ERRBUF_SIZE]; if (pcap_lookupnet(device.c_str(), &ip, &mask, error) == -1) { ip = 0; @@ -90,33 +77,9 @@ void Tins::Sniffer::stop_sniff() { pcap_breakloop(handle); } -void Tins::Sniffer::sniff_loop(AbstractSnifferHandler *cback_handler, uint32_t max_packets) { - LoopData data(handle, cback_handler, wired); - pcap_loop(handle, max_packets, Sniffer::callback_handler, (u_char*)&data); -} - bool Tins::Sniffer::set_filter(const std::string &filter) { if(actual_filter.bf_insns) pcap_freecode(&actual_filter); return compile_set_filter(filter, actual_filter); } -// Static -void Tins::Sniffer::callback_handler(u_char *args, const struct pcap_pkthdr *header, const u_char *packet) { - try { - PDU *pdu = 0; - LoopData *data = reinterpret_cast(args); - if(data->wired) - pdu = new EthernetII((const uint8_t*)packet, header->caplen); - else - pdu = new RadioTap((const uint8_t*)packet, header->caplen); - bool ret_val = data->c_handler->handle(pdu); - delete pdu; - if(!ret_val) - pcap_breakloop(data->handle); - } - catch(...) { - - } -} - diff --git a/src/utils.cpp b/src/utils.cpp index ea2a846..593d2f8 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -66,49 +66,6 @@ struct IPv4Collector { } }; -/** \cond */ -struct HWAddressCollector { - Tins::HWAddress<6> *result; - bool found; - const char *iface; - - HWAddressCollector(Tins::HWAddress<6> *res, const char *interface) - : result(res), found(false), iface(interface) { } - - bool operator() (struct ifaddrs *addr) { - if(!found && addr->ifa_addr->sa_family == AF_PACKET && !strcmp(addr->ifa_name, iface)) { - *result = ((struct sockaddr_ll*)addr->ifa_addr)->sll_addr; - found = true; - } - return found; - } -}; - -/** \cond */ -struct InterfaceInfoCollector { - Tins::Utils::InterfaceInfo *info; - const char *iface; - bool found; - - InterfaceInfoCollector(Tins::Utils::InterfaceInfo *res, const char *interface) : - info(res), iface(interface), found(false) { } - - bool operator() (struct ifaddrs *addr) { - if(addr->ifa_addr->sa_family == AF_PACKET && !strcmp(addr->ifa_name, iface)) - memcpy(info->hw_addr, ((struct sockaddr_ll*)addr->ifa_addr)->sll_addr, sizeof(info->hw_addr)); - else if(addr->ifa_addr->sa_family == AF_INET && !strcmp(addr->ifa_name, iface)) { - info->ip_addr = ((struct sockaddr_in *)addr->ifa_addr)->sin_addr.s_addr; - info->netmask = ((struct sockaddr_in *)addr->ifa_netmask)->sin_addr.s_addr; - if((addr->ifa_flags & (IFF_BROADCAST | IFF_POINTOPOINT))) - info->bcast_addr = ((struct sockaddr_in *)addr->ifa_ifu.ifu_broadaddr)->sin_addr.s_addr; - else - info->bcast_addr = 0; - found = true; - } - return found; - } -}; - bool Tins::Utils::Internals::from_hex(const string &str, uint32_t &result) { unsigned i(0); result = 0; @@ -170,72 +127,33 @@ string Tins::Utils::ip_to_string(uint32_t ip) { return oss.str(); } -bool Tins::Utils::hwaddr_to_byte(const std::string &hw_addr, uint8_t *array) { - if(hw_addr.size() != 17) - return false; - unsigned i(0), arr_index(0); - uint8_t tmp; - while(i < hw_addr.size()) { - unsigned end=i+2; - tmp = 0; - while(i < end) { - if(hw_addr[i] >= 'a' && hw_addr[i] <= 'f') - tmp = (tmp << 4) | (hw_addr[i] - 'a' + 10); - else if(hw_addr[i] >= '0' && hw_addr[i] <= '9') - tmp = (tmp << 4) | (hw_addr[i] - '0'); - else - return false; - i++; - } - array[arr_index++] = tmp; - if(i < hw_addr.size()) { - if(hw_addr[i] == ':') - i++; - else - return false; - } - } - return true; -} - -string Tins::Utils::hwaddr_to_string(const uint8_t *array) { - ostringstream oss; - oss << hex; - for(unsigned i(0); i < 6; ++i) { - if(array[i] < 0x10) - oss << '0'; - oss << (unsigned)array[i]; - if(i < 5) - oss << ':'; - } - return oss.str(); -} - -uint32_t Tins::Utils::resolve_ip(const string &to_resolve) throw (std::runtime_error) { +uint32_t Tins::Utils::resolve_ip(const string &to_resolve) { struct hostent *data = gethostbyname(to_resolve.c_str()); if(!data) throw std::runtime_error("Could not resolve IP"); return Utils::net_to_host_l(((struct in_addr**)data->h_addr_list)[0]->s_addr); } -Tins::PDU *Tins::Utils::ping_address(uint32_t ip, PacketSender *sender, IPv4Address ip_src) { +Tins::PDU *Tins::Utils::ping_address(IPv4Address ip, PacketSender *sender, IPv4Address ip_src) { ICMP *icmp = new ICMP(ICMP::ECHO_REQUEST); if(!ip_src) { - std::string iface(Utils::interface_from_ip(ip)); - if(!iface.size() || !Utils::interface_ip(iface, ip_src)) + try { + NetworkInterface iface(ip); + ip_src = iface.addresses().ip_addr; + } catch(...) { return 0; + } } IP ip_packet(ip, ip_src, icmp); return sender->send_recv(&ip_packet); } -bool Tins::Utils::resolve_hwaddr(const string &iface, IPv4Address ip, - HWAddress<6> *address, PacketSender *sender) { +bool Tins::Utils::resolve_hwaddr(const NetworkInterface &iface, IPv4Address ip, + HWAddress<6> *address, PacketSender *sender) +{ IPv4Address my_ip; - HWAddress<6> my_hw; - if(!interface_ip(iface, my_ip) || !interface_hwaddr(iface, &my_hw)) - return false; - PDU *packet = ARP::make_arp_request(iface, ip, my_ip, my_hw); + NetworkInterface::Info info(iface.addresses()); + PDU *packet = ARP::make_arp_request(iface, ip, info.ip_addr, info.hw_addr); PDU *response = sender->send_recv(packet); delete packet; if(response) { @@ -249,19 +167,6 @@ bool Tins::Utils::resolve_hwaddr(const string &iface, IPv4Address ip, return false; } -string Tins::Utils::interface_from_ip(IPv4Address ip) { - if(ip == 0x7f000001) - return "lo"; - std::vector entries; - uint32_t ip_int = ip; - route_entries(std::back_inserter(entries)); - for(std::vector::const_iterator it(entries.begin()); it != entries.end(); ++it) { - if((ip_int & it->mask) == it->destination) - return it->interface; - } - return ""; -} - bool Tins::Utils::gateway_from_ip(IPv4Address ip, IPv4Address &gw_addr) { typedef std::vector entries_type; entries_type entries; @@ -282,33 +187,6 @@ set Tins::Utils::network_interfaces() { return collector.ifaces; } -bool Tins::Utils::interface_ip(const string &iface, IPv4Address &ip) { - IPv4Collector collector(iface.c_str()); - generic_iface_loop(collector); - ip = Utils::net_to_host_l(collector.ip); - return collector.found; -} - -bool Tins::Utils::interface_hwaddr(const string &iface, HWAddress<6> *address) { - HWAddressCollector collector(address, iface.c_str()); - generic_iface_loop(collector); - return collector.found; -} - -bool Tins::Utils::interface_info(const string &iface, InterfaceInfo &info) { - InterfaceInfoCollector collector(&info, iface.c_str()); - generic_iface_loop(collector); - info.ip_addr = net_to_host_l(info.ip_addr); - info.netmask = net_to_host_l(info.netmask); - info.bcast_addr = net_to_host_l(info.bcast_addr); - return collector.found; -} - -bool Tins::Utils::interface_id(const string &iface, uint32_t &id) { - id = if_nametoindex(iface.c_str()); - return (((int32_t)id) != -1); -} - uint16_t Tins::Utils::channel_to_mhz(uint16_t channel) { return 2407 + (channel * 5); } diff --git a/tests/src/utils_test.cpp b/tests/src/utils_test.cpp index d7790e8..79596f7 100644 --- a/tests/src/utils_test.cpp +++ b/tests/src/utils_test.cpp @@ -80,25 +80,6 @@ TEST_F(UtilsTest, IpToString) { } -TEST_F(UtilsTest, HwaddrToByte) { - uint8_t result_buf[6]; - - Utils::hwaddr_to_byte("00:00:00:00:00:00", result_buf); - EXPECT_TRUE(memcmp(result_buf, zero_hw_addr, 6) == 0); - Utils::hwaddr_to_byte("ff:ff:ff:ff:ff:ff", result_buf); - EXPECT_TRUE(memcmp(result_buf, full_hw_addr, 6) == 0); - Utils::hwaddr_to_byte("01:02:03:04:05:06", result_buf); - EXPECT_TRUE(memcmp(result_buf, mix_hw_addr, 6) == 0); - -} - -TEST_F(UtilsTest, HwaddrToString) { - - EXPECT_EQ(Utils::hwaddr_to_string(zero_hw_addr), "00:00:00:00:00:00"); - EXPECT_EQ(Utils::hwaddr_to_string(full_hw_addr), "ff:ff:ff:ff:ff:ff"); - EXPECT_EQ(Utils::hwaddr_to_string(mix_hw_addr), "01:02:03:04:05:06"); - -} TEST_F(UtilsTest, ResolveIp) { uint32_t localhost_ip = Utils::ip_to_int("127.0.0.1"); @@ -108,17 +89,6 @@ TEST_F(UtilsTest, ResolveIp) { } -TEST_F(UtilsTest, InterfaceIp) { - IPv4Address ip; - IPv4Address localhost_ip = Utils::ip_to_int("127.0.0.1"); - -#ifndef WIN32 - ASSERT_TRUE(Utils::interface_ip("lo", ip)); - EXPECT_EQ(ip, localhost_ip); -#endif - EXPECT_FALSE(Utils::interface_ip("asdfghgfdsa", ip)); - -} TEST_F(UtilsTest, NetToHostS) { uint16_t a = 0x01FE;