From 8f00b3df17db1a0f7458c9bf39f20da23bf1ac50 Mon Sep 17 00:00:00 2001 From: Christian Herdtweck Date: Fri, 22 May 2015 10:16:36 +0200 Subject: [PATCH] completed partial IPv6 compatibility in DNS; does retrieve and Cache IPv6 IPs also added test for this and updated cache test for future cases of cache structure changes --- doc/pingcheck_icmp_distributor.graphml | 24 ++++- src/CMakeLists.txt | 1 + src/dns/dnscache.cpp | 103 +++++++++++++----- src/dns/dnscache.h | 22 +++- src/dns/dnsipprotocol.cpp | 59 ++++++++++ src/dns/dnsipprotocol.h | 37 ++++++ src/dns/dnsmaster.cpp | 28 +----- src/dns/dnsmaster.h | 9 +-- src/dns/dnsresolver.cpp | 5 +- src/dns/dnsresolver.h | 1 - src/dns/ippseudoresolver.h | 3 +- src/dns/resolverbase.cpp | 17 ++- src/dns/resolverbase.h | 3 + src/host/pingerfactory.cpp | 3 +- src/host/pingprotocol.cpp | 15 +++ src/host/pingprotocol.h | 1 + src/host/pingscheduler.cpp | 9 +- src/icmp/icmppinger.cpp | 13 ++- test/CMakeLists.test_dns.txt | 1 + test/data/CMakeLists.txt | 2 +- test/data/dns_cache_compatibility_test.xml | 70 ++++++++++++ test/data/dns_cache_example.xml | 88 --------------- test/test_dns.cpp | 165 ++++++++++++++++++++++------ 23 files changed, 472 insertions(+), 207 deletions(-) create mode 100644 src/dns/dnsipprotocol.cpp create mode 100644 src/dns/dnsipprotocol.h create mode 100644 test/data/dns_cache_compatibility_test.xml delete mode 100644 test/data/dns_cache_example.xml diff --git a/doc/pingcheck_icmp_distributor.graphml b/doc/pingcheck_icmp_distributor.graphml index 7e532fa..2db6668 100644 --- a/doc/pingcheck_icmp_distributor.graphml +++ b/doc/pingcheck_icmp_distributor.graphml @@ -2559,7 +2559,7 @@ shared_ptrs - + @@ -3259,6 +3259,28 @@ shared_ptrs + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index efed741..10b4f12 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -63,6 +63,7 @@ set(SOURCES config/option/dnscachefileoption.cpp dns/timetolive.cpp dns/hostaddress.cpp + dns/dnsipprotocol.cpp dns/dnscache.cpp dns/resolverbase.cpp dns/dnsresolver.cpp diff --git a/src/dns/dnscache.cpp b/src/dns/dnscache.cpp index d7c8182..bf68129 100644 --- a/src/dns/dnscache.cpp +++ b/src/dns/dnscache.cpp @@ -208,33 +208,51 @@ void DnsCache::load_from_cachefile() // UPDATE // ----------------------------------------------------------------------------- -// warn if hostname is empty and remove trailing dot -std::string DnsCache::key_for_hostname(const std::string &hostname) const +/* + * warn if hostname is empty and remove trailing dot + * also warn if protocol is neither IPv4 nor IPv6 + */ +ip_map_key_type DnsCache::key_for_ips(const std::string &hostname, + const DnsIpProtocol &protocol) const { if (hostname.empty()) { GlobalLogger.info() << "DnsCache: empty host!"; - return ""; + return ip_map_key_type("", DNS_IPALL); + } + if (protocol == DNS_IPALL) + { + GlobalLogger.info() << "DnsCache: neither IPv4 nor v6!"; + return ip_map_key_type("", DNS_IPALL); } // check whether last character is a dot if (hostname.rfind('.') == hostname.length()-1) - return hostname.substr(0, hostname.length()-1); + return ip_map_key_type( hostname.substr(0, hostname.length()-1), + protocol ); else - return hostname; + return ip_map_key_type( hostname, + protocol ); } void DnsCache::update(const std::string &hostname, + const DnsIpProtocol &protocol, const HostAddressVec &new_ips) { - std::string key = key_for_hostname(hostname); + // check for valid input arguments + ip_map_key_type key = key_for_ips(hostname, protocol); + if ( key.first.empty() ) + return; + + // ensure that there is never IP and CNAME for the same host if ( !get_cname(hostname).Host.empty() ) - { // ensure that there is never IP and CNAME for the same host - GlobalLogger.info() << "DnsCache: Saving IPs for " << key + { + GlobalLogger.info() << "DnsCache: Saving IPs for " << key.first << " removes CNAME to " << get_cname(hostname).Host << "!"; update(hostname, Cname()); // overwrite with "empty" cname } + // ensure min ttl of MinTimeBetweenResolves HostAddressVec ips_checked; BOOST_FOREACH( const HostAddress &addr, new_ips ) @@ -242,7 +260,7 @@ void DnsCache::update(const std::string &hostname, if ( addr.get_ttl().get_value() < MinTimeBetweenResolves ) { GlobalLogger.info() << "DnsCache: Correcting TTL of IP for " - << hostname << " from " << addr.get_ttl().get_value() << "s to " + << key.first << " from " << addr.get_ttl().get_value() << "s to " << MinTimeBetweenResolves << "s because was too short"; ips_checked.push_back( HostAddress( addr.get_ip(), MinTimeBetweenResolves) ); @@ -251,8 +269,9 @@ void DnsCache::update(const std::string &hostname, ips_checked.push_back(addr); } + // write IPs into one log line stringstream log_temp; - log_temp << "DnsCache: update IPs for " << key << " to " + log_temp << "DnsCache: update IPs for " << key.first << " to " << ips_checked.size() << "-list: "; BOOST_FOREACH( const HostAddress &ip, ips_checked ) log_temp << ip.get_ip() << ", "; @@ -263,32 +282,61 @@ void DnsCache::update(const std::string &hostname, } +/* + * warn if hostname is empty and remove trailing dot + */ +cname_map_key_type DnsCache::key_for_cname(const std::string &hostname) const +{ + if (hostname.empty()) + { + GlobalLogger.info() << "DnsCache: empty host!"; + return ""; + } + + // check whether last character is a dot + if (hostname.rfind('.') == hostname.length()-1) + return hostname.substr(0, hostname.length()-1); + else + return hostname; +} + + void DnsCache::update(const std::string &hostname, const Cname &cname) { - std::string key = key_for_hostname(hostname); - if ( !get_ips(hostname).empty() ) - { // ensure that there is never IP and CNAME for the same host + // check for valid input arguments + cname_map_key_type key = key_for_cname(hostname); + if ( key.empty() ) + return; + + // ensure that there is never IP and CNAME for the same host + int n_ips = get_ips(hostname, DNS_IPv4).size() + + get_ips(hostname, DNS_IPv6).size(); + if ( n_ips > 0 ) + { + GlobalLogger.info() << "DnsCache: Saving IPs for " << key + << " removes CNAME to " << get_cname(hostname).Host << "!"; GlobalLogger.info() << "DnsCache: Saving CNAME for " << key - << " removes " << get_ips(hostname).size() << " IPs for same host!"; - update(hostname, HostAddressVec()); // overwrite with empty IP list + << " removes " << n_ips << " IPs for same host!"; + update(hostname, DNS_IPv4, HostAddressVec()); + update(hostname, DNS_IPv6, HostAddressVec()); } - // remove possible trailing dot from cname - Cname to_save = Cname(key_for_hostname(cname.Host), + // remove possible trailing dot from cname's target host + Cname to_save = Cname(key_for_cname(cname.Host), // implicit cast to string cname.Ttl); // ensure min ttl of MinTimeBetweenResolves if ( to_save.Ttl.get_value() < MinTimeBetweenResolves ) { GlobalLogger.info() << "DnsCache: Correcting TTL of CNAME of " - << hostname << " from " << to_save.Ttl.get_value() << "s to " + << key << " from " << to_save.Ttl.get_value() << "s to " << MinTimeBetweenResolves << "s because was too short"; to_save.Ttl = TimeToLive(MinTimeBetweenResolves); } GlobalLogger.notice() << "DnsCache: update CNAME for " << key - << " to " << to_save.Host; + << " to " << to_save.Host; CnameCache[key] = to_save; HasChanged = true; } @@ -302,9 +350,10 @@ void DnsCache::update(const std::string &hostname, * @returns empty list if no (up to date) ips for hostname in cache */ HostAddressVec DnsCache::get_ips(const std::string &hostname, + const DnsIpProtocol &protocol, const bool check_up_to_date) { - std::string key = key_for_hostname(hostname); + ip_map_key_type key = key_for_ips(hostname, protocol); HostAddressVec result = IpCache[key]; if (check_up_to_date) { @@ -325,7 +374,7 @@ HostAddressVec DnsCache::get_ips(const std::string &hostname, } result = result_up_to_date; } - /*GlobalLogger.debug() << "DnsCache: request IPs for " << key + /*GlobalLogger.debug() << "DnsCache: request IPs for " << key.first << " --> " << result.size() << "-list"; BOOST_FOREACH( const HostAddress &addr, result ) GlobalLogger.debug() << "DnsCache: " << addr.get_ip().to_string() @@ -340,7 +389,7 @@ HostAddressVec DnsCache::get_ips(const std::string &hostname, Cname DnsCache::get_cname(const std::string &hostname, const bool check_up_to_date) { - std::string key = key_for_hostname(hostname); + cname_map_key_type key = key_for_cname(hostname); Cname result_obj = CnameCache[key]; /*GlobalLogger.debug() << "DnsCache: request CNAME for " << key << " --> \"" << result_obj.Host << "\" (TTL " @@ -366,11 +415,12 @@ Cname DnsCache::get_cname(const std::string &hostname, // underlying assumption in this function: for a hostname, the cache has either // a list of IPs saved or a cname saved, but never both HostAddressVec DnsCache::get_ips_recursive(const std::string &hostname, + const DnsIpProtocol &protocol, const bool check_up_to_date) { std::string current_host = hostname; Cname current_cname; - HostAddressVec result = get_ips(current_host, check_up_to_date); + HostAddressVec result = get_ips(current_host, protocol, check_up_to_date); int n_recursions = 0; uint32_t min_cname_ttl = 0xffff; // largest possible unsigned 4-byte value int max_recursion_count = DnsMaster::get_instance() @@ -379,9 +429,10 @@ HostAddressVec DnsCache::get_ips_recursive(const std::string &hostname, { current_cname = get_cname(current_host, check_up_to_date); if (current_cname.Host.empty()) - break; + break; // no ips (since result.empty()) and no cname + // --> will return empty result - current_host = key_for_hostname(current_cname.Host); + current_host = current_cname.Host; if (++n_recursions >= max_recursion_count) { GlobalLogger.info() << "DnsCache: reached recursion limit of " @@ -393,7 +444,7 @@ HostAddressVec DnsCache::get_ips_recursive(const std::string &hostname, { min_cname_ttl = min(min_cname_ttl, current_cname.Ttl.get_updated_value()); - result = get_ips(current_host, check_up_to_date); + result = get_ips(current_host, protocol, check_up_to_date); } } diff --git a/src/dns/dnscache.h b/src/dns/dnscache.h index 9268c77..e1ed495 100644 --- a/src/dns/dnscache.h +++ b/src/dns/dnscache.h @@ -32,12 +32,15 @@ #include #include "host/pinger.h" // for IoserviceItem +#include "dns/dnsipprotocol.h" #include "dns/hostaddress.h" #include "dns/cname.h" typedef std::vector HostAddressVec; -typedef std::map ip_map_type; -typedef std::map cname_map_type; +typedef std::pair ip_map_key_type; +typedef std::map ip_map_type; +typedef std::string cname_map_key_type; +typedef std::map cname_map_type; // ----------------------------------------------------------------------------- // DnsCache @@ -56,15 +59,20 @@ public: ~DnsCache(); // accessed from ResolverBase subclasses - void update(const std::string &hostname, const HostAddressVec &new_ips); - void update(const std::string &hostname, const Cname &cname); + void update(const std::string &hostname, + const DnsIpProtocol &protocol, + const HostAddressVec &new_ips); + void update(const std::string &hostname, + const Cname &cname); // retrieval HostAddressVec get_ips(const std::string &hostname, + const DnsIpProtocol &protocol, const bool check_up_to_date=false); Cname get_cname(const std::string &hostname, - const bool check_up_to_date=false); + const bool check_up_to_date=false); HostAddressVec get_ips_recursive(const std::string &hostname, + const DnsIpProtocol &protocol, const bool check_up_to_date=false); std::string get_first_outdated_cname(const std::string &hostname, const uint32_t ttl_thresh); @@ -83,7 +91,9 @@ private: void schedule_save(const boost::system::error_code &error); void save_to_cachefile(); void load_from_cachefile(); - std::string key_for_hostname(const std::string &hostname) const; + ip_map_key_type key_for_ips(const std::string &hostname, + const DnsIpProtocol &protocol) const; + cname_map_key_type key_for_cname(const std::string &hostname) const; }; diff --git a/src/dns/dnsipprotocol.cpp b/src/dns/dnsipprotocol.cpp new file mode 100644 index 0000000..54ff4e7 --- /dev/null +++ b/src/dns/dnsipprotocol.cpp @@ -0,0 +1,59 @@ +/* + The software in this package is distributed under the GNU General + Public License version 2 (with a special exception described below). + + A copy of GNU General Public License (GPL) is included in this distribution, + in the file COPYING.GPL. + + As a special exception, if other files instantiate templates or use macros + or inline functions from this file, or you compile this file and link it + with other works to produce a work based on this file, this file + does not by itself cause the resulting work to be covered + by the GNU General Public License. + + However the source code for this file must still be made available + in accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work based + on this file might be covered by the GNU General Public License. + + Christian Herdtweck, Intra2net AG 2015 + */ + +#include "dns/dnsipprotocol.h" + +#include + +using I2n::Logger::GlobalLogger; + + +std::string to_string(const DnsIpProtocol &protocol) +{ + switch (protocol) + { + case DNS_IPv4: return "IPv4"; break; + case DNS_IPv6: return "IPv6"; break; + case DNS_IPALL: return "IPv4/6"; break; + default: GlobalLogger.warning() << "Unexpected protocol in to_string!"; + return "Unexpected Protocol"; break; + } +} + +DnsIpProtocol ping2dns_protocol(const PingProtocol& pprot) +{ + switch (pprot) + { + case PingProtocol_ICMP: return DNS_IPv4; break; + case PingProtocol_ICMPv6: return DNS_IPv6; break; + case PingProtocol_TCP: return DNS_IPv4; break; + case PingProtocol_TCP_IPv6: return DNS_IPv6; break; + default: + GlobalLogger.warning() + << "Unexpected ping protocol in ping2dns_protocol: " + << static_cast(pprot); + return DNS_IPALL; + break; + } +} + + diff --git a/src/dns/dnsipprotocol.h b/src/dns/dnsipprotocol.h new file mode 100644 index 0000000..fb0c4bf --- /dev/null +++ b/src/dns/dnsipprotocol.h @@ -0,0 +1,37 @@ +/* + The software in this package is distributed under the GNU General + Public License version 2 (with a special exception described below). + + A copy of GNU General Public License (GPL) is included in this distribution, + in the file COPYING.GPL. + + As a special exception, if other files instantiate templates or use macros + or inline functions from this file, or you compile this file and link it + with other works to produce a work based on this file, this file + does not by itself cause the resulting work to be covered + by the GNU General Public License. + + However the source code for this file must still be made available + in accordance with section (3) of the GNU General Public License. + + This exception does not invalidate any other reasons why a work based + on this file might be covered by the GNU General Public License. + + Christian Herdtweck, Intra2net AG 2015 + */ + +#ifndef DNS_IP_PROTOCOL_H +#define DNS_IP_PROTOCOL_H + +#include +#include "host/pingprotocol.h" + +typedef boost::net::dns::type_t DnsIpProtocol; +const DnsIpProtocol DNS_IPv4 = boost::net::dns::type_a; +const DnsIpProtocol DNS_IPv6 = boost::net::dns::type_a6; +const DnsIpProtocol DNS_IPALL = boost::net::dns::type_all; + +std::string to_string(const DnsIpProtocol &protocol); +DnsIpProtocol ping2dns_protocol(const PingProtocol& pprot); + +#endif diff --git a/src/dns/dnsmaster.cpp b/src/dns/dnsmaster.cpp index b0b4049..c6bca7f 100644 --- a/src/dns/dnsmaster.cpp +++ b/src/dns/dnsmaster.cpp @@ -158,6 +158,7 @@ ResolverItem& DnsMaster::get_resolver_for(const std::string &hostname, << ip; ResolverItem new_resolver( new IpPseudoResolver(IoService, hostname, + protocol, Cache) ); ResolverMap[key] = new_resolver; } @@ -196,22 +197,6 @@ bool DnsMaster::is_ip(const std::string &hostname) const } -DnsIpProtocol DnsMaster::ping2dns_protocol(const PingProtocol& pprot) -{ - switch (pprot) - { - case PingProtocol_ICMP: return DNS_IPv4; break; - case PingProtocol_ICMPv6: return DNS_IPv6; break; - case PingProtocol_TCP: return DNS_IPv4; break; - case PingProtocol_TCP_IPv6: return DNS_IPv6; break; - default: - GlobalLogger.warning() << "DnsMaster: Unexpected ping protocol: " - << static_cast(pprot); - return DNS_IPALL; - break; - } -} - /*boost::asio::ip::address &DnsMaster::get_name_server() const { return NameServer; @@ -232,14 +217,3 @@ int DnsMaster::get_max_recursion_count() const return MaxRecursionCount; } -std::string to_string(const DnsIpProtocol &protocol) -{ - switch (protocol) - { - case DNS_IPv4: return "IPv4"; break; - case DNS_IPv6: return "IPv6"; break; - case DNS_IPALL: return "IPv4/6"; break; - default: GlobalLogger.warning() << "Unexpected protocol in to_string!"; - return "Unexpected Protocol"; break; - } -} diff --git a/src/dns/dnsmaster.h b/src/dns/dnsmaster.h index ad26341..6b2a8b9 100644 --- a/src/dns/dnsmaster.h +++ b/src/dns/dnsmaster.h @@ -44,6 +44,7 @@ #include "host/pinger.h" // for IoserviceItem #include "host/pingprotocol.h" +#include "dns/dnsipprotocol.h" #include "dns/dnscache.h" #include "dns/resolverbase.h" @@ -51,16 +52,9 @@ class DnsMaster; typedef boost::shared_ptr DnsMasterItem; -typedef boost::net::dns::type_t DnsIpProtocol; -const DnsIpProtocol DNS_IPv4 = boost::net::dns::type_a; -const DnsIpProtocol DNS_IPv6 = boost::net::dns::type_a6; -const DnsIpProtocol DNS_IPALL = boost::net::dns::type_all; - typedef std::pair resolver_key_type; typedef std::map resolver_map_type; -std::string to_string(const DnsIpProtocol &protocol); - /** * Factory and Cache of DNS resolvers * @@ -134,7 +128,6 @@ private: // internal helper functions private: bool is_ip(const std::string &hostname) const; - static DnsIpProtocol ping2dns_protocol(const PingProtocol& pprot); }; #endif diff --git a/src/dns/dnsresolver.cpp b/src/dns/dnsresolver.cpp index 13a00eb..ff376ea 100644 --- a/src/dns/dnsresolver.cpp +++ b/src/dns/dnsresolver.cpp @@ -55,12 +55,11 @@ DnsResolver::DnsResolver(IoServiceItem &io_serv, const DnsIpProtocol &protocol, const DnsCacheItem cache, const boost::asio::ip::address &name_server) - : ResolverBase( io_serv, hostname, cache ) + : ResolverBase( io_serv, hostname, protocol, cache ) , Socket( *io_serv, ip::udp::endpoint(ip::udp::v4(), 0)) // just connect to anything, will specify sender/receiver later , ReceiveBuffer() , RequestBuffer() - , Protocol( protocol ) , NameServer( name_server, Config::DNS_PORT ) , ResolveTimeoutTimer( *io_serv ) , PauseBeforeRetryTimer( *io_serv ) @@ -465,7 +464,7 @@ void DnsResolver::handle_cname(const int recursion_count, else { // check cache for IP for this cname bool check_up_to_date = true; - HostAddressVec cached_data = Cache->get_ips_recursive(last_cname, + HostAddressVec cached_data = get_cached_ips_recursively(last_cname, check_up_to_date); if ( !cached_data.empty() ) { diff --git a/src/dns/dnsresolver.h b/src/dns/dnsresolver.h index 5ab1fd2..0201dfa 100644 --- a/src/dns/dnsresolver.h +++ b/src/dns/dnsresolver.h @@ -101,7 +101,6 @@ private: boost::asio::ip::udp::socket Socket; boost::net::dns_buffer_t ReceiveBuffer; boost::net::dns_buffer_t RequestBuffer; - DnsIpProtocol Protocol; boost::asio::ip::udp::endpoint NameServer; boost::asio::deadline_timer ResolveTimeoutTimer; boost::asio::deadline_timer PauseBeforeRetryTimer; diff --git a/src/dns/ippseudoresolver.h b/src/dns/ippseudoresolver.h index 0e194b8..abe9299 100644 --- a/src/dns/ippseudoresolver.h +++ b/src/dns/ippseudoresolver.h @@ -52,8 +52,9 @@ public: private: IpPseudoResolver(const IoServiceItem io_serv, const std::string &ip_string, + const DnsIpProtocol &protocol, const DnsCacheItem &cache ) - : ResolverBase( io_serv, ip_string, cache ) + : ResolverBase( io_serv, ip_string, protocol, cache ) , IpAddress( boost::asio::ip::address::from_string(ip_string), Config::DefaultTtl ) {} diff --git a/src/dns/resolverbase.cpp b/src/dns/resolverbase.cpp index 367f46e..da76115 100644 --- a/src/dns/resolverbase.cpp +++ b/src/dns/resolverbase.cpp @@ -66,9 +66,11 @@ void ResolverBase::async_resolve(const callback_type &callback, ResolverBase::ResolverBase(const IoServiceItem &io_serv, const std::string &hostname, + const DnsIpProtocol &protocol, const DnsCacheItem &cache ) : IoService( io_serv ) , Hostname( hostname ) + , Protocol( protocol ) , Cache( cache ) , CallbackList() {} @@ -90,26 +92,33 @@ std::string ResolverBase::get_skip_cname() const void ResolverBase::update_cache( const std::string &hostname, const HostAddressVec &new_ips ) const -{ Cache->update( hostname, new_ips ); } +{ Cache->update( hostname, Protocol, new_ips ); } void ResolverBase::update_cache( const std::string &hostname, const Cname &cname ) const { Cache->update( hostname, cname ); } void ResolverBase::update_cache( const HostAddressVec &new_ips ) const -{ Cache->update( Hostname, new_ips ); } +{ Cache->update( Hostname, Protocol, new_ips ); } void ResolverBase::update_cache( const Cname &cname ) const { Cache->update( Hostname, cname ); } +/** + * @brief get IPs like a "real" resolver, i.e. recursively going through CNAMEs + * + * if host is empty, will use this Resolver's Hostname and Protocol + * + * just calls DnsCache::get_ips_recursive + */ HostAddressVec ResolverBase::get_cached_ips_recursively(const std::string host, bool check_up_to_date) const { if (host.empty()) - return Cache->get_ips_recursive(Hostname, check_up_to_date); + return Cache->get_ips_recursive(Hostname, Protocol, check_up_to_date); else - return Cache->get_ips_recursive(host, check_up_to_date); + return Cache->get_ips_recursive(host, Protocol, check_up_to_date); } void ResolverBase::schedule_callbacks(const bool was_success, diff --git a/src/dns/resolverbase.h b/src/dns/resolverbase.h index ee33447..1c9a573 100644 --- a/src/dns/resolverbase.h +++ b/src/dns/resolverbase.h @@ -28,6 +28,7 @@ #include #include "host/pinger.h" +#include "dns/dnsipprotocol.h" #include "dns/hostaddress.h" #include "dns/dnscache.h" @@ -68,12 +69,14 @@ public: protected: ResolverBase(const IoServiceItem &io_serv, const std::string &hostname, + const DnsIpProtocol &protocol, const DnsCacheItem &cache ); // variables protected: IoServiceItem IoService; const std::string Hostname; + const DnsIpProtocol Protocol; DnsCacheItem Cache; callback_list_type CallbackList; diff --git a/src/host/pingerfactory.cpp b/src/host/pingerfactory.cpp index a919b9a..75d43ed 100644 --- a/src/host/pingerfactory.cpp +++ b/src/host/pingerfactory.cpp @@ -117,7 +117,8 @@ PingerItem PingerFactory::createPinger( { // Raw sockets are locked down by Unix operating systems to prevent // malware and security holes, thus it requires root access to use it. - GlobalLogger.error() << "Pingcheck must run as root (" << ex.what() << ")" << endl; + GlobalLogger.error() << "Error creating Pinger: " << ex.what() + << " (you may have to run pingcheck as root)" << endl; exit( EXIT_FAILURE ); } diff --git a/src/host/pingprotocol.cpp b/src/host/pingprotocol.cpp index 6e57c87..6698526 100644 --- a/src/host/pingprotocol.cpp +++ b/src/host/pingprotocol.cpp @@ -60,3 +60,18 @@ PingProtocol get_ping_protocol_from_string( const string & protocol_string ) return protocol; } + +std::string ping_protocol_to_string( const PingProtocol &protocol) +{ + switch(protocol) + { + case PingProtocol_ICMP: return "ICMPv4"; break; + case PingProtocol_ICMPv6: return "ICMPv6"; break; + case PingProtocol_TCP: return "TCPv4"; break; + case PingProtocol_TCP_IPv6: return "TCPv6"; break; + default: + BOOST_ASSERT( + !"unexpected PingProtocol in ping_protocol_to_string!" ); + break; + } +} diff --git a/src/host/pingprotocol.h b/src/host/pingprotocol.h index 2e0bda1..54c91db 100644 --- a/src/host/pingprotocol.h +++ b/src/host/pingprotocol.h @@ -39,6 +39,7 @@ enum PingProtocol }; PingProtocol get_ping_protocol_from_string( const std::string & protocol_string ); +std::string ping_protocol_to_string( const PingProtocol &protocol); //----------------------------------------------------------------------------- // PingProtocolList diff --git a/src/host/pingscheduler.cpp b/src/host/pingscheduler.cpp index 179a70b..481d394 100644 --- a/src/host/pingscheduler.cpp +++ b/src/host/pingscheduler.cpp @@ -318,6 +318,12 @@ void PingScheduler::update_ping_protocol() void PingScheduler::get_next_ping_protocol() { + if (Ping) + { + Ping->stop_pinging(); + Ping.reset(); + } + GlobalLogger.debug() << LogPrefix << "------------------------------------------------------------------"; ++ProtocolIter; @@ -327,9 +333,6 @@ void PingScheduler::get_next_ping_protocol() // --> ProtocolIter still points to currently used protocol which is // required in dns_resolve_callback - if (Ping) - Ping->stop_pinging(); - Ping = PingerFactory::createPinger(ping_protocol, IoService, NetworkInterfaceName, PingReplyTimeout); diff --git a/src/icmp/icmppinger.cpp b/src/icmp/icmppinger.cpp index 92a30fc..93a071d 100644 --- a/src/icmp/icmppinger.cpp +++ b/src/icmp/icmppinger.cpp @@ -53,7 +53,7 @@ PingerItem IcmpPinger::create( { // get distributor IcmpPacketDistributorItem distributor = IcmpPacketDistributor::get_distributor( - icmp::v4(), source_network_interface, io_serv); + protocol, source_network_interface, io_serv); // create pinger IcmpPinger *ptr = new IcmpPinger(io_serv, protocol, echo_reply_timeout_in_sec, distributor); @@ -439,8 +439,17 @@ IcmpPacketDistributorItem IcmpPacketDistributor::get_distributor( // check if there is an instance for this protocol and interface if ( Instances.count(identifier) == 0 ) { // need to create an instance for this protocol and network interface + std::string protocol_str; + if (protocol == icmp::v4()) + protocol_str = "ICMPv4"; + else if (protocol == icmp::v6()) + protocol_str = "ICMPv6"; + else + protocol_str = "unknown protocol!"; + GlobalLogger.info() << "Creating IcmpPacketDistributor for interface " - << network_interface << std::endl; + << network_interface << " and protocol " + << protocol_str << std::endl; IcmpPacketDistributorItem new_instance( new IcmpPacketDistributor( protocol, network_interface, io_serv ) ); Instances[identifier] = new_instance; diff --git a/test/CMakeLists.test_dns.txt b/test/CMakeLists.test_dns.txt index c93c79a..71ef23d 100644 --- a/test/CMakeLists.test_dns.txt +++ b/test/CMakeLists.test_dns.txt @@ -8,6 +8,7 @@ add_executable(test_dns ${CMAKE_SOURCE_DIR}/src/host/pinger.cpp ${CMAKE_SOURCE_DIR}/src/dns/hostaddress.cpp ${CMAKE_SOURCE_DIR}/src/dns/timetolive.cpp + ${CMAKE_SOURCE_DIR}/src/dns/dnsipprotocol.cpp ${CMAKE_SOURCE_DIR}/src/dns/dnsmaster.cpp ${CMAKE_SOURCE_DIR}/src/dns/dnscache.cpp ${CMAKE_SOURCE_DIR}/src/dns/resolverbase.cpp diff --git a/test/data/CMakeLists.txt b/test/data/CMakeLists.txt index f70f55b..e381d44 100644 --- a/test/data/CMakeLists.txt +++ b/test/data/CMakeLists.txt @@ -3,4 +3,4 @@ configure_file(icmp_echorequest.pcap ${CMAKE_CURRENT_BINARY_DIR}/icmp_echoreques configure_file(icmp_echoreply.pcap ${CMAKE_CURRENT_BINARY_DIR}/icmp_echoreply.pcap COPYONLY) configure_file(icmp_timeexceeded.pcap ${CMAKE_CURRENT_BINARY_DIR}/icmp_timeexceeded.pcap COPYONLY) configure_file(icmp_destinationunreachable.pcap ${CMAKE_CURRENT_BINARY_DIR}/icmp_destinationunreachable.pcap COPYONLY) -configure_file(dns_cache_example.xml ${CMAKE_CURRENT_BINARY_DIR}/dns_cache_example.xml COPYONLY) +configure_file(dns_cache_compatibility_test.xml ${CMAKE_CURRENT_BINARY_DIR}/dns_cache_compatibility_test.xml COPYONLY) diff --git a/test/data/dns_cache_compatibility_test.xml b/test/data/dns_cache_compatibility_test.xml new file mode 100644 index 0000000..6a1e045 --- /dev/null +++ b/test/data/dns_cache_compatibility_test.xml @@ -0,0 +1,70 @@ + + + + + 3 + 0 + + + abc.xyz + 1 + + + 1 + 0 + + 11.22.33.44 + + 567 + 20150522T072136.957424 + + + + + + + cname1.xyz + 1 + + + 0 + 0 + + + + + cname1.xyz + 28 + + + 0 + 0 + + + + + 2 + 0 + + abc.xyz + + + + 0 + 20150522T072136.957426 + + + + + cname1.xyz + + abc.xyz + + 27 + 20150522T072136.957467 + + + + + + diff --git a/test/data/dns_cache_example.xml b/test/data/dns_cache_example.xml deleted file mode 100644 index e3f61aa..0000000 --- a/test/data/dns_cache_example.xml +++ /dev/null @@ -1,88 +0,0 @@ - - - - - 4 - 0 - - abc.xyz - - 1 - 0 - - 11.22.33.44 - - 567 - 20150429T140512.609565 - - - - - - cname1.xyz - - 0 - 0 - - - - cname2.xyz - - 0 - 0 - - - - cname3.xyz - - 0 - 0 - - - - - 4 - 0 - - abc.xyz - - - - 0 - 20150429T131654.312043 - - - - - cname1.xyz - - abc.xyz - - 27 - 20150429T140512.609555 - - - - - cname2.xyz - - cname1.xyz - - 207 - 20150429T140512.609545 - - - - - cname3.xyz - - cname2.xyz - - 87 - 20150429T140512.609533 - - - - - - diff --git a/test/test_dns.cpp b/test/test_dns.cpp index 11e7e9d..7938aa3 100644 --- a/test/test_dns.cpp +++ b/test/test_dns.cpp @@ -29,9 +29,11 @@ on this file might be covered by the GNU General Public License. #include #include +#include #include "host/pingprotocol.h" #include "dns/hostaddress.h" +#include "dns/dnsipprotocol.h" #include "dns/dnsmaster.h" #include "dns/dnscache.h" #include "dns/resolverbase.h" @@ -112,7 +114,7 @@ struct GlobalFixture HostAddress ip(address::from_string("192.168.42.1"), 61); HostAddressVec ips; ips.push_back(ip); - Cache->update("host1.test", ips); + Cache->update("host1.test", DNS_IPv4, ips); } { @@ -121,7 +123,7 @@ struct GlobalFixture HostAddressVec ips; ips.push_back(ip1); ips.push_back(ip2); - Cache->update("host2_3.test", ips); + Cache->update("host2_3.test", DNS_IPv4, ips); } { @@ -145,7 +147,7 @@ struct GlobalFixture resolved_ip_ttl_threshold ) ); ips.push_back( HostAddress( address::from_string("192.168.42.7"), resolved_ip_ttl_threshold+1 ) ); - Cache->update("host_outdated.test", ips); + Cache->update("host_outdated.test", DNS_IPv4, ips); Cache->update( "cname_outdated.test", Cname("host_outdated.test", resolved_ip_ttl_threshold) ); @@ -158,7 +160,7 @@ struct GlobalFixture HostAddressVec ips; ips.push_back( HostAddress( address::from_string("192.128.42.8"), 1 ) ); - Cache->update("host_ttl_below_thresh.test", ips); + Cache->update("host_ttl_below_thresh.test", DNS_IPv4, ips); Cache->update( "cname_ttl_below_thresh.test", Cname("host_ttl_below_thresh.test", 2) ); @@ -171,6 +173,18 @@ struct GlobalFixture Cache->update( "cname_loop3.test", Cname("cname_loop1.test", 60) ); } + { // create IPv4 and IPv6 for same host + HostAddressVec ips; + ips.push_back( HostAddress( address::from_string("192.168.42.8"), + 432 ) ); + Cache->update("host_v4_and_v6.test", DNS_IPv4, ips); + ips.clear(); + ips.push_back( HostAddress( + address::from_string("2a00:1450:4001:808::1004"), + 543 ) ); + Cache->update("host_v4_and_v6.test", DNS_IPv6, ips); + } + BOOST_TEST_MESSAGE( "Done filling cache." ); } @@ -231,7 +245,7 @@ BOOST_FIXTURE_TEST_SUITE( TestDnsCache, TestFixture ) BOOST_AUTO_TEST_CASE( cache_retrieve_ip1 ) { - HostAddressVec ips = Cache->get_ips("host1.test"); + HostAddressVec ips = Cache->get_ips("host1.test", DNS_IPv4); BOOST_REQUIRE_EQUAL( ips.size(), 1 ); HostAddress ip = ips.front(); BOOST_CHECK( ip.is_valid() ); @@ -242,7 +256,7 @@ BOOST_AUTO_TEST_CASE( cache_retrieve_ip1 ) BOOST_AUTO_TEST_CASE( cache_retrieve_ip2 ) { - HostAddressVec ips = Cache->get_ips("host2_3.test"); + HostAddressVec ips = Cache->get_ips("host2_3.test", DNS_IPv4); BOOST_CHECK_EQUAL( ips.size(), 2 ); HostAddress ip = ips[0]; BOOST_CHECK( ip.is_valid() ); @@ -256,7 +270,7 @@ BOOST_AUTO_TEST_CASE( cache_retrieve_ip2 ) BOOST_AUTO_TEST_CASE( cache_retrieve_cname ) { - HostAddressVec ips = Cache->get_ips("cname.test"); + HostAddressVec ips = Cache->get_ips("cname.test", DNS_IPv4); BOOST_CHECK( ips.empty() ); Cname cname = Cache->get_cname("cname.test"); @@ -267,7 +281,7 @@ BOOST_AUTO_TEST_CASE( cache_retrieve_cname ) BOOST_AUTO_TEST_CASE( cache_retrieve_recursive1 ) { // should get IP from host1 but ttl from cname since is smaller - HostAddressVec ips = Cache->get_ips_recursive("cname.test"); + HostAddressVec ips = Cache->get_ips_recursive("cname.test", DNS_IPv4); BOOST_REQUIRE_EQUAL( ips.size(), 1 ); HostAddress ip = ips.front(); BOOST_CHECK( ip.is_valid() ); @@ -278,7 +292,7 @@ BOOST_AUTO_TEST_CASE( cache_retrieve_recursive1 ) BOOST_AUTO_TEST_CASE( cache_retrieve_recursive2 ) { // should get IP from host1 but ttl from cname2 since is smaller - HostAddressVec ips = Cache->get_ips_recursive("cname3.test"); + HostAddressVec ips = Cache->get_ips_recursive("cname3.test", DNS_IPv4); BOOST_REQUIRE_EQUAL( ips.size(), 1 ); HostAddress ip = ips.front(); BOOST_CHECK( ip.is_valid() ); @@ -303,12 +317,13 @@ void cname_skip_test(const uint32_t ttl1, const uint32_t ttl2, HostAddressVec ips; ips.push_back( HostAddress( address::from_string("192.168.42.100"), ttl4 ) ); - cache->update("skip_chain_fourth.test", ips); + cache->update("skip_chain_fourth.test", DNS_IPv4, ips); } // normal recursive call should give nothing since one cname is outdated bool check_up_to_date = true; HostAddressVec ips = cache->get_ips_recursive("skip_chain_first.test", + DNS_IPv4, check_up_to_date); bool one_is_out_of_date = (ttl1 < 5) || (ttl2 < 5) || (ttl3 < 5) || (ttl4 < 5); @@ -347,59 +362,118 @@ BOOST_AUTO_TEST_CASE( cache_skip_tests ) BOOST_CHECK_EQUAL( Cache->get_first_outdated_cname("host1.test", 5), "" ); } -BOOST_AUTO_TEST_CASE( cache_load_test ) + +void create_cache(const std::string &file_name, IoServiceItem io_serv) { - std::stringstream file_name; - file_name << DATA_DIR_STRING << "/" << "dns_cache_example.xml"; - BOOST_TEST_MESSAGE( "loading cache from file " << file_name.str() ); - DnsCache loaded_cache( IoService, - file_name.str(), + BOOST_TEST_MESSAGE( "creating cache for file " << file_name ); + if ( I2n::file_exists(file_name) ) + BOOST_CHECK( I2n::unlink(file_name) ); // try to remove + DnsCache save_cache( io_serv, file_name, min_time_between_resolves ); + + HostAddressVec ips; + ips.push_back( HostAddress(address::from_string("11.22.33.44"), 567) ); + save_cache.update("abc.xyz", DNS_IPv4, ips); + + save_cache.update("cname1.xyz", Cname("abc.xyz", 27)); + + // is saved when destructed +} + + +void test_cache(const std::string &file_name, IoServiceItem io_serv) +{ + BOOST_TEST_MESSAGE( "loading cache from file " << file_name ); + DnsCache loaded_cache( io_serv, + file_name, min_time_between_resolves ); - HostAddressVec ips = loaded_cache.get_ips("abc.xyz"); - BOOST_REQUIRE_EQUAL( ips.size(), 1 ); - HostAddress ip = ips.front(); - BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "11.22.33.44" ); - BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 567 ); - BOOST_CHECK_EQUAL( ip.get_ttl().get_updated_value(), 0 ); + HostAddressVec ips = loaded_cache.get_ips("abc.xyz", DNS_IPv4); + BOOST_CHECK_EQUAL( ips.size(), 1 ); + if (ips.size() == 1) + { + HostAddress ip = ips.front(); + BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "11.22.33.44" ); + BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 567 ); + //BOOST_CHECK_EQUAL( ip.get_ttl().get_updated_value(), 0 ); + } Cname cname = loaded_cache.get_cname("cname1.xyz"); BOOST_CHECK_EQUAL( cname.Host, "abc.xyz" ); BOOST_CHECK_EQUAL( cname.Ttl.get_value(), 27 ); - BOOST_CHECK_EQUAL( cname.Ttl.get_updated_value(), 0 ); + //BOOST_CHECK_EQUAL( cname.Ttl.get_updated_value(), 0 ); // not testing Ttl set time since is private } + +BOOST_AUTO_TEST_CASE( cache_save_and_load_test ) +{ + std::stringstream file_name_temp; + file_name_temp << DATA_DIR_STRING << "/" << "dns_cache_save_load_test.xml"; + std::string file_name = file_name_temp.str(); + + // create and save + create_cache( file_name, IoService ); + + // now load and test + test_cache( file_name, IoService ); + +} + +BOOST_AUTO_TEST_CASE( cache_load_compatibility_test ) +{ + std::stringstream file_name; + file_name << DATA_DIR_STRING << "/" << "dns_cache_compatibility_test.xml"; + test_cache( file_name.str(), IoService ); + + // If this test fails because the cache structure was changed, + // you can overwrite the dns_cache_compatibility_test.xml by uncommenting + // the following code line. + //create_cache( file_name.str(), IoService ); + // + // Run test again with arg --log_level=MESSAGE and look out for message + // "create cache for file [...]dns_cache_compatibility_test.xml" + // You should then copy the dns_cache_compatibility_test.xml to test/data + // in git and commit together with the new cache structure code +} + BOOST_AUTO_TEST_CASE( cache_outdated_test ) { bool check_up_to_date = false; - HostAddressVec ips = Cache->get_ips("host_outdated.test", check_up_to_date); + HostAddressVec ips = Cache->get_ips("host_outdated.test", DNS_IPv4, + check_up_to_date); BOOST_CHECK_EQUAL( ips.size(), 4 ); - ips = Cache->get_ips_recursive("host_outdated.test", check_up_to_date); + ips = Cache->get_ips_recursive("host_outdated.test", DNS_IPv4, + check_up_to_date); BOOST_CHECK_EQUAL( ips.size(), 4 ); Cname cname = Cache->get_cname("cname_outdated.test", check_up_to_date); BOOST_CHECK( !cname.Host.empty() ); - ips = Cache->get_ips_recursive("cname_outdated.test", check_up_to_date); + ips = Cache->get_ips_recursive("cname_outdated.test", DNS_IPv4, + check_up_to_date); BOOST_CHECK_EQUAL( ips.size(), 4 ); - ips = Cache->get_ips_recursive("cname_up_to_date.test", check_up_to_date); + ips = Cache->get_ips_recursive("cname_up_to_date.test", DNS_IPv4, + check_up_to_date); BOOST_CHECK_EQUAL( ips.size(), 4 ); check_up_to_date = true; - ips = Cache->get_ips( "host_outdated.test", check_up_to_date ); + ips = Cache->get_ips( "host_outdated.test", DNS_IPv4, check_up_to_date ); BOOST_CHECK_EQUAL( ips.size(), 1 ); - ips = Cache->get_ips_recursive("host_outdated.test", check_up_to_date); + ips = Cache->get_ips_recursive("host_outdated.test", DNS_IPv4, + check_up_to_date); BOOST_CHECK_EQUAL( ips.size(), 1 ); cname = Cache->get_cname("cname_outdated.test", check_up_to_date); BOOST_CHECK( cname.Host.empty() ); - ips = Cache->get_ips_recursive("cname_outdated.test", check_up_to_date); + ips = Cache->get_ips_recursive("cname_outdated.test", DNS_IPv4, + check_up_to_date); BOOST_CHECK_EQUAL( ips.size(), 0 ); - ips = Cache->get_ips_recursive("cname_up_to_date.test", check_up_to_date); + ips = Cache->get_ips_recursive("cname_up_to_date.test", DNS_IPv4, + check_up_to_date); BOOST_CHECK_EQUAL( ips.size(), 1 ); } BOOST_AUTO_TEST_CASE( cache_ttl_below_thresh_test ) { - HostAddressVec ips = Cache->get_ips("host_ttl_below_thresh.test", false); + HostAddressVec ips = Cache->get_ips("host_ttl_below_thresh.test", DNS_IPv4, + false); BOOST_REQUIRE_EQUAL( ips.size(), 1 ); HostAddress ip = ips.front(); BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), min_time_between_resolves ); @@ -410,9 +484,30 @@ BOOST_AUTO_TEST_CASE( cache_ttl_below_thresh_test ) BOOST_AUTO_TEST_CASE( cache_retrieve_loop ) { - BOOST_CHECK_EQUAL( Cache->get_ips_recursive("cname_loop1.test").size(), 0 ); - BOOST_CHECK_EQUAL( Cache->get_ips_recursive("cname_loop2.test").size(), 0 ); - BOOST_CHECK_EQUAL( Cache->get_ips_recursive("cname_loop3.test").size(), 0 ); + BOOST_CHECK_EQUAL( + Cache->get_ips_recursive("cname_loop1.test", DNS_IPv4).size(), 0 ); + BOOST_CHECK_EQUAL( + Cache->get_ips_recursive("cname_loop2.test", DNS_IPv4).size(), 0 ); + BOOST_CHECK_EQUAL( + Cache->get_ips_recursive("cname_loop3.test", DNS_IPv4).size(), 0 ); +} + + +BOOST_AUTO_TEST_CASE( cache_IPv6 ) +{ + HostAddressVec ips = Cache->get_ips("host_v4_and_v6.test", DNS_IPv4); + BOOST_REQUIRE_EQUAL( ips.size(), 1 ); + HostAddress ip = ips.front(); + BOOST_CHECK( ip.is_valid() ); + BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.8" ); + BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 432 ); + + ips = Cache->get_ips("host_v4_and_v6.test", DNS_IPv6); + BOOST_REQUIRE_EQUAL( ips.size(), 1 ); + ip = ips.front(); + BOOST_CHECK( ip.is_valid() ); + BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "2a00:1450:4001:808::1004" ); + BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 543 ); } -- 1.7.1