From 5b2934e10221630fd21106f474b2ecc9995c5a78 Mon Sep 17 00:00:00 2001 From: Santiago Alessandri Date: Fri, 29 Aug 2014 16:30:13 -0700 Subject: [PATCH 1/8] Refactored sniffer class to take a SnifferConfigurator to do the setup. This way it is easier to extend the different configuration capabilities. --- include/sniffer.h | 94 +++++++++++------ src/sniffer.cpp | 258 ++++++++++++++++++++++++++++++---------------- 2 files changed, 232 insertions(+), 120 deletions(-) diff --git a/include/sniffer.h b/include/sniffer.h index 854dabd..d56aeba 100644 --- a/include/sniffer.h +++ b/include/sniffer.h @@ -45,6 +45,7 @@ namespace Tins { class SnifferIterator; + class SnifferConfigurator; /** * \class BaseSniffer @@ -238,17 +239,16 @@ namespace Tins { * Default constructor. */ BaseSniffer(); - - /** - * \brief Initialices this BaseSniffer. - * - * \param phandle The pcap handle to be used for sniffing. - * \param filter The pcap filter which will be applied to the - * stream. - * \param if_mask The interface's subnet mask. If 0 is provided, - * then some IP broadcast tests won't work correctly. - */ - void init(pcap_t *phandle, const std::string &filter, bpf_u_int32 if_mask); + + void set_pcap_handle(pcap_t* const pcap_handle); + + pcap_t* get_pcap_handle(); + + const pcap_t* get_pcap_handle() const; + + void set_if_mask(bpf_u_int32 if_mask); + + bpf_u_int32 get_if_mask() const; private: BaseSniffer(const BaseSniffer&); BaseSniffer &operator=(const BaseSniffer&); @@ -264,10 +264,6 @@ namespace Tins { */ class Sniffer : public BaseSniffer { public: - enum promisc_type { - NON_PROMISC, - PROMISC - }; /** * Constructs an instance of Sniffer. @@ -281,25 +277,18 @@ namespace Tins { * \param filter A capture filter to be used on the sniffing session.(optional); * \param rfmon Indicates if the interface should be put in monitor mode.(optional); */ - Sniffer(const std::string &device, unsigned max_packet_size, - bool promisc = false, const std::string &filter = "", bool rfmon = false); + Sniffer(const std::string &device, const SnifferConfigurator& sniffer_configurator); - /** - * \brief Constructs an instance of Sniffer. - * - * The maximum capture size is set to 65535. By default the interface won't - * be put into promiscuous mode, and won't be put into monitor mode. - * - * \param device The device which will be sniffed. - * \param promisc Indicates if the interface should be put in promiscuous mode. - * \param filter A capture filter to be used on the sniffing session.(optional); - * \param rfmon Indicates if the interface should be put in monitor mode.(optional); - */ - Sniffer(const std::string &device, promisc_type promisc = NON_PROMISC, - const std::string &filter = "", bool rfmon = false); private: - void init_sniffer(const std::string &device, unsigned max_packet_size, - bool promisc = false, const std::string &filter = "", bool rfmon = false); + friend class SnifferConfigurator; + + void set_snap_len(unsigned snap_len); + + void set_buffer_size(unsigned buffer_size); + + void set_promisc_mode(bool promisc_enabled); + + void set_rfmon(bool rfmon_enabled); }; /** @@ -316,7 +305,7 @@ namespace Tins { * \param file_name The pcap file which will be parsed. * \param filter A capture filter to be used on the file.(optional); */ - FileSniffer(const std::string &file_name, const std::string &filter = ""); + FileSniffer(const std::string &file_name, const SnifferConfigurator& sniffer_configurator); }; template @@ -417,6 +406,45 @@ namespace Tins { Packet pkt; }; + class SnifferConfigurator { + public: + + SnifferConfigurator(); + + void set_snap_len(unsigned snap_len); + + void set_buffer_size(unsigned buffer_size); + + void set_promisc_mode(bool enabled); + + void set_filter(const std::string& filter); + + void set_rfmon(bool enabled); + + void set_timeout(unsigned timeout); + + protected: + friend class Sniffer; + friend class FileSniffer; + + void configure_sniffer(Sniffer& sniffer) const; + + void configure_sniffer(FileSniffer& sniffer) const; + + bool _has_snap_len; + unsigned _snap_len; + bool _has_buffer_size; + unsigned _buffer_size; + bool _has_promisc; + bool _promisc; + bool _has_rfmon; + bool _rfmon; + bool _has_filter; + std::string _filter; + bool _has_timeout; + unsigned _timeout; + }; + template void Tins::BaseSniffer::sniff_loop(Functor function, uint32_t max_packets) { for(iterator it = begin(); it != end(); ++it) { diff --git a/src/sniffer.cpp b/src/sniffer.cpp index aee0987..5ef6784 100644 --- a/src/sniffer.cpp +++ b/src/sniffer.cpp @@ -44,24 +44,41 @@ using std::runtime_error; namespace Tins { BaseSniffer::BaseSniffer() -: handle(0), mask(0), extract_raw(false) +: handle(0), mask(PCAP_NETMASK_UNKNOWN), extract_raw(false) { } -BaseSniffer::~BaseSniffer() { - if(handle) +BaseSniffer::~BaseSniffer() +{ + if (handle) { pcap_close(handle); + } } -void BaseSniffer::init(pcap_t *phandle, const std::string &filter, - bpf_u_int32 if_mask) +void BaseSniffer::set_pcap_handle(pcap_t* const pcap_handle) +{ + handle = pcap_handle; +} + +pcap_t* BaseSniffer::get_pcap_handle() +{ + return handle; +} + +const pcap_t* BaseSniffer::get_pcap_handle() const +{ + return handle; +} + +void BaseSniffer::set_if_mask(bpf_u_int32 if_mask) { - handle = phandle; mask = if_mask; - - if(!filter.empty() && !set_filter(filter)) - throw runtime_error("Invalid filter"); +} + +bpf_u_int32 BaseSniffer::get_if_mask() const +{ + return mask; } struct sniff_data { @@ -183,8 +200,9 @@ BaseSniffer::iterator BaseSniffer::end() { bool BaseSniffer::set_filter(const std::string &filter) { bpf_program prog; - if(pcap_compile(handle, &prog, filter.c_str(), 0, mask) == -1) + if(pcap_compile(handle, &prog, filter.c_str(), 0, mask) == -1) { return false; + } bool result = pcap_setfilter(handle, &prog) != -1; pcap_freecode(&prog); return result; @@ -196,94 +214,160 @@ void BaseSniffer::set_timeout(int ms) { // ****************************** Sniffer ****************************** -pcap_t * -pcap_open_live_extended(const char *source, int snaplen, int promisc, int to_ms, int rfmon, std::string& error) -{ - pcap_t *p; - char errbuf[PCAP_ERRBUF_SIZE]; - int status; - - p = pcap_create(source, errbuf); - if (p == NULL) { - error = errbuf; - return (NULL); - } - status = pcap_set_snaplen(p, snaplen); - if (status < 0) - goto fail; - status = pcap_set_promisc(p, promisc); - if (status < 0) - goto fail; - status = pcap_set_timeout(p, to_ms); - if (status < 0) - goto fail; - - #ifndef WIN32 - if(pcap_can_set_rfmon(p) == 1) { - status = pcap_set_rfmon(p, rfmon); - if (status < 0) - goto fail; - } - #endif // WIN32 - - status = pcap_activate(p); - if (status < 0) - goto fail; - return (p); - -fail: - std::ostringstream oss; - oss << source << ": " << pcap_geterr(p); - error = oss.str(); - pcap_close(p); - return (NULL); -} - -Sniffer::Sniffer(const string &device, unsigned max_packet_size, - bool promisc, const string &filter, bool rfmon) -{ - init_sniffer(device, max_packet_size, promisc, filter, rfmon); -} - -Sniffer::Sniffer(const std::string &device, promisc_type promisc, - const std::string &filter, bool rfmon) -{ - init_sniffer(device, 65535, promisc == PROMISC, filter, rfmon); -} - -void Sniffer::init_sniffer(const std::string &device, unsigned max_packet_size, - bool promisc, const std::string &filter, bool rfmon) +Sniffer::Sniffer(const string &device, const SnifferConfigurator& sniffer_configurator) { char error[PCAP_ERRBUF_SIZE]; - bpf_u_int32 ip, if_mask; - if (pcap_lookupnet(device.c_str(), &ip, &if_mask, error) == -1) { - ip = 0; - if_mask = 0; + pcap_t* phandle = pcap_create(device.c_str(), error); + if (!phandle) { + throw runtime_error(error); } - std::string string_error; - pcap_t *phandle = pcap_open_live_extended( - device.c_str(), - max_packet_size, - promisc, - 1000, - rfmon, - string_error - ); - if(!phandle) - throw runtime_error(string_error); - - init(phandle, filter, if_mask); + set_pcap_handle(phandle); + + // Set the netmask if we are able to find it. + bpf_u_int32 ip, if_mask; + if (pcap_lookupnet(device.c_str(), &ip, &if_mask, error) == 0) { + set_if_mask(if_mask); + } + + // Configure the sniffer + sniffer_configurator.configure_sniffer(*this); + + // Finally, activate the pcap. In case of error throw runtime_error + if (pcap_activate(get_pcap_handle()) < 0) { + throw std::runtime_error(pcap_geterr(get_pcap_handle())); + } +} + +void Sniffer::set_snap_len(unsigned snap_len) +{ + if (pcap_set_snaplen(get_pcap_handle(), snap_len)) { + throw std::runtime_error(pcap_geterr(get_pcap_handle())); + } +} + +void Sniffer::set_buffer_size(unsigned buffer_size) +{ + if (pcap_set_buffer_size(get_pcap_handle(), buffer_size)) { + throw std::runtime_error(pcap_geterr(get_pcap_handle())); + } +} + +void Sniffer::set_promisc_mode(bool promisc_enabled) +{ + if (pcap_set_promisc(get_pcap_handle(), promisc_enabled)) { + throw runtime_error(pcap_geterr(get_pcap_handle())); + } +} + +void Sniffer::set_rfmon(bool rfmon_enabled) +{ + #ifdef WIN32 + #warning "Setting monitor mode disabled in Windows platform" + #else + if (pcap_can_set_rfmon(get_pcap_handle()) == 1) { + if (pcap_set_rfmon(get_pcap_handle(), rfmon_enabled)) { + throw runtime_error(pcap_geterr(get_pcap_handle())); + } + } + #endif } // **************************** FileSniffer **************************** -FileSniffer::FileSniffer(const string &file_name, const string &filter) { +FileSniffer::FileSniffer(const string &file_name, const SnifferConfigurator& sniffer_configurator) { char error[PCAP_ERRBUF_SIZE]; pcap_t *phandle = pcap_open_offline(file_name.c_str(), error); - if(!phandle) + if(!phandle) { throw std::runtime_error(error); + } + set_pcap_handle(phandle); + + // Configure the sniffer + sniffer_configurator.configure_sniffer(*this); - init(phandle, filter, 0); } + +// ************************ SnifferConfigurator ************************ + +SnifferConfigurator::SnifferConfigurator() : + _has_snap_len(false), _snap_len(0), + _has_buffer_size(false), _buffer_size(0), + _has_promisc(false), _promisc(false), + _has_rfmon(false), _rfmon(false), + _has_filter(false), + _has_timeout(false), _timeout(0) +{ + +} + +void SnifferConfigurator::configure_sniffer(Sniffer& sniffer) const +{ + if (_has_snap_len) { + sniffer.set_snap_len(_snap_len); + } + if (_has_buffer_size) { + sniffer.set_buffer_size(_buffer_size); + } + if (_has_promisc) { + sniffer.set_promisc_mode(_promisc); + } + if (_has_rfmon) { + sniffer.set_rfmon(_rfmon); + } + if (_has_filter) { + if (!sniffer.set_filter(_filter)) { + throw std::runtime_error("Could not set the filter!"); + } + } + if (_has_timeout) { + sniffer.set_timeout(_timeout); + } +} + +void SnifferConfigurator::configure_sniffer(FileSniffer& sniffer) const +{ + if (_has_filter) { + if (!sniffer.set_filter(_filter)) { + throw std::runtime_error("Could not set the filter!"); + } + } +} + +void SnifferConfigurator::set_snap_len(unsigned snap_len) +{ + _has_snap_len = true; + _snap_len = snap_len; +} + +void SnifferConfigurator::set_buffer_size(unsigned buffer_size) +{ + _has_buffer_size = true; + _buffer_size = buffer_size; +} + +void SnifferConfigurator::set_promisc_mode(bool enabled) +{ + _has_promisc = true; + _promisc = enabled; +} + +void SnifferConfigurator::set_filter(const std::string& filter) +{ + _has_filter = true; + _filter = filter; +} + +void SnifferConfigurator::set_rfmon(bool enabled) +{ + _has_rfmon = true; + _rfmon = enabled; +} + +void SnifferConfigurator::set_timeout(unsigned timeout) +{ + _has_timeout = true; + _timeout = timeout; +} + } From 10c5013305b6a69e3a5ccb1d33fc39cb06e3a352 Mon Sep 17 00:00:00 2001 From: Santiago Alessandri Date: Fri, 29 Aug 2014 16:31:01 -0700 Subject: [PATCH 2/8] Created gitignore file to exclude build directory and config.h automatically generated file. --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..83f9de9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build/** +include/config.h \ No newline at end of file From 49f451ecd1c6d62e7f35d9367231ba503e30e22f Mon Sep 17 00:00:00 2001 From: Santiago Alessandri Date: Fri, 29 Aug 2014 16:35:13 -0700 Subject: [PATCH 3/8] Changed the name to SnifferConfiguration, it sounds better. --- include/sniffer.h | 12 ++++++------ src/sniffer.cpp | 28 ++++++++++++++-------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/include/sniffer.h b/include/sniffer.h index d56aeba..daee72b 100644 --- a/include/sniffer.h +++ b/include/sniffer.h @@ -45,7 +45,7 @@ namespace Tins { class SnifferIterator; - class SnifferConfigurator; + class SnifferConfiguration; /** * \class BaseSniffer @@ -277,10 +277,10 @@ namespace Tins { * \param filter A capture filter to be used on the sniffing session.(optional); * \param rfmon Indicates if the interface should be put in monitor mode.(optional); */ - Sniffer(const std::string &device, const SnifferConfigurator& sniffer_configurator); + Sniffer(const std::string &device, const SnifferConfiguration& configuration); private: - friend class SnifferConfigurator; + friend class SnifferConfiguration; void set_snap_len(unsigned snap_len); @@ -305,7 +305,7 @@ namespace Tins { * \param file_name The pcap file which will be parsed. * \param filter A capture filter to be used on the file.(optional); */ - FileSniffer(const std::string &file_name, const SnifferConfigurator& sniffer_configurator); + FileSniffer(const std::string &file_name, const SnifferConfiguration& configuration); }; template @@ -406,10 +406,10 @@ namespace Tins { Packet pkt; }; - class SnifferConfigurator { + class SnifferConfiguration { public: - SnifferConfigurator(); + SnifferConfiguration(); void set_snap_len(unsigned snap_len); diff --git a/src/sniffer.cpp b/src/sniffer.cpp index 5ef6784..2ac25f3 100644 --- a/src/sniffer.cpp +++ b/src/sniffer.cpp @@ -214,7 +214,7 @@ void BaseSniffer::set_timeout(int ms) { // ****************************** Sniffer ****************************** -Sniffer::Sniffer(const string &device, const SnifferConfigurator& sniffer_configurator) +Sniffer::Sniffer(const string &device, const SnifferConfiguration& configuration) { char error[PCAP_ERRBUF_SIZE]; pcap_t* phandle = pcap_create(device.c_str(), error); @@ -230,7 +230,7 @@ Sniffer::Sniffer(const string &device, const SnifferConfigurator& sniffer_config } // Configure the sniffer - sniffer_configurator.configure_sniffer(*this); + configuration.configure_sniffer(*this); // Finally, activate the pcap. In case of error throw runtime_error if (pcap_activate(get_pcap_handle()) < 0) { @@ -275,7 +275,7 @@ void Sniffer::set_rfmon(bool rfmon_enabled) // **************************** FileSniffer **************************** -FileSniffer::FileSniffer(const string &file_name, const SnifferConfigurator& sniffer_configurator) { +FileSniffer::FileSniffer(const string &file_name, const SnifferConfiguration& configuration) { char error[PCAP_ERRBUF_SIZE]; pcap_t *phandle = pcap_open_offline(file_name.c_str(), error); if(!phandle) { @@ -284,13 +284,13 @@ FileSniffer::FileSniffer(const string &file_name, const SnifferConfigurator& sni set_pcap_handle(phandle); // Configure the sniffer - sniffer_configurator.configure_sniffer(*this); + configuration.configure_sniffer(*this); } -// ************************ SnifferConfigurator ************************ +// ************************ SnifferConfiguration ************************ -SnifferConfigurator::SnifferConfigurator() : +SnifferConfiguration::SnifferConfiguration() : _has_snap_len(false), _snap_len(0), _has_buffer_size(false), _buffer_size(0), _has_promisc(false), _promisc(false), @@ -301,7 +301,7 @@ SnifferConfigurator::SnifferConfigurator() : } -void SnifferConfigurator::configure_sniffer(Sniffer& sniffer) const +void SnifferConfiguration::configure_sniffer(Sniffer& sniffer) const { if (_has_snap_len) { sniffer.set_snap_len(_snap_len); @@ -325,7 +325,7 @@ void SnifferConfigurator::configure_sniffer(Sniffer& sniffer) const } } -void SnifferConfigurator::configure_sniffer(FileSniffer& sniffer) const +void SnifferConfiguration::configure_sniffer(FileSniffer& sniffer) const { if (_has_filter) { if (!sniffer.set_filter(_filter)) { @@ -334,37 +334,37 @@ void SnifferConfigurator::configure_sniffer(FileSniffer& sniffer) const } } -void SnifferConfigurator::set_snap_len(unsigned snap_len) +void SnifferConfiguration::set_snap_len(unsigned snap_len) { _has_snap_len = true; _snap_len = snap_len; } -void SnifferConfigurator::set_buffer_size(unsigned buffer_size) +void SnifferConfiguration::set_buffer_size(unsigned buffer_size) { _has_buffer_size = true; _buffer_size = buffer_size; } -void SnifferConfigurator::set_promisc_mode(bool enabled) +void SnifferConfiguration::set_promisc_mode(bool enabled) { _has_promisc = true; _promisc = enabled; } -void SnifferConfigurator::set_filter(const std::string& filter) +void SnifferConfiguration::set_filter(const std::string& filter) { _has_filter = true; _filter = filter; } -void SnifferConfigurator::set_rfmon(bool enabled) +void SnifferConfiguration::set_rfmon(bool enabled) { _has_rfmon = true; _rfmon = enabled; } -void SnifferConfigurator::set_timeout(unsigned timeout) +void SnifferConfiguration::set_timeout(unsigned timeout) { _has_timeout = true; _timeout = timeout; From 039b41cb767c2e4ea3fb9efd2e06404ecd1557c8 Mon Sep 17 00:00:00 2001 From: Santiago Alessandri Date: Fri, 29 Aug 2014 18:20:15 -0700 Subject: [PATCH 4/8] Updated the examples to work with the new Sniffer constructor using the SnifferConfiguration object. --- examples/arpmonitor.cpp | 8 ++++++-- examples/beacon_display.cpp | 6 +++++- examples/dns_queries.cpp | 7 ++++--- examples/dns_spoof.cpp | 7 ++++--- examples/dns_stats.cpp | 6 ++++-- examples/portscan.cpp | 4 +++- examples/traceroute.cpp | 10 ++++++---- examples/wps_detect.cpp | 6 +++++- tests/CMakeLists.txt | 1 + 9 files changed, 38 insertions(+), 17 deletions(-) diff --git a/examples/arpmonitor.cpp b/examples/arpmonitor.cpp index 7d9a527..16655ff 100644 --- a/examples/arpmonitor.cpp +++ b/examples/arpmonitor.cpp @@ -87,10 +87,14 @@ int main(int argc, char *argv[]) return 1; } arp_monitor monitor; + // Sniffer configuration + SnifferConfiguration config; + config.set_promisc_mode(true); + config.set_filter("arp"); + // Sniff on the provided interface in promiscuous mode - Sniffer sniffer(argv[1], Sniffer::PROMISC); + Sniffer sniffer(argv[1], config); // Only capture arp packets - sniffer.set_filter("arp"); monitor.run(sniffer); } diff --git a/examples/beacon_display.cpp b/examples/beacon_display.cpp index 37e9f8a..042d7a0 100644 --- a/examples/beacon_display.cpp +++ b/examples/beacon_display.cpp @@ -47,7 +47,11 @@ private: }; void BeaconSniffer::run(const std::string &iface) { - Sniffer sniffer(iface, Sniffer::PROMISC, "type mgt subtype beacon", true); + SnifferConfiguration config; + config.set_promisc_mode(true); + config.set_filter("type mgt subtype beacon"); + config.set_rfmon(true); + Sniffer sniffer(iface, config); sniffer.sniff_loop(make_sniffer_handler(this, &BeaconSniffer::callback)); } diff --git a/examples/dns_queries.cpp b/examples/dns_queries.cpp index 8327c7c..05d9412 100644 --- a/examples/dns_queries.cpp +++ b/examples/dns_queries.cpp @@ -55,10 +55,11 @@ int main(int argc, char *argv[]) return 1; } // Sniff on the provided interface in promiscuos mode - Sniffer sniffer(argv[1], Sniffer::PROMISC); - + SnifferConfiguration config; + config.set_promisc_mode(true); // Only capture udp packets sent to port 53 - sniffer.set_filter("udp and dst port 53"); + config.set_filter("udp and dst port 53"); + Sniffer sniffer(argv[1], config); // Start the capture sniffer.sniff_loop(callback); diff --git a/examples/dns_spoof.cpp b/examples/dns_spoof.cpp index f44e6cd..ace62af 100644 --- a/examples/dns_spoof.cpp +++ b/examples/dns_spoof.cpp @@ -90,10 +90,11 @@ int main(int argc, char *argv[]) return 1; } // Sniff on the provided interface in promiscuos mode - Sniffer sniffer(argv[1], Sniffer::PROMISC); - + SnifferConfiguration config; + config.set_promisc_mode(true); // Only capture udp packets sent to port 53 - sniffer.set_filter("udp and dst port 53"); + config.set_filter("udp and dst port 53"); + Sniffer sniffer(argv[1], config); // All packets will be sent through the provided interface sender.default_interface(argv[1]); diff --git a/examples/dns_stats.cpp b/examples/dns_stats.cpp index f1ed23a..0a23d33 100644 --- a/examples/dns_stats.cpp +++ b/examples/dns_stats.cpp @@ -166,8 +166,10 @@ int main(int argc, char *argv[]) { return 1; } try { - Sniffer sniffer(argv[1], Sniffer::PROMISC); - sniffer.set_filter("udp and port 53"); + SnifferConfiguration config; + config.set_promisc_mode(true); + config.set_filter("udp and dst port 53"); + Sniffer sniffer(argv[1], config); dns_monitor monitor; std::thread thread( [&]() { diff --git a/examples/portscan.cpp b/examples/portscan.cpp index ceb74da..af8550d 100644 --- a/examples/portscan.cpp +++ b/examples/portscan.cpp @@ -117,7 +117,9 @@ void scan(int argc, char *argv[]) { cout << "Sniffing on interface: " << iface.name() << endl; // 300 bytes are enough to receive SYNs and RSTs. - Sniffer sniffer(iface.name(), 300); + SnifferConfiguration config; + config.set_snap_len(300); + Sniffer sniffer(iface.name(), config); sniffer_data data(&sniffer, argv[1]); pthread_t thread; // Launch our sniff thread. diff --git a/examples/traceroute.cpp b/examples/traceroute.cpp index a04714e..7a39016 100644 --- a/examples/traceroute.cpp +++ b/examples/traceroute.cpp @@ -47,10 +47,12 @@ public: result_type trace() { // ICMPs that aren't sent from us. - Sniffer sniffer( - iface.name(), 500, false, - "ip proto \\icmp and not src host " + iface.addresses().ip_addr.to_string() - ); + SnifferConfiguration config; + config.set_snap_len(500); + config.set_promisc_mode(false); + config.set_filter( + "ip proto \\icmp and not src host " + iface.addresses().ip_addr.to_string()); + Sniffer sniffer(iface.name(), config); PacketSender sender; // Create our handler diff --git a/examples/wps_detect.cpp b/examples/wps_detect.cpp index 6f690ff..c87d435 100644 --- a/examples/wps_detect.cpp +++ b/examples/wps_detect.cpp @@ -69,6 +69,10 @@ int main(int argc, char *argv[]) { return 1; } // Only sniff beacons - Sniffer sniffer(argv[1], 2000, true, "wlan type mgt subtype beacon"); + SnifferConfiguration config; + config.set_snap_len(2000); + config.set_promisc_mode(true); + config.set_filter("wlan type mgt subtype beacon"); + Sniffer sniffer(argv[1], config); sniffer.sniff_loop(handler); } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 0b7b64c..9b20d87 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,5 +1,6 @@ FIND_PACKAGE(GTest) IF(GTEST_FOUND) + INCLUDE_DIRECTORIES(${GTEST_INCLUDE_DIRS}) ADD_SUBDIRECTORY(src) ELSE(GTEST_FOUND) MESSAGE(WARNING "Google test not found. Disabling tests.") From 2b6a079980ba14e86976e92c307f7c623e67ca55 Mon Sep 17 00:00:00 2001 From: Santiago Alessandri Date: Sat, 30 Aug 2014 17:29:30 -0700 Subject: [PATCH 5/8] Added back the original constructors of the Sniffer and FileSniffer to keep compatibility. Tagged them as deprecated, though. Changed the default if_mask to 0 as PCAP_NETMASK_UNKNOWN is still not present in all versions of pcap. Snap length is always set and it's default value is of 65535 not 0. --- include/sniffer.h | 46 +++++++++++++++++++++++-- src/sniffer.cpp | 88 +++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 126 insertions(+), 8 deletions(-) diff --git a/include/sniffer.h b/include/sniffer.h index daee72b..7f371cb 100644 --- a/include/sniffer.h +++ b/include/sniffer.h @@ -264,8 +264,24 @@ namespace Tins { */ class Sniffer : public BaseSniffer { public: + /** + * \deprecated + */ + enum promisc_type { + NON_PROMISC, + PROMISC + }; /** + * Constructs an instance of Sniffer. + * + * \param device The device which will be sniffed. + * \param configuration The configuration object to use to setup the sniffer. + */ + Sniffer(const std::string &device, const SnifferConfiguration& configuration); + + /** + * \deprecated * Constructs an instance of Sniffer. * * By default the interface won't be put into promiscuous mode, and won't @@ -277,7 +293,23 @@ namespace Tins { * \param filter A capture filter to be used on the sniffing session.(optional); * \param rfmon Indicates if the interface should be put in monitor mode.(optional); */ - Sniffer(const std::string &device, const SnifferConfiguration& configuration); + Sniffer(const std::string &device, unsigned max_packet_size, + bool promisc = false, const std::string &filter = "", bool rfmon = false); + + /** + * \deprecaetd + * \brief Constructs an instance of Sniffer. + * + * The maximum capture size is set to 65535. By default the interface won't + * be put into promiscuous mode, and won't be put into monitor mode. + * + * \param device The device which will be sniffed. + * \param promisc Indicates if the interface should be put in promiscuous mode. + * \param filter A capture filter to be used on the sniffing session.(optional); + * \param rfmon Indicates if the interface should be put in monitor mode.(optional); + */ + Sniffer(const std::string &device, promisc_type promisc = NON_PROMISC, + const std::string &filter = "", bool rfmon = false); private: friend class SnifferConfiguration; @@ -306,6 +338,15 @@ namespace Tins { * \param filter A capture filter to be used on the file.(optional); */ FileSniffer(const std::string &file_name, const SnifferConfiguration& configuration); + + /** + * \deprecated + * + * \brief Constructs an instance of FileSniffer. + * \param file_name The pcap file which will be parsed. + * \param filter A capture filter to be used on the file.(optional); + */ + FileSniffer(const std::string &file_name, const std::string &filter = ""); }; template @@ -409,6 +450,8 @@ namespace Tins { class SnifferConfiguration { public: + static const unsigned DEFAULT_SNAP_LEN; + SnifferConfiguration(); void set_snap_len(unsigned snap_len); @@ -431,7 +474,6 @@ namespace Tins { void configure_sniffer(FileSniffer& sniffer) const; - bool _has_snap_len; unsigned _snap_len; bool _has_buffer_size; unsigned _buffer_size; diff --git a/src/sniffer.cpp b/src/sniffer.cpp index 2ac25f3..3ec3829 100644 --- a/src/sniffer.cpp +++ b/src/sniffer.cpp @@ -44,7 +44,7 @@ using std::runtime_error; namespace Tins { BaseSniffer::BaseSniffer() -: handle(0), mask(PCAP_NETMASK_UNKNOWN), extract_raw(false) +: handle(0), mask(0), extract_raw(false) { } @@ -238,6 +238,67 @@ Sniffer::Sniffer(const string &device, const SnifferConfiguration& configuration } } +Sniffer::Sniffer(const std::string &device, unsigned max_packet_size, bool promisc, + const std::string &filter, bool rfmon) +{ + SnifferConfiguration config; + config.set_snap_len(max_packet_size); + config.set_promisc_mode(promisc); + config.set_filter(filter); + config.set_rfmon(rfmon); + + char error[PCAP_ERRBUF_SIZE]; + pcap_t* phandle = pcap_create(device.c_str(), error); + if (!phandle) { + throw runtime_error(error); + } + set_pcap_handle(phandle); + + // Set the netmask if we are able to find it. + bpf_u_int32 ip, if_mask; + if (pcap_lookupnet(device.c_str(), &ip, &if_mask, error) == 0) { + set_if_mask(if_mask); + } + + // Configure the sniffer + config.configure_sniffer(*this); + + // Finally, activate the pcap. In case of error throw runtime_error + if (pcap_activate(get_pcap_handle()) < 0) { + throw std::runtime_error(pcap_geterr(get_pcap_handle())); + } +} + +Sniffer::Sniffer(const std::string &device, promisc_type promisc, const std::string &filter, + bool rfmon) +{ + SnifferConfiguration config; + config.set_promisc_mode(promisc == PROMISC); + config.set_filter(filter); + config.set_rfmon(rfmon); + + char error[PCAP_ERRBUF_SIZE]; + pcap_t* phandle = pcap_create(device.c_str(), error); + if (!phandle) { + throw runtime_error(error); + } + set_pcap_handle(phandle); + + // Set the netmask if we are able to find it. + bpf_u_int32 ip, if_mask; + if (pcap_lookupnet(device.c_str(), &ip, &if_mask, error) == 0) { + set_if_mask(if_mask); + } + + // Configure the sniffer + config.configure_sniffer(*this); + + // Finally, activate the pcap. In case of error throw runtime_error + if (pcap_activate(get_pcap_handle()) < 0) { + throw std::runtime_error(pcap_geterr(get_pcap_handle())); + } +} + void Sniffer::set_snap_len(unsigned snap_len) { if (pcap_set_snaplen(get_pcap_handle(), snap_len)) { @@ -288,10 +349,28 @@ FileSniffer::FileSniffer(const string &file_name, const SnifferConfiguration& co } +FileSniffer::FileSniffer(const std::string &file_name, const std::string &filter) +{ + SnifferConfiguration config; + config.set_filter(filter); + + char error[PCAP_ERRBUF_SIZE]; + pcap_t *phandle = pcap_open_offline(file_name.c_str(), error); + if(!phandle) { + throw std::runtime_error(error); + } + set_pcap_handle(phandle); + + // Configure the sniffer + config.configure_sniffer(*this); +} + // ************************ SnifferConfiguration ************************ +const unsigned SnifferConfiguration::DEFAULT_SNAP_LEN = 65535; + SnifferConfiguration::SnifferConfiguration() : - _has_snap_len(false), _snap_len(0), + _snap_len(DEFAULT_SNAP_LEN), _has_buffer_size(false), _buffer_size(0), _has_promisc(false), _promisc(false), _has_rfmon(false), _rfmon(false), @@ -303,9 +382,7 @@ SnifferConfiguration::SnifferConfiguration() : void SnifferConfiguration::configure_sniffer(Sniffer& sniffer) const { - if (_has_snap_len) { - sniffer.set_snap_len(_snap_len); - } + sniffer.set_snap_len(_snap_len); if (_has_buffer_size) { sniffer.set_buffer_size(_buffer_size); } @@ -336,7 +413,6 @@ void SnifferConfiguration::configure_sniffer(FileSniffer& sniffer) const void SnifferConfiguration::set_snap_len(unsigned snap_len) { - _has_snap_len = true; _snap_len = snap_len; } From d820b0d19daab5d927fc0f5856811bf8da71805f Mon Sep 17 00:00:00 2001 From: Matias Fontanini Date: Sat, 30 Aug 2014 23:35:05 -0300 Subject: [PATCH 6/8] Added documentation for SnifferConfiguration. --- include/sniffer.h | 57 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 55 insertions(+), 2 deletions(-) diff --git a/include/sniffer.h b/include/sniffer.h index 7f371cb..71e9955 100644 --- a/include/sniffer.h +++ b/include/sniffer.h @@ -447,25 +447,78 @@ namespace Tins { Packet pkt; }; + /** + * \class SnifferConfiguration + * \brief Represents the configuration of a BaseSniffer object. + * + * This class can be used as an easy way to configure a Sniffer + * or FileSniffer object. + * + * It can be used by constructing an object of this type, + * setting the desired values and then passing it to the + * Sniffer or FileSniffer object's constructor. + * + * For example: + * + * \code + * // Initialize the configuration. + * SnifferConfiguration config; + * config.set_filter("ip and port 80"); + * config.set_promisc_mode(true); + * + * // Use it on a Sniffer object. + * Sniffer sniffer("eth0", config); + * \endcode + */ class SnifferConfiguration { public: - + /** + * \brief The default snapshot length. + * + * This is 65535 by default. + */ static const unsigned DEFAULT_SNAP_LEN; + /** + * Default constructs a SnifferConfiguration. + */ SnifferConfiguration(); + /** + * Sets the snapshot length option. + * \param snap_len The snapshot length to be set. + */ void set_snap_len(unsigned snap_len); + /** + * Sets the buffer size option. + * \param buffer_size The buffer size to be set. + */ void set_buffer_size(unsigned buffer_size); + /** + * Sets the promiscuous mode option. + * \param enabled The promiscuous mode value. + */ void set_promisc_mode(bool enabled); + /** + * Sets a pcap filter to use on the sniffer. + * \param filter The pcap filter to be used. + */ void set_filter(const std::string& filter); + /** + * Sets the rfmon option. + * \param enabled The rfmon option value. + */ void set_rfmon(bool enabled); + /** + * Sets the timeout option. + * \param timeout The timeout to be set. + */ void set_timeout(unsigned timeout); - protected: friend class Sniffer; friend class FileSniffer; From 83ced826d0e0e2f5264f9690687485c74dad02bb Mon Sep 17 00:00:00 2001 From: Matias Fontanini Date: Sat, 30 Aug 2014 23:54:18 -0300 Subject: [PATCH 7/8] Fixed using pcap_compile on a not-yet activated pcap handle. --- include/sniffer.h | 5 ++-- src/sniffer.cpp | 66 ++++++++++++++++++++++++++++------------------- 2 files changed, 42 insertions(+), 29 deletions(-) diff --git a/include/sniffer.h b/include/sniffer.h index 71e9955..2563b51 100644 --- a/include/sniffer.h +++ b/include/sniffer.h @@ -523,9 +523,10 @@ namespace Tins { friend class Sniffer; friend class FileSniffer; - void configure_sniffer(Sniffer& sniffer) const; + void configure_sniffer_pre_activation(Sniffer& sniffer) const; + void configure_sniffer_pre_activation(FileSniffer& sniffer) const; - void configure_sniffer(FileSniffer& sniffer) const; + void configure_sniffer_post_activation(Sniffer& sniffer) const; unsigned _snap_len; bool _has_buffer_size; diff --git a/src/sniffer.cpp b/src/sniffer.cpp index 3ec3829..d8ced2a 100644 --- a/src/sniffer.cpp +++ b/src/sniffer.cpp @@ -201,6 +201,7 @@ BaseSniffer::iterator BaseSniffer::end() { bool BaseSniffer::set_filter(const std::string &filter) { bpf_program prog; if(pcap_compile(handle, &prog, filter.c_str(), 0, mask) == -1) { + std::cout << pcap_geterr(handle) << std::endl; return false; } bool result = pcap_setfilter(handle, &prog) != -1; @@ -229,23 +230,26 @@ Sniffer::Sniffer(const string &device, const SnifferConfiguration& configuration set_if_mask(if_mask); } - // Configure the sniffer - configuration.configure_sniffer(*this); + // Configure the sniffer's attributes prior to activation. + configuration.configure_sniffer_pre_activation(*this); // Finally, activate the pcap. In case of error throw runtime_error if (pcap_activate(get_pcap_handle()) < 0) { throw std::runtime_error(pcap_geterr(get_pcap_handle())); } + + // Configure the sniffer's attributes after activation. + configuration.configure_sniffer_post_activation(*this); } Sniffer::Sniffer(const std::string &device, unsigned max_packet_size, bool promisc, const std::string &filter, bool rfmon) { - SnifferConfiguration config; - config.set_snap_len(max_packet_size); - config.set_promisc_mode(promisc); - config.set_filter(filter); - config.set_rfmon(rfmon); + SnifferConfiguration configuration; + configuration.set_snap_len(max_packet_size); + configuration.set_promisc_mode(promisc); + configuration.set_filter(filter); + configuration.set_rfmon(rfmon); char error[PCAP_ERRBUF_SIZE]; pcap_t* phandle = pcap_create(device.c_str(), error); @@ -260,22 +264,25 @@ Sniffer::Sniffer(const std::string &device, unsigned max_packet_size, bool promi set_if_mask(if_mask); } - // Configure the sniffer - config.configure_sniffer(*this); + // Configure the sniffer's attributes prior to activation. + configuration.configure_sniffer_pre_activation(*this); // Finally, activate the pcap. In case of error throw runtime_error if (pcap_activate(get_pcap_handle()) < 0) { throw std::runtime_error(pcap_geterr(get_pcap_handle())); } + + // Configure the sniffer's attributes after activation. + configuration.configure_sniffer_post_activation(*this); } Sniffer::Sniffer(const std::string &device, promisc_type promisc, const std::string &filter, bool rfmon) { - SnifferConfiguration config; - config.set_promisc_mode(promisc == PROMISC); - config.set_filter(filter); - config.set_rfmon(rfmon); + SnifferConfiguration configuration; + configuration.set_promisc_mode(promisc == PROMISC); + configuration.set_filter(filter); + configuration.set_rfmon(rfmon); char error[PCAP_ERRBUF_SIZE]; pcap_t* phandle = pcap_create(device.c_str(), error); @@ -290,13 +297,16 @@ Sniffer::Sniffer(const std::string &device, promisc_type promisc, const std::str set_if_mask(if_mask); } - // Configure the sniffer - config.configure_sniffer(*this); + // Configure the sniffer's attributes prior to activation. + configuration.configure_sniffer_pre_activation(*this); // Finally, activate the pcap. In case of error throw runtime_error if (pcap_activate(get_pcap_handle()) < 0) { throw std::runtime_error(pcap_geterr(get_pcap_handle())); } + + // Configure the sniffer's attributes after activation. + configuration.configure_sniffer_post_activation(*this); } void Sniffer::set_snap_len(unsigned snap_len) @@ -322,9 +332,7 @@ void Sniffer::set_promisc_mode(bool promisc_enabled) void Sniffer::set_rfmon(bool rfmon_enabled) { - #ifdef WIN32 - #warning "Setting monitor mode disabled in Windows platform" - #else + #ifndef WIN32 if (pcap_can_set_rfmon(get_pcap_handle()) == 1) { if (pcap_set_rfmon(get_pcap_handle(), rfmon_enabled)) { throw runtime_error(pcap_geterr(get_pcap_handle())); @@ -345,7 +353,7 @@ FileSniffer::FileSniffer(const string &file_name, const SnifferConfiguration& co set_pcap_handle(phandle); // Configure the sniffer - configuration.configure_sniffer(*this); + configuration.configure_sniffer_pre_activation(*this); } @@ -362,7 +370,7 @@ FileSniffer::FileSniffer(const std::string &file_name, const std::string &filter set_pcap_handle(phandle); // Configure the sniffer - config.configure_sniffer(*this); + config.configure_sniffer_pre_activation(*this); } // ************************ SnifferConfiguration ************************ @@ -380,7 +388,7 @@ SnifferConfiguration::SnifferConfiguration() : } -void SnifferConfiguration::configure_sniffer(Sniffer& sniffer) const +void SnifferConfiguration::configure_sniffer_pre_activation(Sniffer& sniffer) const { sniffer.set_snap_len(_snap_len); if (_has_buffer_size) { @@ -392,17 +400,12 @@ void SnifferConfiguration::configure_sniffer(Sniffer& sniffer) const if (_has_rfmon) { sniffer.set_rfmon(_rfmon); } - if (_has_filter) { - if (!sniffer.set_filter(_filter)) { - throw std::runtime_error("Could not set the filter!"); - } - } if (_has_timeout) { sniffer.set_timeout(_timeout); } } -void SnifferConfiguration::configure_sniffer(FileSniffer& sniffer) const +void SnifferConfiguration::configure_sniffer_pre_activation(FileSniffer& sniffer) const { if (_has_filter) { if (!sniffer.set_filter(_filter)) { @@ -411,6 +414,15 @@ void SnifferConfiguration::configure_sniffer(FileSniffer& sniffer) const } } +void SnifferConfiguration::configure_sniffer_post_activation(Sniffer& sniffer) const +{ + if (_has_filter) { + if (!sniffer.set_filter(_filter)) { + throw std::runtime_error("Could not set the filter! "); + } + } +} + void SnifferConfiguration::set_snap_len(unsigned snap_len) { _snap_len = snap_len; From b6fdba007740ddb5b6effd72cd64c02f50b0446d Mon Sep 17 00:00:00 2001 From: Matias Fontanini Date: Sun, 31 Aug 2014 18:34:52 -0300 Subject: [PATCH 8/8] The timeout Sniffer option is set to 1000 by default. --- include/sniffer.h | 8 +++++++- src/sniffer.cpp | 8 +++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/include/sniffer.h b/include/sniffer.h index 2563b51..d31fe4e 100644 --- a/include/sniffer.h +++ b/include/sniffer.h @@ -479,6 +479,13 @@ namespace Tins { */ static const unsigned DEFAULT_SNAP_LEN; + /** + * \brief The default timeout. + * + * This is 1000 by default. + */ + static const unsigned DEFAULT_TIMEOUT; + /** * Default constructs a SnifferConfiguration. */ @@ -537,7 +544,6 @@ namespace Tins { bool _rfmon; bool _has_filter; std::string _filter; - bool _has_timeout; unsigned _timeout; }; diff --git a/src/sniffer.cpp b/src/sniffer.cpp index d8ced2a..4a7355b 100644 --- a/src/sniffer.cpp +++ b/src/sniffer.cpp @@ -376,6 +376,7 @@ FileSniffer::FileSniffer(const std::string &file_name, const std::string &filter // ************************ SnifferConfiguration ************************ const unsigned SnifferConfiguration::DEFAULT_SNAP_LEN = 65535; +const unsigned SnifferConfiguration::DEFAULT_TIMEOUT = 1000; SnifferConfiguration::SnifferConfiguration() : _snap_len(DEFAULT_SNAP_LEN), @@ -383,7 +384,7 @@ SnifferConfiguration::SnifferConfiguration() : _has_promisc(false), _promisc(false), _has_rfmon(false), _rfmon(false), _has_filter(false), - _has_timeout(false), _timeout(0) + _timeout(DEFAULT_TIMEOUT) { } @@ -391,6 +392,7 @@ SnifferConfiguration::SnifferConfiguration() : void SnifferConfiguration::configure_sniffer_pre_activation(Sniffer& sniffer) const { sniffer.set_snap_len(_snap_len); + sniffer.set_timeout(_timeout); if (_has_buffer_size) { sniffer.set_buffer_size(_buffer_size); } @@ -400,9 +402,6 @@ void SnifferConfiguration::configure_sniffer_pre_activation(Sniffer& sniffer) co if (_has_rfmon) { sniffer.set_rfmon(_rfmon); } - if (_has_timeout) { - sniffer.set_timeout(_timeout); - } } void SnifferConfiguration::configure_sniffer_pre_activation(FileSniffer& sniffer) const @@ -454,7 +453,6 @@ void SnifferConfiguration::set_rfmon(bool enabled) void SnifferConfiguration::set_timeout(unsigned timeout) { - _has_timeout = true; _timeout = timeout; }