From: Christian Herdtweck Date: Wed, 29 Apr 2015 12:38:50 +0000 (+0200) Subject: created and passed first unit tests for DNS; finished recovery from PingScheduler... X-Git-Url: http://developer.intra2net.com/git/?p=pingcheck;a=commitdiff_plain;h=8d26221dccdfffd3a9d12e3f866256720ddf67fe created and passed first unit tests for DNS; finished recovery from PingScheduler::ContinueOnOutdatedIPs Also: * renamed PingScheduler::try_to_ping to PingScheduler::ping_when_ready * fixed string constant DnsCache::DoNotUseCacheFile * moved Cname to own header file * created constructor of DnsMaster with DnsCacheItem so can test cache offline * fixed retrieval of outdated CName --- diff --git a/src/dns/cname.h b/src/dns/cname.h new file mode 100644 index 0000000..7f301be --- /dev/null +++ b/src/dns/cname.h @@ -0,0 +1,63 @@ +/* + 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_CNAME_H +#define DNS_CNAME_H + +#include +#include +#include +#include + +#include "dns/timetolive.h" + +// ----------------------------------------------------------------------------- +// CNAME +// ----------------------------------------------------------------------------- + +struct Cname +{ // all public: + std::string Host; + TimeToLive Ttl; + + Cname(); + Cname(const std::string &host, const uint32_t ttl); + Cname(const std::string &host, const TimeToLive &ttl); + + // serialization + friend class boost::serialization::access; + template + void save(Archive & ar, const unsigned int version) const + { + ar & BOOST_SERIALIZATION_NVP(Host); + ar & BOOST_SERIALIZATION_NVP(Ttl); + } + template + void load(Archive & ar, const unsigned int version) + { + ar & BOOST_SERIALIZATION_NVP(Host); + ar & BOOST_SERIALIZATION_NVP(Ttl); + } + BOOST_SERIALIZATION_SPLIT_MEMBER() +}; + +#endif diff --git a/src/dns/dnscache.cpp b/src/dns/dnscache.cpp index d306fd1..4b8df17 100644 --- a/src/dns/dnscache.cpp +++ b/src/dns/dnscache.cpp @@ -42,6 +42,7 @@ using boost::bind; using boost::posix_time::seconds; using I2n::Logger::GlobalLogger; + namespace Config { int SaveTimerSeconds = 60; @@ -63,6 +64,9 @@ Cname::Cname(const std::string &host, const TimeToLive &ttl) , Ttl( ttl ) {} + +const string DnsCache::DoNotUseCacheFile = "do not use cache file!"; + DnsCache::DnsCache(const IoServiceItem &io_serv, const std::string &cache_file) : IpCache() @@ -126,7 +130,7 @@ void DnsCache::save_to_cachefile() else if (CacheFile.empty()) GlobalLogger.warning() << "DnsCache: skip saving because file name empty!"; - else if (CacheFile == DO_NOT_USE_CACHE_FILE) + else if (CacheFile == DoNotUseCacheFile) GlobalLogger.info() << "DnsCache: configured not to use cache file"; else { @@ -138,7 +142,8 @@ void DnsCache::save_to_cachefile() //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; + GlobalLogger.info() << "DnsCache: saved to cache file " + << CacheFile; HasChanged = false; } @@ -153,8 +158,8 @@ void DnsCache::load_from_cachefile() { if (CacheFile.empty()) GlobalLogger.warning() - << "DnsCache: cannot load because cache file name is empty!"; - else if (CacheFile == DO_NOT_USE_CACHE_FILE) + << "DnsCache: cannot load because cache file name is empty!"; + else if (CacheFile == DoNotUseCacheFile) GlobalLogger.info() << "DnsCache: configured not to use cache file"; else if ( !I2n::file_exists(CacheFile) ) GlobalLogger.warning() << "DnsCache: cannot load because cache file " @@ -172,8 +177,9 @@ void DnsCache::load_from_cachefile() } catch (boost::archive::archive_exception &exc) { - GlobalLogger.warning() << "DnsCache: archive exception loading from " - << CacheFile << ": " << exc.what(); + GlobalLogger.warning() + << "DnsCache: archive exception loading from " << CacheFile + << ": " << exc.what(); } catch (std::exception &exc) { @@ -316,10 +322,11 @@ HostAddressVec DnsCache::get_ips_recursive(const std::string &hostname, while ( result.empty() ) { current_cname = get_cname(current_host, check_up_to_date); - current_host = key_for_hostname(current_cname.Host); - if (current_host.empty()) + if (current_cname.Host.empty()) break; - else if (++n_recursions >= Config::MaxRetrievalRecursions) + + current_host = key_for_hostname(current_cname.Host); + if (++n_recursions >= Config::MaxRetrievalRecursions) { GlobalLogger.warning() << "DnsCache: reached recursion limit of " << n_recursions << " in recursive IP retrieval!"; @@ -344,7 +351,10 @@ HostAddressVec DnsCache::get_ips_recursive(const std::string &hostname, BOOST_FOREACH( HostAddress &addr, result ) { if (addr.get_ttl().get_value() > min_cname_ttl) + { + GlobalLogger.debug() << "DnsCache: using shorter CNAME TTL"; addr.set_ttl(cname_ttl); + } } } @@ -366,7 +376,7 @@ HostAddressVec DnsCache::get_ips_recursive(const std::string &hostname, std::string DnsCache::get_first_outdated_cname(const std::string &hostname, const uint32_t ttl_thresh) { - std::string up_to_date_host = hostname; + std::string first_outdated = hostname; Cname cname; int n_recursions = 0; while (true) @@ -378,18 +388,20 @@ std::string DnsCache::get_first_outdated_cname(const std::string &hostname, break; } - cname = get_cname(up_to_date_host); - if (key_for_hostname(cname.Host).empty()) + cname = get_cname(first_outdated); + if (cname.Host.empty()) // reached end of cname list break; else if (cname.Ttl.get_updated_value() > ttl_thresh) // cname is up to date --> continue looking - up_to_date_host = cname.Host; + first_outdated = cname.Host; else - // cname is out of date --> return that + { // cname is out of date --> return its target + first_outdated = cname.Host; break; + } } - return up_to_date_host; + return first_outdated; } // (created using vim -- the world's best text editor) diff --git a/src/dns/dnscache.h b/src/dns/dnscache.h index eaca600..3bc1758 100644 --- a/src/dns/dnscache.h +++ b/src/dns/dnscache.h @@ -30,54 +30,15 @@ #include #include #include -#include -#include -#include -#include #include "host/pinger.h" // for IoserviceItem #include "dns/hostaddress.h" -#include "dns/timetolive.h" +#include "dns/cname.h" typedef std::vector HostAddressVec; typedef std::map ip_map_type; - -// ----------------------------------------------------------------------------- -// CNAME -// ----------------------------------------------------------------------------- - -struct Cname -{ // all public: - std::string Host; - TimeToLive Ttl; - - Cname(); - Cname(const std::string &host, const uint32_t ttl); - Cname(const std::string &host, const TimeToLive &ttl); - - // serialization - friend class boost::serialization::access; - template - void save(Archive & ar, const unsigned int version) const - { - ar & BOOST_SERIALIZATION_NVP(Host); - ar & BOOST_SERIALIZATION_NVP(Ttl); - } - template - void load(Archive & ar, const unsigned int version) - { - ar & BOOST_SERIALIZATION_NVP(Host); - ar & BOOST_SERIALIZATION_NVP(Ttl); - } - BOOST_SERIALIZATION_SPLIT_MEMBER() -}; 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,6 +46,10 @@ const std::string DO_NOT_USE_CACHE_FILE = "do not use cache file!"; class DnsCache { public: + /// constant to give as cache_file arg to DnsCache constructor + /// indicating that no cache file should be used + static const std::string DoNotUseCacheFile; + DnsCache( const IoServiceItem &io_serv, const std::string &cache_file ); ~DnsCache(); diff --git a/src/dns/dnsmaster.cpp b/src/dns/dnsmaster.cpp index 609924d..9592e1c 100644 --- a/src/dns/dnsmaster.cpp +++ b/src/dns/dnsmaster.cpp @@ -35,12 +35,25 @@ using I2n::Logger::GlobalLogger; DnsMasterItem DnsMaster::TheOnlyInstance; +// just delegates work to other create_master function void DnsMaster::create_master(const IoServiceItem &io_serv, const boost::asio::ip::address &default_name_server, const int resolved_ip_ttl_threshold, const int max_address_resolution_attempts, const std::string &cache_file) { + GlobalLogger.info() << "Creating DNS Cache"; + DnsCacheItem cache( new DnsCache(io_serv, cache_file) ); + create_master(io_serv, default_name_server, resolved_ip_ttl_threshold, + max_address_resolution_attempts, cache); +} + +void DnsMaster::create_master(const IoServiceItem &io_serv, + const boost::asio::ip::address &default_name_server, + const int resolved_ip_ttl_threshold, + const int max_address_resolution_attempts, + const DnsCacheItem &cache) +{ if (TheOnlyInstance) { GlobalLogger.warning() @@ -48,8 +61,7 @@ void DnsMaster::create_master(const IoServiceItem &io_serv, return; } - GlobalLogger.info() << "Creating DNS Cache and Master"; - DnsCacheItem cache( new DnsCache(io_serv, cache_file) ); + GlobalLogger.info() << "Creating DNS Master"; TheOnlyInstance.reset( new DnsMaster(io_serv, default_name_server, resolved_ip_ttl_threshold, @@ -58,7 +70,6 @@ void DnsMaster::create_master(const IoServiceItem &io_serv, ); } - DnsMaster::DnsMaster(const IoServiceItem &io_serv, const boost::asio::ip::address &default_name_server, const int resolved_ip_ttl_threshold, diff --git a/src/dns/dnsmaster.h b/src/dns/dnsmaster.h index 27470a9..1976ba5 100644 --- a/src/dns/dnsmaster.h +++ b/src/dns/dnsmaster.h @@ -102,6 +102,11 @@ public: const int resolved_ip_ttl_threshold, const int max_address_resolution_attempts, const std::string &cache_file); + static void create_master(const IoServiceItem &io_serv, + const boost::asio::ip::address &default_name_server, + const int resolved_ip_ttl_threshold, + const int max_address_resolution_attempts, + const DnsCacheItem &cache); // needed for unit test static DnsMasterItem& get_instance(); ~DnsMaster(); diff --git a/src/dns/dnsresolver.cpp b/src/dns/dnsresolver.cpp index 3240e31..a1c81ad 100644 --- a/src/dns/dnsresolver.cpp +++ b/src/dns/dnsresolver.cpp @@ -581,8 +581,8 @@ void DnsResolver::handle_resolve_timeout(const boost::system::error_code &error) { if ( error == boost::asio::error::operation_aborted ) // cancelled { - GlobalLogger.warning() << LogPrefix - << "Resolve timeout timer was cancelled!"; + GlobalLogger.info() << LogPrefix + << "Resolve timeout timer was cancelled!"; return; } else if (error) @@ -640,8 +640,8 @@ void DnsResolver::wait_timer_timeout_handler( if ( error == boost::asio::error::operation_aborted ) // cancelled { // assume that our code cancelled this timer, so callbacks will be // taken care of! - GlobalLogger.warning() << LogPrefix - << "Resolve wait timer was cancelled! "; + GlobalLogger.info() << LogPrefix + << "Resolve wait timer was cancelled! "; } else if (error) { // not sure what to do here, but callers waiting forever for a callback @@ -724,6 +724,8 @@ bool DnsResolver::have_up_to_date_ip() int DnsResolver::get_resolved_ip_count() { + // run with empty hostname (--> uses internal var Hostname) + // and check_up_to_date = true return ResolverBase::get_cached_ips_recursively("", true).size(); } diff --git a/src/dns/resolverbase.cpp b/src/dns/resolverbase.cpp index 82b6c29..9f36e68 100644 --- a/src/dns/resolverbase.cpp +++ b/src/dns/resolverbase.cpp @@ -64,10 +64,7 @@ std::string ResolverBase::get_skip_cname() const if ( first_out_of_date == Hostname ) return ""; else - // we want to skip this first outdated cname, so return what is - // directly behind it. - // If that is not a cname but ips, will return empty here - return Cache->get_cname(first_out_of_date).Host; + return first_out_of_date; } void ResolverBase::update_cache( const std::string &hostname, diff --git a/src/host/pingscheduler.cpp b/src/host/pingscheduler.cpp index 694f6a7..e2267fd 100644 --- a/src/host/pingscheduler.cpp +++ b/src/host/pingscheduler.cpp @@ -89,7 +89,7 @@ PingScheduler::PingScheduler( Ping(), WantToPing( false ), LogPrefix(), - ContinueOnOutdatedIps( false ); + ContinueOnOutdatedIps( false ) { BOOST_ASSERT( !network_interface.empty() ); BOOST_ASSERT( !destination_address.empty() ); @@ -161,11 +161,11 @@ void PingScheduler::ping(const boost::system::error_code &error) // ping as soon as dns is ready WantToPing = true; - try_to_ping(); + ping_when_ready(); } -void PingScheduler::try_to_ping() +void PingScheduler::ping_when_ready() { if ( !WantToPing ) { @@ -184,9 +184,11 @@ void PingScheduler::try_to_ping() GlobalLogger.info() << "PingScheduler: start ping"; WantToPing = false; + // try to get an up-to-date IP HostAddress ip = Resolver->get_next_ip(); + if ( !ip.is_valid() && ContinueOnOutdatedIps) - { + { // we failed to resolve --> try to use outdated IP GlobalLogger.info() << LogPrefix << "Checking for outdated IPs"; bool check_up_to_date = false; ip = Resolver->get_next_ip(check_up_to_date); @@ -202,9 +204,12 @@ void PingScheduler::try_to_ping() GlobalLogger.error() << LogPrefix << "No IP to ping " << "-- this should not have happened!!"; WantToPing = true; - if ( !Resolver.is_resolving() ) + if ( !Resolver->is_resolving() ) start_resolving_ping_address(); } + + // next time try with up-to-date IP + ContinueOnOutdatedIps = false; } @@ -315,7 +320,7 @@ bool PingScheduler::can_change_ping_protocol() const //------------------------------------------------------------------------------ // show "!" after host name if running on outdated IPs -void update_log_prefix() +void PingScheduler::update_log_prefix() { std::stringstream temp; temp << "PS(" << DestinationAddress; @@ -346,7 +351,7 @@ void PingScheduler::update_dns_resolver( PingProtocol current_protocol ) GlobalLogger.warning() << "PingScheduler: have up to date IPs but " << "resolver seems to be resolving all the same... " << "Start pinging anyway!"; - try_to_ping(); + ping_when_ready(); } else start_resolving_ping_address(); @@ -367,36 +372,38 @@ void PingScheduler::dns_resolve_callback(const bool was_success, // 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; + ContinueOnOutdatedIps = !was_success; update_log_prefix(); if ( was_success ) { HostAnalyzer.set_resolved_ip_count( Resolver->get_resolved_ip_count()); - try_to_ping(); + ping_when_ready(); } else { // host name resolution failed; try again bypassing first outdated CNAME + // or using cached IP std::string skip_host = Resolver->get_skip_cname(); if (skip_host.empty()) - { + { // continue with IP GlobalLogger.notice() << LogPrefix << "DNS failed, " << "try anyway with cached data"; HostAnalyzer.set_resolved_ip_count(0); - try_to_ping(); + ping_when_ready(); } else - { + { // have CNAME to continue GlobalLogger.notice() << LogPrefix << "DNS failed, " << "try again skipping a CNAME and resolving " << skip_host << " directly"; Resolver = DnsMaster::get_instance() ->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(); + + // (the original resolver is still alive and cached by DnsMaster and + // counting down time to re-try on its own until cancel_resolve) } } } diff --git a/src/host/pingscheduler.h b/src/host/pingscheduler.h index e61b0b0..c37f3c1 100644 --- a/src/host/pingscheduler.h +++ b/src/host/pingscheduler.h @@ -81,7 +81,7 @@ private: void update_dns_resolver( PingProtocol current_protocol ); - void try_to_ping(); + void ping_when_ready(); void dns_resolve_callback(const bool was_success, const int cname_count); void start_resolving_ping_address(); diff --git a/test/test_dns.cpp b/test/test_dns.cpp index 8ce9793..3c60bf3 100644 --- a/test/test_dns.cpp +++ b/test/test_dns.cpp @@ -26,68 +26,272 @@ on this file might be covered by the GNU General Public License. #include #include #include +#include + +#include #include "host/pingprotocol.h" #include "dns/hostaddress.h" #include "dns/dnsmaster.h" #include "dns/dnscache.h" #include "dns/resolverbase.h" +#include "dns/cname.h" + +using boost::asio::ip::address; //------------------------------------------------------------------------------ -// helper functions +// Global Test fixture (created once for test suite) //------------------------------------------------------------------------------ -void create_master(); +// constants for master +address name_server = address::from_string("127.0.0.1"); +int resolved_ip_ttl_threshold = 3; +int max_address_resolution_attempts = 2; +std::string cache_file = DnsCache::DoNotUseCacheFile; + +// rely on boost that there will only be one instance +struct GlobalFixture; +GlobalFixture *global_fixture; -// TODO: try to run this only once for test suite -void create_master() + +struct GlobalFixture { - // only create if it does not exist yet - DnsMasterItem master = DnsMaster::get_instance(); - if ( !master ) + + GlobalFixture() + : IoService() + , Cache() + , 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, + BOOST_TEST_MESSAGE("Create global fixture"); + + // setup logging so we can see output from out code + I2n::Logger::enable_stderr_log( true ); + I2n::Logger::set_log_level( I2n::Logger::LogLevel::Debug ); + I2n::Logger::GlobalLogger.info() << "Logging enabled for DnsTest"; + + // IoService + IoServiceItem io_service_temp( new boost::asio::io_service() ); + io_service_temp.swap( IoService ); + io_service_temp.reset(); + + // DNS Cache + DnsCacheItem cache_temp = DnsCacheItem( + new DnsCache(IoService, cache_file) ); + cache_temp.swap( Cache ); + cache_temp.reset(); + fill_cache(); + + // create master + DnsMaster::create_master(IoService, 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 ); + Cache); + Master = DnsMaster::get_instance(); + + // remember this instance, so we can later access all these variables + if (global_fixture == 0) + global_fixture = this; } -} + + ~GlobalFixture() + { + BOOST_TEST_MESSAGE("Destructing global fixture"); + IoService->stop(); + IoService.reset(); + Master.reset(); + } + + void fill_cache() + { + BOOST_TEST_MESSAGE( "Filling cache..." ); + { + HostAddress ip(address::from_string("192.168.42.1"), 61); + HostAddressVec ips; + ips.push_back(ip); + Cache->update("host1.test", ips); + } + + { + HostAddress ip1(address::from_string("192.168.42.2"), 92); + HostAddress ip2(address::from_string("192.168.42.3"), 93); + HostAddressVec ips; + ips.push_back(ip1); + ips.push_back(ip2); + Cache->update("host2_3.test", ips); + } + + { // cname.test --> host1.test + Cname cname("host1.test", 35); + Cache->update("cname.test", cname); + } + + { // cname2.test --> cname.test --> host1.test + Cname cname("cname.test", 33); + Cache->update("cname2.test", cname); + } + + { // cname3.test --> cname2.test --> cname.test --> host1.test + Cname cname("cname2.test", 37); + Cache->update("cname3.test", cname); + } + BOOST_TEST_MESSAGE( "Done filling cache." ); + } + + // these variables will not be available in test cases: + IoServiceItem IoService; + DnsCacheItem Cache; + DnsMasterItem Master; +}; + +// this causes above fixture to be created only once before tests start and +// destructed after tests end; however, variables are not accessible in test +// cases +BOOST_GLOBAL_FIXTURE( GlobalFixture ) + + +// using this as suite-level fixture makes variable Master accessible in all +// test cases +struct TestFixture +{ + IoServiceItem IoService; + DnsCacheItem Cache; + DnsMasterItem Master; + + TestFixture() + : IoService() + , Cache() + , Master() + { + BOOST_TEST_MESSAGE("Create test-level fixture"); + GlobalFixture *global = global_fixture; + IoService = global->IoService; + Cache = global->Cache; + Master = global->Master; + } + + virtual ~TestFixture() {} +}; //------------------------------------------------------------------------------ // test suite //------------------------------------------------------------------------------ -BOOST_AUTO_TEST_SUITE( TestDns ) +BOOST_FIXTURE_TEST_SUITE( TestDns, TestFixture ) BOOST_AUTO_TEST_CASE( create_master ) { - create_master(); + // 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 ); } -BOOST_AUTO_TEST_CASE( create_v4 ) +//------------------------------------------------------------------------------ +// test Cache +//------------------------------------------------------------------------------ +BOOST_FIXTURE_TEST_SUITE( TestDnsCache, TestFixture ) + +BOOST_AUTO_TEST_CASE( cache_retrieve_ip1 ) { - create_master(); + HostAddressVec ips = Cache->get_ips("host1.test"); + BOOST_CHECK_EQUAL( ips.size(), 1 ); + HostAddress ip = ips.front(); + BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.1" ); + BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 61 ); +} + + +BOOST_AUTO_TEST_CASE( cache_retrieve_ip2 ) +{ + HostAddressVec ips = Cache->get_ips("host2_3.test"); + BOOST_CHECK_EQUAL( ips.size(), 2 ); + HostAddress ip = ips[0]; + BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.2" ); + BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 92 ); + ip = ips[1]; + BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.3" ); + BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 93 ); +} +BOOST_AUTO_TEST_CASE( cache_retrieve_cname ) +{ + HostAddressVec ips = Cache->get_ips("cname.test"); + BOOST_CHECK( ips.empty() ); + + Cname cname = Cache->get_cname("cname.test"); + BOOST_CHECK_EQUAL( cname.Host, "host1.test" ); + BOOST_CHECK_EQUAL( cname.Ttl.get_value(), 35 ); +} + +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"); + BOOST_CHECK_EQUAL( ips.size(), 1 ); + HostAddress ip = ips.front(); + BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.1" ); + BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 35 ); +} + +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"); + BOOST_CHECK_EQUAL( ips.size(), 1 ); + HostAddress ip = ips.front(); + BOOST_CHECK_EQUAL( ip.get_ip().to_string(), "192.168.42.1" ); + BOOST_CHECK_EQUAL( ip.get_ttl().get_value(), 33 ); +} + +BOOST_AUTO_TEST_CASE( cache_skip_test1 ) +{ + // build a cname chain where first one is out of date + { // skip_chain_first -(120)-> skip_chain_second -(0)-> skip_chain_third + // -(120)-> skip_chain_fourth -(60)-> IPs + Cname first("skip_chain_second.test", 120); + Cache->update("skip_chain_first.test", first); + Cname second("skip_chain_third.test", 0); + Cache->update("skip_chain_second.test", second); + Cname third("skip_chain_fourth.test", 120); + Cache->update("skip_chain_third.test", third); + HostAddressVec ips; + ips.push_back( HostAddress( address::from_string("192.168.42.4"), 60) ); + Cache->update("skip_chain_fourth.test", ips); + } + + // normal recursive call should give nothing since all are outdated + bool check_up_to_date = true; + HostAddressVec ips = Cache->get_ips_recursive("skip_chain_first.test", + check_up_to_date); + BOOST_CHECK( ips.empty() ); + + // now try to skip + std::string first_outdated = Cache->get_first_outdated_cname( + "skip_chain_first.test", 5); + BOOST_CHECK_EQUAL( first_outdated, "skip_chain_third.test" ); +} + + +BOOST_AUTO_TEST_SUITE_END() // of TestDnsCache + + +// ----------------------------------------------------------------------------- +// test resolver +// ----------------------------------------------------------------------------- + +BOOST_FIXTURE_TEST_SUITE( TestDnsResolver, TestFixture ) + +void resolve_callback(IoServiceItem io_serv, ResolverItem resolver, + const bool was_success, const int cname_count); + +BOOST_AUTO_TEST_CASE( create_resolver_v4 ) +{ // create resolver std::string hostname = "www.intra2net.com"; - ResolverItem resolver = DnsMaster::get_instance() - ->get_resolver_for(hostname, PingProtocol_ICMP); + ResolverItem resolver = Master->get_resolver_for(hostname, + PingProtocol_ICMP); BOOST_CHECK_EQUAL( resolver->get_hostname(), hostname ); BOOST_CHECK( !resolver->is_resolving() ); @@ -102,4 +306,35 @@ BOOST_AUTO_TEST_CASE( create_v4 ) BOOST_CHECK_EQUAL( resolver->get_next_ip().get_ip().to_string(), no_ip ); } + +void resolve_callback(IoServiceItem io_serv, ResolverItem resolver, + const bool was_success, const int cname_count) +{ + resolver->cancel_resolve(); + io_serv->stop(); + BOOST_TEST_MESSAGE( "Stopped io_service" ); +} + + +BOOST_AUTO_TEST_CASE( do_resolve ) +{ + // create resolver + std::string hostname = "www.intra2net.com"; + ResolverItem resolver = Master->get_resolver_for(hostname, + PingProtocol_ICMP); + + // set callback + callback_type callback = boost::bind( resolve_callback, IoService, resolver, + _1, _2 ); + // start resolving + resolver->async_resolve(callback); + IoService->run(); + // this will block until io service is stopped in resolve_callback + + // check for result + BOOST_CHECK( resolver->have_up_to_date_ip() ); +} + +BOOST_AUTO_TEST_SUITE_END() // of TestDnsResolver + BOOST_AUTO_TEST_SUITE_END()