created and passed first unit tests for DNS; finished recovery from PingScheduler...
authorChristian Herdtweck <christian.herdtweck@intra2net.com>
Wed, 29 Apr 2015 12:38:50 +0000 (14:38 +0200)
committerChristian Herdtweck <christian.herdtweck@intra2net.com>
Mon, 4 May 2015 14:57:58 +0000 (16:57 +0200)
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

src/dns/cname.h [new file with mode: 0644]
src/dns/dnscache.cpp
src/dns/dnscache.h
src/dns/dnsmaster.cpp
src/dns/dnsmaster.h
src/dns/dnsresolver.cpp
src/dns/resolverbase.cpp
src/host/pingscheduler.cpp
src/host/pingscheduler.h
test/test_dns.cpp

diff --git a/src/dns/cname.h b/src/dns/cname.h
new file mode 100644 (file)
index 0000000..7f301be
--- /dev/null
@@ -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 <boost/serialization/access.hpp>
+#include <boost/serialization/nvp.hpp>
+#include <boost/serialization/split_member.hpp>
+#include <boost/serialization/string.hpp>
+
+#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<class Archive>
+    void save(Archive & ar, const unsigned int version) const
+    {
+        ar & BOOST_SERIALIZATION_NVP(Host);
+        ar & BOOST_SERIALIZATION_NVP(Ttl);
+    }
+    template<class Archive>
+    void load(Archive & ar, const unsigned int version)
+    {
+        ar & BOOST_SERIALIZATION_NVP(Host);
+        ar & BOOST_SERIALIZATION_NVP(Ttl);
+    }
+    BOOST_SERIALIZATION_SPLIT_MEMBER()
+};
+
+#endif
index d306fd1..4b8df17 100644 (file)
@@ -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)
index eaca600..3bc1758 100644 (file)
 #include <boost/system/error_code.hpp>
 #include <boost/archive/xml_oarchive.hpp>
 #include <boost/archive/xml_iarchive.hpp>
-#include <boost/serialization/access.hpp>
-#include <boost/serialization/nvp.hpp>
-#include <boost/serialization/split_member.hpp>
-#include <boost/serialization/string.hpp>
 
 #include "host/pinger.h"    // for IoserviceItem
 #include "dns/hostaddress.h"
-#include "dns/timetolive.h"
+#include "dns/cname.h"
 
 typedef std::vector<HostAddress> HostAddressVec;
 typedef std::map<std::string, HostAddressVec> 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<class Archive>
-    void save(Archive & ar, const unsigned int version) const
-    {
-        ar & BOOST_SERIALIZATION_NVP(Host);
-        ar & BOOST_SERIALIZATION_NVP(Ttl);
-    }
-    template<class Archive>
-    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<std::string, Cname> 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();
index 609924d..9592e1c 100644 (file)
@@ -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,
index 27470a9..1976ba5 100644 (file)
@@ -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();
 
index 3240e31..a1c81ad 100644 (file)
@@ -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();
 }
 
index 82b6c29..9f36e68 100644 (file)
@@ -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,
index 694f6a7..e2267fd 100644 (file)
@@ -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)
         }
     }
 }
index e61b0b0..c37f3c1 100644 (file)
@@ -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();
 
index 8ce9793..3c60bf3 100644 (file)
@@ -26,68 +26,272 @@ on this file might be covered by the GNU General Public License.
 #include <boost/test/unit_test.hpp>
 #include <boost/asio/io_service.hpp>
 #include <boost/asio/ip/address.hpp>
+#include <boost/bind.hpp>
+
+#include <logfunc.hpp>
 
 #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()