diff --git a/examples/Makefile.in b/examples/Makefile.in index bceb7bc..c502d12 100644 --- a/examples/Makefile.in +++ b/examples/Makefile.in @@ -1,7 +1,7 @@ CXX=@CXX@ CXXFLAGS=-Wall @CXXFLAGS@ LDFLAGS=-ltins -EXECUTABLES=arpspoofing arpmonitor portscan traceroute beacon_display dns_queries dns_spoof wps_detect +EXECUTABLES=arpspoofing arpmonitor portscan traceroute beacon_display dns_queries dns_spoof dns_stats wps_detect all: $(EXECUTABLES) @@ -21,6 +21,9 @@ dns_queries: dns_spoof: $(CXX) dns_spoof.cpp -o dns_spoof -std=c++0x $(CXXFLAGS) $(LDFLAGS) +dns_stats: + $(CXX) dns_stats.cpp -o dns_stats -std=c++0x $(CXXFLAGS) $(LDFLAGS) -lpthread + beacon_display: $(CXX) beacon_display.cpp -o beacon_display $(CXXFLAGS) $(LDFLAGS) diff --git a/examples/dns_stats.cpp b/examples/dns_stats.cpp new file mode 100644 index 0000000..53a742a --- /dev/null +++ b/examples/dns_stats.cpp @@ -0,0 +1,160 @@ +#include +#include +#include +#include +#include +#include +#include + +using namespace Tins; + +// Holds the DNS response time statistics. The response time is +// represented using the Duration template parameter. +template +class statistics { +public: + using duration_type = Duration; + using locker_type = std::lock_guard; + + struct information { + duration_type average, worst; + size_t count; + }; + + statistics() + : m_duration(), m_worst(duration_type::min()), m_count() + { + + } + + void add_response_time(const duration_type& duration) + { + locker_type _(m_lock); + m_duration += duration; + m_count++; + m_worst = std::max(m_worst, duration); + } + + information get_information() const + { + locker_type _(m_lock); + if(m_count == 0) + return { }; + else + return { m_duration / m_count, m_worst, m_count }; + }; +private: + duration_type m_duration, m_worst; + size_t m_count; + mutable std::mutex m_lock; +}; + +// Sniffs and tracks DNS queries. When a matching DNS response is found, +// the response time is added to a statistics object. +// +// This class performs *no cleanup* on data associated with queries that +// weren't answered. +class dns_monitor { +public: + // The response times are measured in milliseconds + using duration_type = std::chrono::milliseconds; + // The statistics type used. + using statistics_type = statistics; + + void run(BaseSniffer& sniffer); + const statistics_type& stats() const { + return m_stats; + } +private: + using packet_info = std::tuple; + using clock_type = std::chrono::steady_clock; + using time_point_type = std::chrono::time_point; + + bool callback(const PDU& pdu); + static packet_info make_packet_info(const PDU& pdu, const DNS& dns); + + statistics_type m_stats; + std::map m_packet_info; +}; + +void dns_monitor::run(BaseSniffer& sniffer) +{ + sniffer.sniff_loop( + std::bind( + &dns_monitor::callback, + this, + std::placeholders::_1 + ) + ); +} + +bool dns_monitor::callback(const PDU& pdu) +{ + auto now = clock_type::now(); + auto dns = pdu.rfind_pdu().to(); + auto info = make_packet_info(pdu, dns); + // If it's a query, add the sniff time to our map. + if(dns.type() == DNS::QUERY) { + m_packet_info.insert( + std::make_pair(info, now) + ); + } + else { + // It's a response, we need to find the query in our map. + auto iter = m_packet_info.find(info); + if(iter != m_packet_info.end()) { + // We found the query, let's add the response time to the + // statistics object. + m_stats.add_response_time( + std::chrono::duration_cast(now - iter->second) + ); + // Forget about the query. + m_packet_info.erase(iter); + } + } + return true; +} + +// It is required that we can identify packets sent and received that +// hold the same DNS id as belonging to the same query. +// +// This function retrieves a tuple (addr, addr, id) that will achieve it. +auto dns_monitor::make_packet_info(const PDU& pdu, const DNS& dns) -> packet_info +{ + const auto& ip = pdu.rfind_pdu(); + return std::make_tuple( + // smallest address first + std::min(ip.src_addr(), ip.dst_addr()), + // largest address second + std::max(ip.src_addr(), ip.dst_addr()), + dns.id() + ); +} + +int main(int argc, char *argv[]) { + if(argc != 2) { + std::cout << "Usage: " << *argv << " \n"; + return 1; + } + try { + Sniffer sniffer(argv[1]); + sniffer.set_filter("udp and port 53"); + dns_monitor monitor; + std::thread thread( + [&]() { + monitor.run(sniffer); + } + ); + while(true) { + auto info = monitor.stats().get_information(); + std::cout << "\rAverage " << info.average.count() + << "ms. Worst: " << info.worst.count() << "ms. Count: " + << info.count; + std::cout.flush(); + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + } + catch(std::exception& ex) { + std::cout << "[-] Error: " << ex.what() << std::endl; + } +}