From 26b0f6875e9ff9a3050d7da77f1f99638540db5d Mon Sep 17 00:00:00 2001 From: Christian Herdtweck Date: Fri, 24 Apr 2015 18:41:29 +0200 Subject: [PATCH] merged PingRotate into PingScheduler; fixed save/load of cache to/from file; started creating DNS unittest code now compiles and links and pings again! merged because through Asynchronicity it is more difficult to give feedback from Rotate to Scheduler further changes * in cache ensure that there is not cname and ip for same hostname * in cache remove trailing dots from hostnames * by default ask only for [number of] updatd IPs in retrieval from resolver * remove some left-overs from recursive resolving (e.g. Recursor creation in master) --- doc/pingcheck_icmp_distributor.graphml | 196 +++++++------------------------- src/dns/dnscache.cpp | 194 ++++++++++++++++++-------------- src/dns/dnscache.h | 9 +- src/dns/dnsmaster.cpp | 29 +---- src/dns/dnsmaster.h | 3 - src/dns/dnsresolver.cpp | 51 ++++++--- src/dns/dnsresolver.h | 6 +- src/dns/hostaddress.cpp | 6 + src/dns/hostaddress.h | 14 ++- src/dns/ippseudoresolver.h | 3 +- src/dns/resolverbase.cpp | 12 +- src/dns/resolverbase.h | 9 +- src/dns/timetolive.h | 4 +- src/host/pingscheduler.cpp | 132 ++++++++++++++-------- src/host/pingscheduler.h | 55 +++++----- src/icmp/icmppinger.cpp | 2 - src/main.cpp | 2 - test/CMakeLists.test_dns.txt | 32 +++++ test/CMakeLists.txt | 2 + test/test_dns.cpp | 105 +++++++++++++++++ 20 files changed, 482 insertions(+), 384 deletions(-) create mode 100644 test/CMakeLists.test_dns.txt create mode 100644 test/test_dns.cpp diff --git a/doc/pingcheck_icmp_distributor.graphml b/doc/pingcheck_icmp_distributor.graphml index bca452a..7e532fa 100644 --- a/doc/pingcheck_icmp_distributor.graphml +++ b/doc/pingcheck_icmp_distributor.graphml @@ -35,7 +35,7 @@ - PingRotate + PingRotate --> merged into Scheduler @@ -141,7 +141,7 @@ - ping + ping @@ -516,7 +516,7 @@ PingIntervalInSec - send_to + send_to @@ -1407,7 +1407,6 @@ _ping_address - @@ -1426,7 +1425,6 @@ _ping_address - @@ -1444,7 +1442,6 @@ _ping_address - @@ -1462,7 +1459,6 @@ _ping_address - @@ -1480,7 +1476,6 @@ _ping_address - @@ -1500,13 +1495,12 @@ finished - - DnsResolver + DnsResolver ::have_up_to_date_ips @@ -1519,7 +1513,6 @@ finished - @@ -1559,7 +1552,7 @@ IPs? - send request + send request receive echo reply or destin. unreach @@ -1588,7 +1581,6 @@ or destin. unreach - @@ -1606,7 +1598,6 @@ or destin. unreach - @@ -1624,7 +1615,6 @@ or destin. unreach - @@ -1642,7 +1632,6 @@ or destin. unreach - @@ -1661,7 +1650,6 @@ or destin. unreach - @@ -1679,7 +1667,6 @@ or destin. unreach - @@ -1698,7 +1685,6 @@ or destin. unreach - @@ -1716,7 +1702,6 @@ or destin. unreach - @@ -1735,7 +1720,6 @@ cancel_resolve - @@ -1758,7 +1742,7 @@ SIGKILL - + IcmpPinger @@ -1775,7 +1759,7 @@ SIGKILL - + IcmpPinger @@ -1792,7 +1776,7 @@ SIGKILL - + IcmpPacketDistributor @@ -1810,7 +1794,7 @@ SIGKILL - + PingerFactory @@ -1827,7 +1811,7 @@ SIGKILL - + IcmpPacketFactory @@ -1844,7 +1828,7 @@ SIGKILL - + ICMP @@ -1862,7 +1846,7 @@ Socket - + the world @@ -1878,7 +1862,7 @@ Socket - + NetworkInterface @@ -1895,7 +1879,7 @@ Socket - + deadline_timer @@ -1912,7 +1896,7 @@ Socket - + deadline_timer @@ -1929,7 +1913,7 @@ Socket - + PingScheduler @@ -1946,7 +1930,7 @@ Socket - + PingScheduler @@ -1963,7 +1947,7 @@ Socket - + deadline_timer @@ -1980,7 +1964,7 @@ Socket - + deadline_timer @@ -1997,41 +1981,7 @@ Socket - - - - PingRotate - - - - - - - - - - - - - - - - - PingRotate - - - - - - - - - - - - - - + ... @@ -2045,10 +1995,10 @@ Socket - + - + HostStatus @@ -2062,10 +2012,10 @@ Socket - + - + HostStatus @@ -2085,7 +2035,7 @@ Socket - hold + hold shared_ptrs @@ -2112,7 +2062,7 @@ shared_ptrs - creates + creates @@ -2138,7 +2088,7 @@ shared_ptrs - owns + owns @@ -2173,7 +2123,7 @@ shared_ptrs - send + send @@ -2199,7 +2149,7 @@ shared_ptrs - owns + owns @@ -2216,7 +2166,7 @@ shared_ptrs - calls + calls @@ -2233,7 +2183,7 @@ shared_ptrs - owns + owns @@ -2250,7 +2200,7 @@ shared_ptrs - owns + owns @@ -2288,13 +2238,13 @@ shared_ptrs - + - owns + owns @@ -2305,13 +2255,13 @@ shared_ptrs - + - owns + owns @@ -2322,30 +2272,13 @@ shared_ptrs - + - owns - - - - - - - - - - - - - - - - - owns + owns @@ -2385,30 +2318,13 @@ shared_ptrs - - - - - - - owns - - - - - - - - - - - + - owns + owns @@ -2923,13 +2839,13 @@ shared_ptrs - + - owns + owns @@ -2940,13 +2856,13 @@ shared_ptrs - + - owns + owns @@ -3073,7 +2989,6 @@ shared_ptrs - @@ -3084,7 +2999,6 @@ shared_ptrs - @@ -3095,7 +3009,6 @@ shared_ptrs - @@ -3106,7 +3019,6 @@ shared_ptrs - @@ -3117,7 +3029,6 @@ shared_ptrs - @@ -3128,7 +3039,6 @@ shared_ptrs - @@ -3139,7 +3049,6 @@ shared_ptrs - @@ -3150,7 +3059,6 @@ shared_ptrs - @@ -3161,7 +3069,6 @@ shared_ptrs - @@ -3172,7 +3079,6 @@ shared_ptrs - @@ -3191,7 +3097,6 @@ shared_ptrs - @@ -3210,7 +3115,6 @@ shared_ptrs - @@ -3221,7 +3125,6 @@ shared_ptrs - @@ -3232,7 +3135,6 @@ shared_ptrs - @@ -3245,7 +3147,6 @@ shared_ptrs - @@ -3256,7 +3157,6 @@ shared_ptrs - @@ -3267,7 +3167,6 @@ shared_ptrs - @@ -3278,7 +3177,6 @@ shared_ptrs - @@ -3297,7 +3195,6 @@ shared_ptrs - @@ -3308,7 +3205,6 @@ shared_ptrs - @@ -3326,7 +3222,6 @@ shared_ptrs - @@ -3338,7 +3233,6 @@ shared_ptrs - @@ -3348,7 +3242,6 @@ shared_ptrs - @@ -3358,7 +3251,6 @@ shared_ptrs - diff --git a/src/dns/dnscache.cpp b/src/dns/dnscache.cpp index d516ae2..d306fd1 100644 --- a/src/dns/dnscache.cpp +++ b/src/dns/dnscache.cpp @@ -58,6 +58,11 @@ Cname::Cname(const std::string &host, const uint32_t ttl) , Ttl( ttl ) {} +Cname::Cname(const std::string &host, const TimeToLive &ttl) + : Host( host ) + , Ttl( ttl ) +{} + DnsCache::DnsCache(const IoServiceItem &io_serv, const std::string &cache_file) : IpCache() @@ -78,7 +83,7 @@ DnsCache::DnsCache(const IoServiceItem &io_serv, DnsCache::~DnsCache() { - GlobalLogger.info() << "Dns Cache is being destructed"; + GlobalLogger.info() << "DnsCache: being destructed"; // save one last time without re-scheduling the next save save_to_cachefile(); @@ -117,109 +122,121 @@ void DnsCache::schedule_save(const boost::system::error_code &error) void DnsCache::save_to_cachefile() { if (!HasChanged) - { GlobalLogger.info() << "DnsCache: skip saving because has not changed"; - return; - } else if (CacheFile.empty()) - { GlobalLogger.warning() << "DnsCache: skip saving because file name empty!"; - return; - } - - try - { - std::ofstream ofs( CacheFile.c_str() ); - boost::archive::xml_oarchive oa(ofs); - oa << boost::serialization::make_nvp("IpCache", IpCache); - oa << boost::serialization::make_nvp("CnameCache", CnameCache); - GlobalLogger.info() << "DnsCache: saved to cache file " << CacheFile; - - HasChanged = false; - } - catch (std::exception &exc) + else if (CacheFile == DO_NOT_USE_CACHE_FILE) + GlobalLogger.info() << "DnsCache: configured not to use cache file"; + else { - GlobalLogger.warning() << "Saving failed: " << exc.what(); + try + { + std::ofstream ofs( CacheFile.c_str() ); + boost::archive::xml_oarchive oa(ofs); + //oa << boost::serialization::make_nvp("IpCache", IpCache); + //oa << boost::serialization::make_nvp("CnameCache", CnameCache); + oa & BOOST_SERIALIZATION_NVP(IpCache); + oa & BOOST_SERIALIZATION_NVP(CnameCache); + GlobalLogger.info() << "DnsCache: saved to cache file " << CacheFile; + + HasChanged = false; + } + catch (std::exception &exc) + { + GlobalLogger.warning() << "Saving failed: " << exc.what(); + } } } - void DnsCache::load_from_cachefile() { if (CacheFile.empty()) - { GlobalLogger.warning() << "DnsCache: cannot load because cache file name is empty!"; - return; - } + else if (CacheFile == DO_NOT_USE_CACHE_FILE) + GlobalLogger.info() << "DnsCache: configured not to use cache file"; else if ( !I2n::file_exists(CacheFile) ) - { GlobalLogger.warning() << "DnsCache: cannot load because cache file " << CacheFile << " does not exist!"; - return; - } - try + else { - HostAddressVec cache; - - std::ifstream ifs( CacheFile.c_str() ); - boost::archive::xml_iarchive ia(ifs); + try + { + std::ifstream ifs( CacheFile.c_str() ); + boost::archive::xml_iarchive ia(ifs); - ia >> boost::serialization::make_nvp("IpCache", cache); - ia >> boost::serialization::make_nvp("CnameCache", cache); - GlobalLogger.info() << "DnsCache: loaded from file " << CacheFile; - } - catch (boost::archive::archive_exception &exc) - { - GlobalLogger.warning() << "DnsCache: archive exception loading from " - << CacheFile << ": " << exc.what(); - } - catch (std::exception &exc) - { - GlobalLogger.warning() << "DnsCache: exception while loading from " - << CacheFile << ": " << exc.what(); + ia & BOOST_SERIALIZATION_NVP(IpCache); + ia & BOOST_SERIALIZATION_NVP(CnameCache); + GlobalLogger.info() << "DnsCache: loaded from file " << CacheFile; + } + catch (boost::archive::archive_exception &exc) + { + GlobalLogger.warning() << "DnsCache: archive exception loading from " + << CacheFile << ": " << exc.what(); + } + catch (std::exception &exc) + { + GlobalLogger.warning() << "DnsCache: exception while loading from " + << CacheFile << ": " << exc.what(); + } } } -void DnsCache::update(const std::string &hostname, - const HostAddressVec &new_data) + +// warn if hostname is empty and remove trailing dot +std::string DnsCache::key_for_hostname(const std::string &hostname) const { - GlobalLogger.info() << "DnsCache: update IPs for " << hostname - << " to " << new_data.size() << "-list"; - IpCache[hostname] = new_data; - HasChanged = true; + if (hostname.empty()) + { + GlobalLogger.warning() << "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) + const HostAddressVec &new_ips) { - GlobalLogger.info() << "DnsCache: update CNAME for " << hostname - << " to " << cname.Host; - CnameCache[hostname] = cname; + std::string key = key_for_hostname(hostname); + if ( !get_cname(hostname).Host.empty() ) + { // ensure that there is never IP and CNAME for the same host + GlobalLogger.warning() << "DnsCache: Saving IPs for " << key + << " removes CNAME to " << get_cname(hostname).Host << "!"; + update(hostname, Cname()); // overwrite with "empty" cname + } + GlobalLogger.debug() << "DnsCache: update IPs for " << key + << " to " << new_ips.size() << "-list"; + IpCache[key] = new_ips; HasChanged = true; } void DnsCache::update(const std::string &hostname, - const uint32_t new_ttl) + const Cname &cname) { - GlobalLogger.info() << "DnsCache: ensure TTL for IPs for " << hostname - << " is below " << new_ttl << "s"; - HostAddressVec ips = IpCache[hostname]; - TimeToLive current_ttl; - BOOST_FOREACH( HostAddress &addr, ips ) - { - current_ttl = addr.get_ttl(); - if (current_ttl.get_value() > new_ttl) - { - current_ttl.set_value(new_ttl); - addr.set_ttl(current_ttl); - HasChanged = true; - } + std::string key = key_for_hostname(hostname); + if ( !get_ips(hostname).empty() ) + { // ensure that there is never IP and CNAME for the same host + GlobalLogger.warning() << "DnsCache: Saving CNAME for " << key + << " removes " << get_ips(hostname).size() << " IPs for same host!"; + update(hostname, HostAddressVec()); // overwrite with empty IP list } - IpCache[hostname] = ips; + + // remove possible trailing dot from cname + Cname to_save = Cname(key_for_hostname(cname.Host), + cname.Ttl); + + GlobalLogger.info() << "DnsCache: update CNAME for " << key + << " to " << to_save.Host; + CnameCache[key] = to_save; + HasChanged = true; } @@ -229,24 +246,32 @@ void DnsCache::update(const std::string &hostname, HostAddressVec DnsCache::get_ips(const std::string &hostname, const bool check_up_to_date) { - HostAddressVec result = IpCache[hostname]; + std::string key = key_for_hostname(hostname); + HostAddressVec result = IpCache[key]; if (check_up_to_date) { HostAddressVec result_up_to_date; uint32_t threshold = static_cast( DnsMaster::get_instance()->get_resolved_ip_ttl_threshold() ); + uint32_t updated_ttl; BOOST_FOREACH( const HostAddress &addr, result ) { - if (addr.get_ttl().get_updated_value() > threshold) + updated_ttl = addr.get_ttl().get_updated_value(); + if (updated_ttl > threshold) result_up_to_date.push_back(addr); + else + GlobalLogger.debug() << "DnsCache: do not return " + << addr.get_ip().to_string() << " since TTL " + << updated_ttl << "s is out of date (thresh=" + << threshold << "s)"; } - GlobalLogger.debug() << "DnsCache: From cached list of size " - << result.size() << " return " << result_up_to_date.size() - << " since rest out of date"; result = result_up_to_date; } - GlobalLogger.info() << "DnsCache: request IPs for " << hostname - << " --> " << result.size() << "-list"; + GlobalLogger.debug() << "DnsCache: request IPs for " << key + << " --> " << result.size() << "-list"; + BOOST_FOREACH( const HostAddress &addr, result ) + GlobalLogger.debug() << "DnsCache: " << addr.get_ip().to_string() + << " (TTL " << addr.get_ttl().get_value() << "s)"; return result; } @@ -256,16 +281,17 @@ HostAddressVec DnsCache::get_ips(const std::string &hostname, Cname DnsCache::get_cname(const std::string &hostname, const bool check_up_to_date) { - Cname result_obj = CnameCache[hostname]; - GlobalLogger.info() << "DnsCache: request CNAME for " << hostname - << " --> \"" << result_obj.Host << "\""; + std::string key = key_for_hostname(hostname); + Cname result_obj = CnameCache[key]; + GlobalLogger.debug() << "DnsCache: request CNAME for " << key + << " --> \"" << result_obj.Host << "\""; if (result_obj.Host.empty()) return result_obj; else if (check_up_to_date) { - if (result_obj.Ttl.get_updated_value() > DnsMaster::get_instance() - ->get_resolved_ip_ttl_threshold()) + if ( result_obj.Ttl.get_updated_value() > static_cast( + DnsMaster::get_instance()->get_resolved_ip_ttl_threshold()) ) return result_obj; else { @@ -290,7 +316,7 @@ HostAddressVec DnsCache::get_ips_recursive(const std::string &hostname, while ( result.empty() ) { current_cname = get_cname(current_host, check_up_to_date); - current_host = current_cname.Host; + current_host = key_for_hostname(current_cname.Host); if (current_host.empty()) break; else if (++n_recursions >= Config::MaxRetrievalRecursions) @@ -353,7 +379,7 @@ std::string DnsCache::get_first_outdated_cname(const std::string &hostname, } cname = get_cname(up_to_date_host); - if (cname.Host.empty()) + if (key_for_hostname(cname.Host).empty()) // reached end of cname list break; else if (cname.Ttl.get_updated_value() > ttl_thresh) diff --git a/src/dns/dnscache.h b/src/dns/dnscache.h index 7a0fbe8..eaca600 100644 --- a/src/dns/dnscache.h +++ b/src/dns/dnscache.h @@ -53,6 +53,7 @@ struct Cname Cname(); Cname(const std::string &host, const uint32_t ttl); + Cname(const std::string &host, const TimeToLive &ttl); // serialization friend class boost::serialization::access; @@ -72,6 +73,10 @@ struct Cname }; typedef std::map cname_map_type; +/// constant to give as cache_file arg to DnsCache constructor +/// indicating that no cache file should be used +const std::string DO_NOT_USE_CACHE_FILE = "do not use cache file!"; + // ----------------------------------------------------------------------------- // DnsCache @@ -85,9 +90,8 @@ public: ~DnsCache(); // accessed from ResolverBase subclasses - void update(const std::string &hostname, const HostAddressVec &new_data); + 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 uint32_t ttl); // retrieval HostAddressVec get_ips(const std::string &hostname, @@ -112,6 +116,7 @@ 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; }; diff --git a/src/dns/dnsmaster.cpp b/src/dns/dnsmaster.cpp index 5b0a1ce..609924d 100644 --- a/src/dns/dnsmaster.cpp +++ b/src/dns/dnsmaster.cpp @@ -87,9 +87,10 @@ DnsMaster::~DnsMaster() GlobalLogger.info() << "DnsMaster is being destructed"; if (DnsMaster::TheOnlyInstance) - { // just to be sure ... - GlobalLogger.warning() << "DnsMaster is being destructed that is not " - << "singleton instance TheOnlyInstance!"; + { // apparently, this static variable still exists while itself is + // destructed... + //GlobalLogger.warning() << "DnsMaster is being destructed that is not " + // << "singleton instance TheOnlyInstance!"; DnsMaster::TheOnlyInstance.reset(); } @@ -147,28 +148,6 @@ ResolverItem& DnsMaster::get_resolver_for(const std::string &hostname, return ResolverMap[key]; } -// create resolver but do not remember it in ResolverMap -ResolverItem DnsMaster::get_recursor_for(const std::string &hostname, - const DnsIpProtocol &protocol, - const boost::asio::ip::address &name_server) -{ - resolver_key_type key(hostname, protocol); - if ( !ResolverMap[key] ) - GlobalLogger.warning() << "DnsMaster: requesting recursor for host " - << hostname << " and protocol " << to_string(protocol) - << " but have no regular resolver for this combination!"; - else - GlobalLogger.warning() << "DnsMaster: requesting recursor for host " - << hostname << " and protocol " << to_string(protocol); - - ResolverItem new_resolver( new DnsResolver(IoService, - hostname, - protocol, - Cache, - name_server) ); - return new_resolver; -} - /** * return true if given hostname string actually is an IP * diff --git a/src/dns/dnsmaster.h b/src/dns/dnsmaster.h index 63e5be9..27470a9 100644 --- a/src/dns/dnsmaster.h +++ b/src/dns/dnsmaster.h @@ -86,9 +86,6 @@ public: const PingProtocol &ping_protocol); ResolverItem& get_resolver_for(const std::string &hostname, const DnsIpProtocol &protocol); - ResolverItem get_recursor_for(const std::string &hostname, - const DnsIpProtocol &protocol, - const boost::asio::ip::address &name_server); // implementation of singleton private: diff --git a/src/dns/dnsresolver.cpp b/src/dns/dnsresolver.cpp index 1e236d3..3240e31 100644 --- a/src/dns/dnsresolver.cpp +++ b/src/dns/dnsresolver.cpp @@ -234,11 +234,7 @@ void DnsResolver::handle_dns_result(const boost::system::error_code &error, // remember cname list (if there were any) BOOST_FOREACH( const src_cname_pair &host_and_cname, result_cnames ) - { - GlobalLogger.info() << LogPrefix << "Remember CNAME " - << host_and_cname.first << " --> " << host_and_cname.second.Host; ResolverBase::update_cache(host_and_cname.first, host_and_cname.second); - } if ( !result_ips.empty() ) handle_ips( result_ips ); @@ -674,34 +670,61 @@ void DnsResolver::wait_timer_timeout_handler( // RETRIEVAL //============================================================================== -HostAddress DnsResolver::get_next_ip() +HostAddress DnsResolver::get_next_ip(bool check_up_to_date) { // get cached data + // (do not use arg check_up_to_date here in order to give NextIpIndex + // a chance to stay above number of outdate IPs) HostAddressVec cached_data = ResolverBase::get_cached_ips_recursively(); // if no results cached, return default-constructed HostAddress (0.0.0.0) + HostAddress return_candidate; if ( cached_data.empty() ) { - HostAddress return_value; - return return_value; + GlobalLogger.debug() << LogPrefix << "Get next IP: nothing cached"; + return return_candidate; } - // check validity of index (cache may have changed since last call) - if (NextIpIndex >= cached_data.size()) - NextIpIndex = 0; + int n_iter = 0; + std::size_t n_ips = cached_data.size(); + uint32_t ttl_thresh = static_cast( DnsMaster::get_instance() + ->get_resolved_ip_ttl_threshold() ); - // return next IP - return cached_data[NextIpIndex++]; + GlobalLogger.info() << LogPrefix << "Get next IP from cached result of " + << n_ips << " IPs; first index to consider is " << NextIpIndex + << "; TTL thresh = " << ttl_thresh << " is used: " << check_up_to_date; + + // loop until we have found a cached result (that is up to date) + // or until we have tried all cached IPs + while (true) + { + // check index since cache size may have changed since last call + if (NextIpIndex >= n_ips) + NextIpIndex = 0; + else if ( n_iter >= n_ips) + return HostAddress(); // have checked all candidates + else + { // there are candidates left to consider + return_candidate = cached_data[NextIpIndex++]; + if (!check_up_to_date) + return return_candidate; + else if (cached_data[NextIpIndex].get_ttl().get_updated_value() + > ttl_thresh) + return cached_data[++NextIpIndex]; + else + ++n_iter; + } + } } bool DnsResolver::have_up_to_date_ip() { - return ! ResolverBase::get_cached_ips_recursively("", true).empty(); + return get_resolved_ip_count() > 0; } int DnsResolver::get_resolved_ip_count() { - return ResolverBase::get_cached_ips_recursively().size(); + return ResolverBase::get_cached_ips_recursively("", true).size(); } // (created using vim -- the world's best text editor) diff --git a/src/dns/dnsresolver.h b/src/dns/dnsresolver.h index 1e8ce1a..895f6b4 100644 --- a/src/dns/dnsresolver.h +++ b/src/dns/dnsresolver.h @@ -52,10 +52,6 @@ public: friend ResolverItem& DnsMaster::get_resolver_for( const std::string &hostname, const DnsIpProtocol &protocol); - friend ResolverItem DnsMaster::get_recursor_for( - const std::string &hostname, - const DnsIpProtocol &protocol, - const boost::asio::ip::address &name_server); private: DnsResolver(IoServiceItem &io_serv, const std::string &hostname, @@ -65,7 +61,7 @@ private: // only real public function (called from pingers) public: - HostAddress get_next_ip(); + HostAddress get_next_ip(const bool check_up_to_date=true); bool have_up_to_date_ip(); int get_resolved_ip_count(); void cancel_resolve(); diff --git a/src/dns/hostaddress.cpp b/src/dns/hostaddress.cpp index ebc01ad..2879090 100644 --- a/src/dns/hostaddress.cpp +++ b/src/dns/hostaddress.cpp @@ -72,3 +72,9 @@ void HostAddress::set_ttl( const TimeToLive &ttl ) { Ttl = ttl; } + +bool HostAddress::is_valid() const +{ + return Ip == address() || Ip == boost::asio::ip::address_v4() + || Ip == boost::asio::ip::address_v6(); +} diff --git a/src/dns/hostaddress.h b/src/dns/hostaddress.h index d5fdf18..4fdc82f 100644 --- a/src/dns/hostaddress.h +++ b/src/dns/hostaddress.h @@ -51,6 +51,10 @@ public: TimeToLive get_ttl() const; void set_ttl( const TimeToLive &ttl ); + // return false if this HostAddress was default-constructed, so does not + // actually point to an ip (ip is probably 0.0.0.0) + bool is_valid() const; + private: /// IP address of the host boost::asio::ip::address Ip; @@ -64,18 +68,18 @@ private: template void save(Archive & ar, const unsigned int version) const { - std::string ip = Ip.to_string(); - ar & BOOST_SERIALIZATION_NVP(ip); + std::string ip_str = Ip.to_string(); + ar & BOOST_SERIALIZATION_NVP(ip_str); ar & BOOST_SERIALIZATION_NVP(Ttl); } template void load(Archive & ar, const unsigned int version) { - std::string ip; - ar & BOOST_SERIALIZATION_NVP(ip); + std::string ip_str; + ar & BOOST_SERIALIZATION_NVP(ip_str); ar & BOOST_SERIALIZATION_NVP(Ttl); - Ip = boost::asio::ip::address::from_string(ip); + Ip = boost::asio::ip::address::from_string(ip_str); } diff --git a/src/dns/ippseudoresolver.h b/src/dns/ippseudoresolver.h index f508749..a3edee7 100644 --- a/src/dns/ippseudoresolver.h +++ b/src/dns/ippseudoresolver.h @@ -63,7 +63,8 @@ private: // only real public function public: - HostAddress get_next_ip() { return IpAddress; } + HostAddress get_next_ip(const bool check_up_to_date=true) + { return IpAddress; } bool have_up_to_date_ip() { return true; } int get_resolved_ip_count(){ return 1; } bool is_resolving() { return false; } diff --git a/src/dns/resolverbase.cpp b/src/dns/resolverbase.cpp index 1ca1375..82b6c29 100644 --- a/src/dns/resolverbase.cpp +++ b/src/dns/resolverbase.cpp @@ -55,7 +55,7 @@ ResolverBase::ResolverBase(const IoServiceItem &io_serv, std::string ResolverBase::get_hostname() const { return Hostname; } -std::string ResolverBase::get_skipper() const +std::string ResolverBase::get_skip_cname() const { DnsMasterItem master = DnsMaster::get_instance(); std::string first_out_of_date = Cache->get_first_outdated_cname( @@ -71,21 +71,19 @@ std::string ResolverBase::get_skipper() const } void ResolverBase::update_cache( const std::string &hostname, - const HostAddressVec &new_results ) const -{ Cache->update( hostname, new_results ); } + const HostAddressVec &new_ips ) const +{ Cache->update( hostname, 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_results ) const -{ Cache->update( Hostname, new_results ); } +void ResolverBase::update_cache( const HostAddressVec &new_ips ) const +{ Cache->update( Hostname, new_ips ); } void ResolverBase::update_cache( const Cname &cname ) const { Cache->update( Hostname, cname ); } -void ResolverBase::update_cache( const uint32_t ttl ) const -{ Cache->update( Hostname, ttl ); } HostAddressVec ResolverBase::get_cached_ips_recursively(const std::string host, bool check_up_to_date) const diff --git a/src/dns/resolverbase.h b/src/dns/resolverbase.h index 3b0fa09..7ceea42 100644 --- a/src/dns/resolverbase.h +++ b/src/dns/resolverbase.h @@ -55,13 +55,13 @@ public: virtual void cancel_resolve() = 0; virtual bool is_resolving() = 0; - virtual HostAddress get_next_ip() = 0; + virtual HostAddress get_next_ip(const bool check_up_to_date=true) = 0; virtual bool have_up_to_date_ip() = 0; virtual int get_resolved_ip_count() = 0; std::string get_hostname() const; - std::string get_skipper() const; + std::string get_skip_cname() const; protected: ResolverBase(const IoServiceItem &io_serv, @@ -80,13 +80,12 @@ protected: virtual void do_resolve() = 0; // convenient forwards to DnsCache that just insert Hostname - void update_cache( const HostAddressVec &new_results ) const; + void update_cache( const HostAddressVec &new_ips ) const; void update_cache( const Cname &cname ) const; void update_cache( const std::string &hostname, - const HostAddressVec &new_results ) const; + const HostAddressVec &new_ips ) const; void update_cache( const std::string &hostname, const Cname &cname ) const; - void update_cache( const uint32_t ttl ) const; HostAddressVec get_cached_ips_recursively(const std::string host="", const bool check_up_to_date=false) const; diff --git a/src/dns/timetolive.h b/src/dns/timetolive.h index 70d9433..e3cd579 100644 --- a/src/dns/timetolive.h +++ b/src/dns/timetolive.h @@ -68,13 +68,11 @@ private: template void load(Archive & ar, const unsigned int version) { - uint32_t ttl_seconds; std::string ttl_creation_time; - ar & BOOST_SERIALIZATION_NVP(ttl_seconds); + ar & BOOST_SERIALIZATION_NVP(Ttl); ar & BOOST_SERIALIZATION_NVP(ttl_creation_time); // now convert to Ip and Ttl - Ttl = ttl_seconds; TtlSetTime = boost::posix_time::from_iso_string(ttl_creation_time); } diff --git a/src/host/pingscheduler.cpp b/src/host/pingscheduler.cpp index 2b476f4..694f6a7 100644 --- a/src/host/pingscheduler.cpp +++ b/src/host/pingscheduler.cpp @@ -23,11 +23,13 @@ on this file might be covered by the GNU General Public License. #include #include +#include #include #include "boost_assert_handler.h" #include "host/pingerfactory.h" +#include "dns/dnsmaster.h" #include "icmp/icmppinger.h" #include "link/linkstatus.h" @@ -67,22 +69,27 @@ PingScheduler::PingScheduler( const long ping_interval_in_sec, const int ping_fail_percentage_limit, const int ping_reply_timeout, - const int resolved_ip_ttl_threshold, LinkStatusItem link_analyzer, const int first_delay ) : IoService( io_serv ), NetworkInterfaceName( network_interface ), - Resolver(), DestinationAddress( destination_address ), DestinationPort( destination_port ), + Protocols( ping_protocol_list ), + ProtocolIter(), + PingIntervalInSec( ping_interval_in_sec ), + FirstDelay( first_delay ), NextPingTimer( *io_serv ), TimeSentLastPing( microsec_clock::universal_time() ), - PingIntervalInSec( ping_interval_in_sec ), + PingReplyTimeout( ping_reply_timeout ), HostAnalyzer( destination_address, ping_fail_percentage_limit, link_analyzer ), - FirstDelay( first_delay ), - Ping() + Resolver(), + Ping(), + WantToPing( false ), + LogPrefix(), + ContinueOnOutdatedIps( false ); { BOOST_ASSERT( !network_interface.empty() ); BOOST_ASSERT( !destination_address.empty() ); @@ -92,13 +99,7 @@ PingScheduler::PingScheduler( BOOST_ASSERT( (0 <= ping_fail_percentage_limit) && ( ping_fail_percentage_limit <= 100) ); - std::stringstream temp; - temp << "PS(" << DestinationAddress << "): "; - LogPrefix = temp.str(); - - // fill circular buffer with protocols - BOOST_FOREACH( const PingProtocol &prot, protocol_list ) - ProtocolRotate.push_back(prot); + update_log_prefix(); init_ping_protocol(); } @@ -164,30 +165,46 @@ void PingScheduler::ping(const boost::system::error_code &error) } -void PingRotate::try_to_ping() +void PingScheduler::try_to_ping() { if ( !WantToPing ) { - GlobalLogger.info() << "PingRotate: not pinging yet (not requested to)"; + GlobalLogger.info() << "PingScheduler: not pinging (not requested to)"; return; } else if ( Resolver && Resolver->is_resolving() ) { - GlobalLogger.info() << "PingRotate: not pinging yet (DNS not finished)"; + GlobalLogger.info() << "PingScheduler: not pinging (DNS not finished)"; return; } else if ( !Resolver ) // should not happen, but check anyway GlobalLogger.warning() << LogPrefix << "Have no resolver!"; - GlobalLogger.info() << "PingRotate: start ping"; + GlobalLogger.info() << "PingScheduler: start ping"; WantToPing = false; - Ping->ping( - Resolver->get_next_ip().get_ip(), - DestinationPort, - boost::bind(&PingScheduler::ping_done_handler, this, _1) - ); + HostAddress ip = Resolver->get_next_ip(); + if ( !ip.is_valid() && ContinueOnOutdatedIps) + { + GlobalLogger.info() << LogPrefix << "Checking for outdated IPs"; + bool check_up_to_date = false; + ip = Resolver->get_next_ip(check_up_to_date); + } + if ( ip.is_valid() ) + Ping->ping( + Resolver->get_next_ip().get_ip(), + DestinationPort, + boost::bind(&PingScheduler::ping_done_handler, this, _1) + ); + else + { // should not happen + GlobalLogger.error() << LogPrefix << "No IP to ping " + << "-- this should not have happened!!"; + WantToPing = true; + if ( !Resolver.is_resolving() ) + start_resolving_ping_address(); + } } @@ -254,12 +271,13 @@ void PingScheduler::update_ping_elapsed_time() // Ping Protocol Rotation //------------------------------------------------------------------------------ -void PingRotate::init_ping_protocol() +void PingScheduler::init_ping_protocol() { + ProtocolIter = Protocols.end(); get_next_ping_protocol(); } -void PingRotate::update_ping_protocol() +void PingScheduler::update_ping_protocol() { if ( can_change_ping_protocol() ) { @@ -267,11 +285,14 @@ void PingRotate::update_ping_protocol() } } -void PingRotate::get_next_ping_protocol() +void PingScheduler::get_next_ping_protocol() { - PingProtocol ping_protocol = ProtocolRotate.front(); - ProtocolRotate.pop_front(); - ProtocolRotate.push_back(ping_protocol); + ++ProtocolIter; + if (ProtocolIter == Protocols.end()) + ProtocolIter = Protocols.begin(); + PingProtocol ping_protocol = *ProtocolIter; + // --> ProtocolIter still points to currently used protocol which is + // required in dns_resolve_callback if (Ping) Ping->stop_pinging(); @@ -282,7 +303,7 @@ void PingRotate::get_next_ping_protocol() update_dns_resolver( ping_protocol ); } -bool PingRotate::can_change_ping_protocol() const +bool PingScheduler::can_change_ping_protocol() const { // TODO can_change_ping_protocol() and get_next_ping_protocol() may be implemented in a Algorithm // class that can be exchanged in this class to provide an algorithm neutral class @@ -292,8 +313,19 @@ bool PingRotate::can_change_ping_protocol() const //------------------------------------------------------------------------------ // DNS host name resolution //------------------------------------------------------------------------------ -// -void PingRotate::update_dns_resolver( PingProtocol current_protocol ) + +// show "!" after host name if running on outdated IPs +void update_log_prefix() +{ + std::stringstream temp; + temp << "PS(" << DestinationAddress; + if (ContinueOnOutdatedIps) + temp << "!"; + temp << "): "; + LogPrefix = temp.str(); +} + +void PingScheduler::update_dns_resolver( PingProtocol current_protocol ) { if (Resolver && Resolver->is_resolving()) { @@ -311,7 +343,7 @@ void PingRotate::update_dns_resolver( PingProtocol current_protocol ) if ( Resolver->have_up_to_date_ip() ) { if (!Resolver->is_resolving()) - GlobalLogger.warning() << "PingRotate: have up to date IPs but " + GlobalLogger.warning() << "PingScheduler: have up to date IPs but " << "resolver seems to be resolving all the same... " << "Start pinging anyway!"; try_to_ping(); @@ -320,28 +352,39 @@ void PingRotate::update_dns_resolver( PingProtocol current_protocol ) start_resolving_ping_address(); } -void PingRotate::start_resolving_ping_address() //lint !e1762 +void PingScheduler::start_resolving_ping_address() { - Resolver->async_resolve( boost::bind(&PingRotate::dns_resolve_callback, + Resolver->async_resolve( boost::bind(&PingScheduler::dns_resolve_callback, this, _1, _2) ); } -void PingRotate::dns_resolve_callback(const bool was_success, - const int cname_count) +void PingScheduler::dns_resolve_callback(const bool was_success, + const int cname_count) { - GlobalLogger.info() << "PingRotate: dns resolution finished " + GlobalLogger.info() << "PingScheduler: dns resolution finished " << "with success = " << was_success << " " << "and cname_count = " << cname_count; - if ( !was_success ) - { // host name resolution failed; try again bypassing first CNAME(s) - std::string skip_host = Resolver->get_skipper(); + // TODO this is too simple, but need to think more about how to update here! + // (may have to switch back some time to resolver for original host or so + ContinueOnOutdatedIps = true; + update_log_prefix(); + + if ( was_success ) + { + HostAnalyzer.set_resolved_ip_count( Resolver->get_resolved_ip_count()); + try_to_ping(); + } + else + { // host name resolution failed; try again bypassing first outdated CNAME + + std::string skip_host = Resolver->get_skip_cname(); if (skip_host.empty()) { GlobalLogger.notice() << LogPrefix << "DNS failed, " << "try anyway with cached data"; - HostAnalyzer->set_resolved_ip_count(0); + HostAnalyzer.set_resolved_ip_count(0); try_to_ping(); } else @@ -350,13 +393,10 @@ void PingRotate::dns_resolve_callback(const bool was_success, << "try again skipping a CNAME and resolving " << skip_host << " directly"; Resolver = DnsMaster::get_instance() - ->get_resolver_for(skip_host, PingRotate.back()); + ->get_resolver_for(skip_host, *ProtocolIter); + // the original resolver is still alive and cached by DnsMaster + // and counting down the time to re-try on its own start_resolving_ping_address(); } } - else - { - HostAnalyzer->set_resolved_ip_count( Resolver->get_resolved_ip_count()); - try_to_ping(); - } } diff --git a/src/host/pingscheduler.h b/src/host/pingscheduler.h index 250bf85..e61b0b0 100644 --- a/src/host/pingscheduler.h +++ b/src/host/pingscheduler.h @@ -33,7 +33,7 @@ on this file might be covered by the GNU General Public License. #include "host/pinger.h" #include "host/pinginterval.h" #include "host/pingprotocol.h" -#include "host/pingrotate.h" +#include "dns/resolverbase.h" //----------------------------------------------------------------------------- // PingScheduler @@ -56,7 +56,6 @@ public: const long ping_interval_in_sec, const int ping_fail_percentage_limit, const int ping_reply_timeout, - const int resolved_ip_ttl_threshold, LinkStatusItem link_analyzer, const int first_delay ); @@ -70,9 +69,7 @@ private: // Methods // - void resolve_and_ping(const boost::system::error_code &error); - - void ping(); + void ping(const boost::system::error_code &error); void ping_done_handler(const bool ping_success); void update_ping_interval(); void update_ping_elapsed_time(); @@ -86,45 +83,47 @@ private: void try_to_ping(); void dns_resolve_callback(const bool was_success, const int cname_count); + void start_resolving_ping_address(); + + void update_log_prefix(); // // Attributes // - /// Timer to trigger the next ping - boost::asio::deadline_timer NextPingTimer; - /// Timer to trigger the next attempt to resolve addresses - boost::asio::deadline_timer NextAddressTimer; - /// Keeps track of the time when the last ping was send - boost::posix_time::ptime TimeSentLastPing; - /// Interval between each ping to the same host - PingInterval PingIntervalInSec; - /// Interval between attempts to resolve host address - PingInterval AddressResolveIntervalInSec; - /// Object responsible to evaluate the status of the host - HostStatus HostAnalyzer; - /// delay for very first ping to avoid lots of simultaneous pings - int FirstDelay; /// The IO service object, which has the loop event IoServiceItem IoService; /// The network interface name std::string NetworkInterfaceName; - /// The Dns resolver - ResolverItem Resolver; /// The address to ping std::string DestinationAddress; - /// The port to ping at destination host (same port to all protocols in the list) + /// The port to ping at destination host (same for all protocols) uint16_t DestinationPort; - /// time threshold for address resolution - int ResolvedIpTtlThreshold; - /// timeout for ping reply + /// The list of protocols to ping and iterator over them + PingProtocolList Protocols; + PingProtocolList::const_iterator ProtocolIter; + /// Interval between each ping to the same host + PingInterval PingIntervalInSec; + /// delay for very first ping to avoid lots of simultaneous pings at startup + int FirstDelay; + /// Timer to trigger the next ping + boost::asio::deadline_timer NextPingTimer; + /// Keeps track of the time when the last ping was send + boost::posix_time::ptime TimeSentLastPing; + /// time threshold for ping const int PingReplyTimeout; - /// The list of protocols to ping - CircularProtocolList ProtocolRotate; - /// Internal boost pinger object + /// Object responsible to evaluate the status of the host + HostStatus HostAnalyzer; + /// The Dns resolver + ResolverItem Resolver; + /// The actual pinger PingerItem Ping; /// a flag whether we should ping as soon as dns is ready bool WantToPing; + /// Prefix to log messages for quicker analysis/debugging + std::string LogPrefix; + /// Flag whether DNS resolution has failed so we have to run on outdated IPs + bool ContinueOnOutdatedIps; }; //----------------------------------------------------------------------------- diff --git a/src/icmp/icmppinger.cpp b/src/icmp/icmppinger.cpp index 501a8b7..e6d51b6 100644 --- a/src/icmp/icmppinger.cpp +++ b/src/icmp/icmppinger.cpp @@ -127,8 +127,6 @@ void IcmpPinger::ping( function ping_done_callback ) { - BOOST_ASSERT( !destination_ip.empty() ); - PingDoneCallback = ping_done_callback; // Prepare ping diff --git a/src/main.cpp b/src/main.cpp index 46d0f58..972c0f7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -235,7 +235,6 @@ void init_pingers( string default_network_interface = configuration->get_source_network_interface(); int ping_fail_limit = configuration->get_ping_fail_limit(); int ping_reply_timeout = configuration->get_ping_reply_timeout(); - int resolved_ip_ttl_threshold = configuration->get_resolved_ip_ttl_threshold(); // remove some hosts at random configuration->randomize_hosts(); @@ -274,7 +273,6 @@ void init_pingers( ping_interval_in_sec, ping_fail_limit, ping_reply_timeout, - resolved_ip_ttl_threshold, status_notifier, current_delay ) diff --git a/test/CMakeLists.test_dns.txt b/test/CMakeLists.test_dns.txt new file mode 100644 index 0000000..158e588 --- /dev/null +++ b/test/CMakeLists.test_dns.txt @@ -0,0 +1,32 @@ +# compiler: creates the binaries +add_executable(test_dns + ${CMAKE_SOURCE_DIR}/src/boost_assert_handler.cpp + ${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/dnsmaster.cpp + ${CMAKE_SOURCE_DIR}/src/dns/dnscache.cpp + ${CMAKE_SOURCE_DIR}/src/dns/resolverbase.cpp + ${CMAKE_SOURCE_DIR}/src/dns/dnsresolver.cpp + test_dns.cpp +) + +set(Boost_USE_STATIC_LIBS OFF) +set(Boost_USE_MULTITHREADED OFF) +set(Boost_USE_STATIC_RUNTIME OFF) +find_package(Boost 1.44 COMPONENTS serialization date_time REQUIRED) +include_directories(${Boost_INCLUDE_DIRS}) +link_directories(${Boost_LIBRARY_DIRS}) + +# package: boost-net-dns +include_directories(${CMAKE_SOURCE_DIR}/lib/boost-net-dns) + +# linker: link the program against the libraries +target_link_libraries( + test_dns + ${I2NCOMMON_LIBRARIES} + ${Boost_LIBRARIES} +) + +# cmake: invocation via "make test" +add_test(test_dns test_dns) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 3351090..7404ed2 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -39,6 +39,7 @@ include(CMakeLists.test_icmpv4header.txt) include(CMakeLists.test_icmpv6header.txt) include(CMakeLists.test_icmppacket.txt) include(CMakeLists.test_tcpheader.txt) +include(CMakeLists.test_dns.txt) # cmake: add a custom "make check" target which automatically builds the binary add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} DEPENDS @@ -56,4 +57,5 @@ add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} DEPENDS test_icmpv6header test_icmppacket test_tcpheader + test_dns ) diff --git a/test/test_dns.cpp b/test/test_dns.cpp new file mode 100644 index 0000000..8ce9793 --- /dev/null +++ b/test/test_dns.cpp @@ -0,0 +1,105 @@ +/* +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. +*/ + +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK + +#include + +#include +#include +#include + +#include "host/pingprotocol.h" +#include "dns/hostaddress.h" +#include "dns/dnsmaster.h" +#include "dns/dnscache.h" +#include "dns/resolverbase.h" + +//------------------------------------------------------------------------------ +// helper functions +//------------------------------------------------------------------------------ + +void create_master(); + +// TODO: try to run this only once for test suite +void create_master() +{ + // only create if it does not exist yet + DnsMasterItem master = DnsMaster::get_instance(); + if ( !master ) + { + boost::asio::ip::address name_server = + boost::asio::ip::address::from_string("127.0.0.1"); + int resolved_ip_ttl_threshold = 3; + int max_address_resolution_attempts = 2; + std::string cache_file = DO_NOT_USE_CACHE_FILE; + IoServiceItem io_serv; + + // create it + DnsMaster::create_master(io_serv, + name_server, + resolved_ip_ttl_threshold, + max_address_resolution_attempts, + cache_file); + master = DnsMaster::get_instance(); + + // simple checks + BOOST_CHECK_EQUAL( master->get_resolved_ip_ttl_threshold(), + resolved_ip_ttl_threshold ); + BOOST_CHECK_EQUAL( master->get_max_address_resolution_attempts(), + max_address_resolution_attempts ); + } +} + +//------------------------------------------------------------------------------ +// test suite +//------------------------------------------------------------------------------ + +BOOST_AUTO_TEST_SUITE( TestDns ) + +BOOST_AUTO_TEST_CASE( create_master ) +{ + create_master(); +} + +BOOST_AUTO_TEST_CASE( create_v4 ) +{ + create_master(); + + // create resolver + std::string hostname = "www.intra2net.com"; + ResolverItem resolver = DnsMaster::get_instance() + ->get_resolver_for(hostname, PingProtocol_ICMP); + BOOST_CHECK_EQUAL( resolver->get_hostname(), hostname ); + BOOST_CHECK( !resolver->is_resolving() ); + + // cancel should have no effect + resolver->cancel_resolve(); + BOOST_CHECK( !resolver->is_resolving() ); + + // should not have any ips since cache did not load anything + BOOST_CHECK_EQUAL( resolver->get_resolved_ip_count(), 0 ); + BOOST_CHECK( !resolver->have_up_to_date_ip() ); + std::string no_ip = "0.0.0.0"; + BOOST_CHECK_EQUAL( resolver->get_next_ip().get_ip().to_string(), no_ip ); +} + +BOOST_AUTO_TEST_SUITE_END() -- 1.7.1