finished self-implementation of DNS resolver recursion; will now remove all that!
authorChristian Herdtweck <christian.herdtweck@intra2net.com>
Thu, 23 Apr 2015 08:38:20 +0000 (10:38 +0200)
committerChristian Herdtweck <christian.herdtweck@intra2net.com>
Mon, 4 May 2015 14:57:58 +0000 (16:57 +0200)
implemented resolving that avoids caching by creating non-recursive queries
that are directly talking to different name servers; will remove this now
because firewalls might prevent direct communication and do not want to
self-implement dnssec some time.
--> now remove own implementation of recursion in resolving and try to deal
    with cached results

src/dns/dnscache.cpp
src/dns/dnscache.h
src/dns/dnsmaster.cpp
src/dns/dnsmaster.h
src/dns/dnsresolver.cpp
src/dns/dnsresolver.h
src/dns/hostaddress.h
src/dns/resolverbase.cpp
src/dns/resolverbase.h
src/dns/timetolive.h

index f065223..e87c039 100644 (file)
@@ -36,6 +36,8 @@
 #include <boost/archive/xml_oarchive.hpp>
 #include <boost/archive/xml_iarchive.hpp>
 
+#include "dns/dnsmaster.h"
+
 using boost::bind;
 using boost::posix_time::seconds;
 using I2n::Logger::GlobalLogger;
@@ -43,10 +45,9 @@ using I2n::Logger::GlobalLogger;
 namespace Config
 {
     int SaveTimerSeconds = 60;
+    int MaxRetrievalRecursions = 10;
 }
 
-
-
 DnsCache::DnsCache(const IoServiceItem &io_serv,
                    const std::string &cache_file)
     : IpCache()
@@ -182,16 +183,16 @@ void DnsCache::update(const std::string &hostname,
 
 
 void DnsCache::update(const std::string &hostname,
-                      const std::string &cname)
+                      const Cname &cname)
 {
     GlobalLogger.info() << "DnsCache: update CNAME for " << hostname
-                        << " to " << cname;
+                        << " to " << cname.first;
     CnameCache[hostname] = cname;
     HasChanged = true;
 }
 
 
-void DnsCache::update_ttl(const std::string &hostname,
+void DnsCache::update(const std::string &hostname,
                           const uint32_t new_ttl)
 {
     GlobalLogger.info() << "DnsCache: ensure TTL for IPs for " << hostname
@@ -212,31 +213,72 @@ void DnsCache::update_ttl(const std::string &hostname,
 }
 
 
-HostAddressVec& DnsCache::get_ips(const std::string &hostname)
+HostAddressVec DnsCache::get_ips(const std::string &hostname,
+                                 const bool check_up_to_date)
 {
+    HostAddressVec result = IpCache[hostname];
+    if (check_up_to_date)
+    {
+        HostAddressVec result_up_to_date;
+        int threshold = DnsMaster::get_instance()
+                        ->get_resolved_ip_ttl_threshold();
+        BOOST_FOREACH( const HostAddress &addr, result )
+        {
+            if (addr.get_ttl().get_updated_value() > threshold)
+                result_up_to_date.push_back(addr);
+        }
+        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
-                        << " --> " << IpCache[hostname].size() << "-list";
-    return IpCache[hostname];
+                        << " --> " << result.size() << "-list";
+    return result;
 }
 
-std::string& DnsCache::get_cname(const std::string &hostname)
+std::string 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
-                        << " --> \"" << CnameCache[hostname] << "\"";
-    return CnameCache[hostname];
+                        << " --> \"" << result_obj.first << "\"";
+    if (check_up_to_date)
+    {
+        if (result_obj.second.get_updated_value() > DnsMaster::get_instance()
+                                              ->get_resolved_ip_ttl_threshold())
+            return result_obj.first;
+        else
+        {
+            GlobalLogger.debug() << "DnsCache: Cname is out of date";
+            return "";
+        }
+    }
+    else
+        return result_obj.first;
 }
 
-HostAddressVec& DnsCache::get_ips_recursive(const std::string &hostname)
+// underlying assumption in this function: for a hostname, the cache has either
+// a list of IPs saved or a cname saved, but never both
+HostAddressVec DnsCache::get_ips_recursive(const std::string &hostname,
+                                           const bool check_up_to_date)
 {
     std::string current_host = hostname;
-    HostAddressVec& result = get_ips(current_host);
+    HostAddressVec result = get_ips(current_host);
+    int n_recursions = 0;
     while ( result.empty() )
     {
-        current_host = get_cname(current_host);
+        current_host = get_cname(current_host, check_up_to_date);
         if (current_host.empty())
             break;
+        else if (++n_recursions >= Config::MaxRetrievalRecursions)
+        {
+            GlobalLogger.warning() << "DnsCache: reached recursion limit of "
+                << n_recursions << " in recursive IP retrieval!";
+            break;
+        }
         else
-            result = get_ips(current_host);
+            result = get_ips(current_host, check_up_to_date);
     }
     return result;
 }
index c3879ae..c213777 100644 (file)
 
 #include "host/pinger.h"    // for IoserviceItem
 #include "dns/hostaddress.h"
+#include "dns/timetolive.h"
 
 typedef std::vector<HostAddress> HostAddressVec;
 typedef std::map<std::string, HostAddressVec> ip_map_type;
-typedef std::map<std::string, std::string> cname_map_type;
+typedef std::pair<std::string, TimeToLive> Cname;
+typedef std::map<std::string, Cname> cname_map_type;
+
 
 class DnsCache
 {
@@ -45,11 +48,14 @@ public:
 
     // accessed from ResolverBase subclasses
     void update(const std::string &hostname, const HostAddressVec &new_data);
-    void update(const std::string &hostname, const std::string &cname);
-    void update_ttl(const std::string &hostname, const uint32_t ttl);
-    HostAddressVec& get_ips(const std::string &hostname);
-    std::string& get_cname(const std::string &hostname);
-    HostAddressVec& get_ips_recursive(const std::string &hostname);
+    void update(const std::string &hostname, const Cname &cname);
+    void update(const std::string &hostname, const uint32_t ttl);
+    HostAddressVec get_ips(const std::string &hostname,
+                            const bool check_up_to_date=false);
+    std::string get_cname(const std::string &hostname,
+                          const bool check_up_to_date=false);
+    HostAddressVec get_ips_recursive(const std::string &hostname,
+                                      const bool check_up_to_date=false);
 
 // variables
 private:
index 0346fc5..5b0a1ce 100644 (file)
@@ -103,7 +103,7 @@ ResolverItem& DnsMaster::get_resolver_for( const std::string &hostname,
                                            const PingProtocol &ping_protocol )
 {
     // find suitable DnsIpProtocol for ping protocol
-    DnsIpProtocol protocol = DnsMaster::ping2dns_protocol(ping_protocol);
+    DnsIpProtocol protocol = ping2dns_protocol(ping_protocol);
     return get_resolver_for(hostname, protocol);
 }
 
@@ -111,15 +111,13 @@ ResolverItem& DnsMaster::get_resolver_for( const std::string &hostname,
 ResolverItem& DnsMaster::get_resolver_for(const std::string &hostname,
                                           const DnsIpProtocol &protocol)
 {
-    DnsMasterItem master = get_instance();
-
     // create key to ResolverMap
     resolver_key_type key(hostname, protocol);
-    if ( master->ResolverMap.count(key) == 0 )
+    if ( ResolverMap.count(key) == 0 )
     {   // need to create a resolver
 
         // check if it is an ip address, so can create a simple pseudo resolver
-        if ( master->is_ip(hostname) )
+        if ( is_ip(hostname) )
         {
             boost::asio::ip::address ip
                               = boost::asio::ip::address::from_string(hostname);
@@ -132,20 +130,21 @@ ResolverItem& DnsMaster::get_resolver_for(const std::string &hostname,
             ResolverItem new_resolver( new IpPseudoResolver(IoService,
                                                             hostname,
                                                             Cache) );
-            master->ResolverMap[key] = new_resolver;
+            ResolverMap[key] = new_resolver;
         }
         else
         {
-            GlobalLogger.info() << "Creating Resolver for host " << hostname;
+            GlobalLogger.info() << "DnsMaster: Creating Resolver for host "
+                << hostname << " and protocol " << to_string(protocol);
             ResolverItem new_resolver( new DnsResolver(IoService,
                                                        hostname,
                                                        protocol,
                                                        Cache,
                                                        DefaultNameServer) );
-            master->ResolverMap[key] = new_resolver;
+            ResolverMap[key] = new_resolver;
         }
     }
-    return master->ResolverMap[key];
+    return ResolverMap[key];
 }
 
 // create resolver but do not remember it in ResolverMap
@@ -153,6 +152,15 @@ 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,
index 128633f..63e5be9 100644 (file)
@@ -61,6 +61,23 @@ typedef std::map<resolver_key_type, ResolverItem> resolver_map_type;
 
 std::string to_string(const DnsIpProtocol &protocol);
 
+/**
+ * Factory and Cache of DNS resolvers
+ * 
+ * to avoid having several resolvers resolving the same hostname which might
+ * result in conflicts with caching, this class is a singleton factory and the
+ * only place where Resolvers are constructed. They are remembered in an
+ * internal cache by hostname and IP version requested (v4, v6 or both).
+ * 
+ * During resolving, several different name servers will have to be queried for
+ * the same hostname. These recursive resolvers are created using
+ * get_recursor_for and are NOT cached, so they should only be used from another
+ * "regular" resolver (created using get_resolver_for)
+ *
+ * The DnsMaster also remembers a few global variables that can be queried
+ * using public getter functions and it creates the DnsCache used by all its
+ * resolvers
+ */
 class DnsMaster : boost::noncopyable
 {
 // Resolver factory
@@ -93,7 +110,7 @@ public:
 
 // storage of global variables
 public:
-    //boost::asio::ip::address &get_name_server() const;  // currently unused
+    //boost::asio::ip::address &get_default_name_server() const;  // unused
     int get_resolved_ip_ttl_threshold() const;
     int get_max_address_resolution_attempts() const;
 
index 5a9cef2..139219c 100644 (file)
@@ -45,7 +45,7 @@ using boost::posix_time::minutes;
 
 namespace Config
 {
-    const int ResolveTimeoutSeconds = 0;
+    const int ResolveTimeoutSeconds = 3;
     const int PauseBeforeRetrySeconds = 10;
     const int StaleDataLongtermMinutes = 15;
     const int DNS_PORT = 53;
@@ -224,17 +224,22 @@ void DnsResolver::handle_dns_result(const boost::system::error_code &error,
     //   owner of data and that exists until end of function
     // Items in answers list are shared_ptr to resource_base_t
     std::vector<host_addr_pair> result_ips;
-    std::vector<string_pair> result_cnames;
-    std::vector<string_pair> result_nameservers;
+    std::vector<src_cname_pair> result_cnames;
+    std::vector<string_pair> result_name_servers;
 
+    GlobalLogger.debug() << "Checking ANSWERS section of dns reply";
     gather_results(result_message.answers(), &result_ips, &result_cnames,
-                                                          &result_nameservers);
+                                                          &result_name_servers);
     // results should have the logical order
     // Hostname [ --> cname1 --> cname2 --> ... --> cnameN ] [ --> ips ]
 
     // remember cname list (if there were any)
-    BOOST_FOREACH( const string_pair &host_and_cname, result_cnames )
+    BOOST_FOREACH( const src_cname_pair &host_and_cname, result_cnames )
+    {
+        GlobalLogger.debug() << LogPrefix << "Remember CNAME "
+            << host_and_cname.first << " --> " << host_and_cname.second.first;
         ResolverBase::update_cache(host_and_cname.first, host_and_cname.second);
+    }
 
     if ( !result_ips.empty() )
         handle_ips( result_ips );
@@ -243,29 +248,38 @@ void DnsResolver::handle_dns_result(const boost::system::error_code &error,
         // re-start resolving with that
         handle_cname(result_cnames);
     else
-    {   // no answers --> check for nameservers in authorities section
-        if ( !result_nameservers.empty() )
+    {   // no answers --> check for name_servers in authorities section
+        if ( !result_name_servers.empty() )
             GlobalLogger.warning() << LogPrefix
                                    << "Received NS records in answers! "
                                    << "That is quite unexpected...";
+        GlobalLogger.debug() << "Checking AUTHORITIES section of dns reply";
         gather_results(result_message.authorites(), &result_ips,
-                       &result_cnames, &result_nameservers);
+                       &result_cnames, &result_name_servers);
+        GlobalLogger.debug() << "Checking ADDITIONALS section of dns reply";
         gather_results(result_message.additionals(), &result_ips,
-                       &result_cnames, &result_nameservers);
+                       &result_cnames, &result_name_servers);
 
-        // search for a nameserver for which an IP is given
+        // search for a name_server for which an IP is given
         bool have_recursed = false;
-        BOOST_FOREACH( const string_pair &nameserver, result_nameservers )
+        BOOST_FOREACH( const string_pair &name_server, result_name_servers )
         {
+            if (name_server.second == Hostname)
+            {   // in case we try to resolve IP for name server, do not use same
+                GlobalLogger.debug() << LogPrefix
+                    << "Name server found is same as hostname --> skip";
+                continue;
+            }
+
             // go through ips and look for match
             BOOST_FOREACH( const host_addr_pair &host_and_addr, result_ips )
             {
-                if (nameserver.second == host_and_addr.first)
+                if (name_server.second == host_and_addr.first)
                 {
-                    GlobalLogger.info() << LogPrefix << "Ask next nameserver "
-                        << nameserver.second << " with IP "
+                    GlobalLogger.info() << LogPrefix << "Ask next name_server "
+                        << name_server.second << " with IP "
                         << host_and_addr.second.get_ip() << " (responsible for "
-                        << nameserver.first << ")";
+                        << name_server.first << ")";
                     have_recursed = true;
                     handle_recurse( host_and_addr.second );
                     break;
@@ -276,8 +290,8 @@ void DnsResolver::handle_dns_result(const boost::system::error_code &error,
         }
 
         if ( !have_recursed )
-        {   // no nameserver with ip found -- strange
-            if (result_nameservers.empty())
+        {   // no name_server with ip found -- strange
+            if (result_name_servers.empty())
             {
                 GlobalLogger.error() << LogPrefix << "Result contained neither "
                     << "IP nor CNAME nor name server --> cannot proceed!";
@@ -285,22 +299,10 @@ void DnsResolver::handle_dns_result(const boost::system::error_code &error,
             }
             else
             {
-                // TODO: check in cache for nameserver ips?
-
                 GlobalLogger.warning() << LogPrefix
-                    << "There are " << result_nameservers.size()
-                    << " nameservers given but none with IP "
-                    << "--> need to resolve nameserver -- this sucks!";
-                //handle_recurse_without_ip(result_nameservers[0].second);
-
-                // would have to create a new resolver with previous nameserver
-                // to resolve new nameserver name; save in Recursor
-                // In callback reset Recursor, get ip(s) and continue in
-                // handle_recurse
-                GlobalLogger.warning() << LogPrefix << "Have not implemented "
-                    << "resolution of name server; I sincerely hope this never "
-                    << "happens or can be dealt with more easily another way!";
-                handle_unavailable();
+                    << "There are " << result_name_servers.size()
+                    << " name_servers given but none with IP!";
+                handle_recurse_without_ip(result_name_servers[0].second);
             }
         }
     }
@@ -308,16 +310,19 @@ void DnsResolver::handle_dns_result(const boost::system::error_code &error,
 
 void DnsResolver::gather_results(const boost::net::dns::rr_list_t *answers,
                                  std::vector<host_addr_pair> *result_ips,
-                                 std::vector<string_pair> *result_cnames,
-                                 std::vector<string_pair> *result_nameservers)
+                                 std::vector<src_cname_pair> *result_cnames,
+                                 std::vector<string_pair> *result_name_servers)
                                                                            const
 {
     using boost::net::dns::resource_base_t;
+    boost::posix_time::ptime now =boost::posix_time::second_clock::local_time();
     BOOST_FOREACH( boost::shared_ptr<resource_base_t> rr_item, *answers )
     {
         boost::net::dns::type_t rr_type = rr_item->rtype();
         uint32_t ttl = rr_item->ttl();
         std::string domain = rr_item->domain();
+        std::string expiry =
+                        boost::posix_time::to_simple_string(now + seconds(ttl));
 
         if (rr_type == boost::net::dns::type_a)
         {    // 'A' resource records carry IPv4 addresses
@@ -331,8 +336,9 @@ void DnsResolver::gather_results(const boost::net::dns::rr_list_t *answers,
                 ( dynamic_cast<boost::net::dns::a_resource *> (rr_item.get()) )
                 ->address();
             result_ips->push_back(host_addr_pair(domain, HostAddress(ip, ttl)));
-            GlobalLogger.debug() << LogPrefix << "IPv4 " << ip << " with TTL "
-                                 << ttl << "s for " << domain;
+            GlobalLogger.debug() << LogPrefix << domain << ": IPv4 " << ip
+                                 << " with TTL " << ttl << "s (until "
+                                 << expiry << ")";
         }
         else if (rr_type == boost::net::dns::type_a6)
         {   // 'AAAA' resource records carry IPv6 addresses
@@ -346,26 +352,30 @@ void DnsResolver::gather_results(const boost::net::dns::rr_list_t *answers,
                 ( dynamic_cast<boost::net::dns::a6_resource *> (rr_item.get()) )
                 ->address();
             result_ips->push_back(host_addr_pair(domain, HostAddress(ip, ttl)));
-            GlobalLogger.debug() << LogPrefix << "IPv6 " << ip << " with TTL "
-                                 << ttl << "s for " << domain;
+            GlobalLogger.debug() << LogPrefix << domain << ": IPv6 " << ip
+                                 << " with TTL " << ttl << "s (until "
+                                 << expiry << ")";
         }
         else if (rr_type == boost::net::dns::type_cname)
         {   // 'CNAME' resource records that carry aliases
             std::string cname =
                 (dynamic_cast<boost::net::dns::cname_resource *>(rr_item.get()))
                 ->canonicalname();
-            result_cnames->push_back( string_pair(domain, cname) );
-            GlobalLogger.debug() << LogPrefix << "CNAME " << cname
-                                 << " with TTL " << ttl << "s for " << domain;
+            result_cnames->push_back( src_cname_pair(domain,
+                                                     Cname(cname, ttl)) );
+            GlobalLogger.debug() << LogPrefix << domain << ": CNAME to "
+                                 << cname << " with TTL " << ttl << "s (until "
+                                 << expiry << ")";
         }
         else if (rr_type == boost::net::dns::type_ns)
-        {   // NS (nameserver) resource records
-            std::string nameserver =
+        {   // NS (name_server) resource records
+            std::string name_server =
                 (dynamic_cast<boost::net::dns::ns_resource *>(rr_item.get()))
                 ->nameserver();
-            result_nameservers->push_back( string_pair(domain, nameserver) );
-            GlobalLogger.debug() << LogPrefix << "NameServer " << nameserver
-                                 << " with TTL " << ttl << "s for " << domain;
+            result_name_servers->push_back( string_pair(domain, name_server) );
+            GlobalLogger.debug() << LogPrefix << "NameServer " << name_server
+                                 << " for " << domain << " with TTL " << ttl
+                                 << "s (until " << expiry << ")";
         }
         else if (rr_type == boost::net::dns::type_soa)
             GlobalLogger.debug() << LogPrefix << "SOA resource";
@@ -436,20 +446,22 @@ void DnsResolver::handle_ips(const std::vector<host_addr_pair> &result_ips)
 }
 
 
-void DnsResolver::handle_cname(const std::vector<string_pair> &result_cnames)
+void DnsResolver::handle_cname(const std::vector<src_cname_pair> &result_cnames)
 {
     // find the "last" cname in the list
     // Hostname --> cname1 --> cname2 --> ... --> cnameN
     // We assume here that this list might not be in order but that all cnames
-    //   form a single list (without a break)
+    //   form a single list (form one connected list and not several isolated)
+
+    // host_and_cname.second is a Cname, which is a pair (destination, ttl)
     std::string last_cname = "";
     bool could_be_last;
-    BOOST_REVERSE_FOREACH( const string_pair &host_and_cname, result_cnames )
+    BOOST_REVERSE_FOREACH( const src_cname_pair &host_and_cname, result_cnames )
     {
         could_be_last = true;
-        BOOST_REVERSE_FOREACH( const string_pair &other, result_cnames )
+        BOOST_REVERSE_FOREACH( const src_cname_pair &other, result_cnames )
         {
-            if (other.first == host_and_cname.second)
+            if (other.first == host_and_cname.second.first)
             {   // found cname for current cname
                 could_be_last = false;
                 break;
@@ -457,7 +469,7 @@ void DnsResolver::handle_cname(const std::vector<string_pair> &result_cnames)
         }
         if (could_be_last)
         {
-            last_cname = host_and_cname.second;
+            last_cname = host_and_cname.second.first;
             break;
         }
     }
@@ -468,9 +480,9 @@ void DnsResolver::handle_cname(const std::vector<string_pair> &result_cnames)
             << "Could not identify \"last\" CNAME to handle -- "
             << "maybe we encountered a CNAME loop? Anyway, cannot proceed!";
         GlobalLogger.info() << LogPrefix << "Result CNAMEs were:";
-        BOOST_FOREACH( const string_pair &host_and_cname, result_cnames )
+        BOOST_FOREACH( const src_cname_pair &host_and_cname, result_cnames )
             GlobalLogger.info() << LogPrefix << host_and_cname.first << " --> "
-                                             << host_and_cname.second;
+                                             << host_and_cname.second.first;
         handle_unavailable();
     }
     else
@@ -577,7 +589,7 @@ void DnsResolver::recursive_resolve_callback(const uint32_t min_ttl,
     else
         Recursor.reset();
 
-    f ( OperationCancelled )
+    if ( OperationCancelled )
     {   // async_resolve was cancelled --> callbacks already called
         GlobalLogger.info() << LogPrefix
                         << "Ignoring recursion results since we were cancelled";
@@ -586,7 +598,7 @@ void DnsResolver::recursive_resolve_callback(const uint32_t min_ttl,
     else if (was_success)
     {
         // make sure the saved TTL is not larger than the one we found here
-        ResolverBase::update_cache_ttl(min_ttl);
+        ResolverBase::update_cache(min_ttl);
         finalize_resolve(was_success, recursion_count+1);
     }
     else
@@ -597,6 +609,73 @@ void DnsResolver::recursive_resolve_callback(const uint32_t min_ttl,
 }
 
 
+void DnsResolver::handle_recurse_without_ip(const std::string &name_server)
+{
+    // get resolver for name_server
+    // save in Recursor although it is a "proper" resolver (so result is cached)
+    if (Recursor)
+    {
+        if (Recursor->is_resolving())
+        {
+            GlobalLogger.warning() << LogPrefix << "Recursor is resolving! "
+                                   << "Will cancel and reset";
+            Recursor->cancel_resolve();
+        }
+        else
+            GlobalLogger.warning() << LogPrefix
+                                   << "Recursor has not been reset!";
+        Recursor.reset();
+    }
+    Recursor = DnsMaster::get_instance()->get_resolver_for(name_server,
+                                                           Protocol);
+
+    // check for IPs in cache
+    if (Recursor->get_resolved_ip_count() == 0)
+    {
+        GlobalLogger.info() << LogPrefix
+                   << "Start to resolve address of name server " << name_server;
+        callback_type callback = boost::bind(
+                                     &DnsResolver::name_server_resolve_callback,
+                                     this, _1, _2);
+        Recursor->async_resolve( callback );
+    }
+    else
+    {
+        GlobalLogger.info() << LogPrefix << "Use cached ip for name server "
+                            << name_server;
+        HostAddress ip = Recursor->get_next_ip();
+        Recursor.reset();
+        handle_recurse(ip);
+    }
+}
+
+void DnsResolver::name_server_resolve_callback(const bool was_success,
+                                               const int recursion_count)
+{
+    if (OperationCancelled)
+    {
+        GlobalLogger.info() << LogPrefix
+                   << "Ignoring name server IP results since we were cancelled";
+        return;
+    }
+    else if (was_success)
+    {
+        HostAddress ip = Recursor->get_next_ip();
+        GlobalLogger.info() << LogPrefix << "Found IP " << ip.get_ip()
+            << " for name server " << Recursor->get_hostname();
+        Recursor.reset();
+        handle_recurse(ip);
+    }
+    else
+    {
+        GlobalLogger.info() << LogPrefix << "Failed to find IP for name server"
+            << Recursor->get_hostname() << " --> schedule retry";
+        Recursor.reset();
+        schedule_retry();
+    }
+}
+
+
 void DnsResolver::finalize_resolve(const bool was_success,
                                    const int recursion_count)
 {
@@ -648,6 +727,13 @@ bool DnsResolver::is_resolving()
 }
 
 
+/**
+ * cancel a earlier call to async_resolve
+ *
+ * callbacks will be called with was_success=false; all internal operations
+ * will be cancelled and internal callbacks (timers, dns results) have no
+ * effect any more
+ */
 void DnsResolver::cancel_resolve()
 {
     if ( !IsResolving )
@@ -667,7 +753,7 @@ void DnsResolver::cancel_resolve()
         Recursor->cancel_resolve(); // does not hurt even if it is not resolving
 
     // set before finalize_resolve so can check in finalize_resolve that ID is
-    //   always 0;  ID is not used any more since handle_dns_result stops if
+    //   always 0; ID is not used any more since handle_dns_result stops if
     //   OperationCancelled is true
     RequestId = 0;
 
@@ -676,7 +762,7 @@ void DnsResolver::cancel_resolve()
     finalize_resolve(was_success, recursion_count);
 
     // set after finalize_resolve, so can check in finalize_resolve that 
-    // cancel is never true
+    // OperationCancelled is never true
     OperationCancelled = true;
 
 }
@@ -694,19 +780,18 @@ void DnsResolver::handle_resolve_timeout(const boost::system::error_code &error)
     {
         GlobalLogger.warning() << LogPrefix
                                << "resolve timeout handler received error "
-                               << error;
-        return;
+                               << error << " --> retry";
+        schedule_retry();
     }
     else if ( OperationCancelled )
     {   // async_resolve was cancelled --> callbacks already called
         GlobalLogger.info() << LogPrefix
-                        << "Ignoring DNS timeout since we were cancelled";
+                            << "Ignoring DNS timeout since we were cancelled";
         return;
     }
     else
     {
         GlobalLogger.notice() << LogPrefix << "DNS resolving timed out";
-
         schedule_retry();
     }
 }
@@ -714,10 +799,11 @@ void DnsResolver::handle_resolve_timeout(const boost::system::error_code &error)
 
 void DnsResolver::schedule_retry()
 {
-    // clean up a bit
+    // this function is called in all sorts of error cases
+    // --> need to clean up a bit
     if ( Recursor )
     {
-        Recursor.cancel();
+        Recursor->cancel_resolve();
         Recursor.reset();
     }
     ResolveTimeoutTimer.cancel();
@@ -803,23 +889,7 @@ HostAddress DnsResolver::get_next_ip()
 
 bool DnsResolver::have_up_to_date_ip()
 {
-    // get cached data
-    HostAddressVec cached_data = ResolverBase::get_cached_ips_recursively();
-
-    // get threshold
-    uint32_t resolved_ip_ttl_threshold = static_cast<uint32_t>(
-                   DnsMaster::get_instance()->get_resolved_ip_ttl_threshold() );
-
-    // loop over addresses
-    BOOST_FOREACH( const HostAddress &addr, cached_data )
-    {
-        uint32_t ttl = addr.get_ttl().get_updated_value();
-        if ( ttl > resolved_ip_ttl_threshold )
-            return true;
-    }
-
-    // if not returned true by now, we have tried all IPs without success
-    return false;
+    return ! ResolverBase::get_cached_ips_recursively("", true).empty();
 }
 
 int DnsResolver::get_resolved_ip_count()
index 15d2a28..a705f50 100644 (file)
@@ -38,6 +38,7 @@
 
 
 typedef std::pair<std::string, std::string> string_pair;
+typedef std::pair<std::string, Cname> src_cname_pair;
 typedef std::pair<std::string, HostAddress> host_addr_pair;
 
 
@@ -81,19 +82,23 @@ private:
                            const std::size_t bytes_transferred);
     void handle_unavailable();
     void handle_ips(const std::vector<host_addr_pair> &result_ips);
-    void handle_cname(const std::vector<string_pair> &result_cnames);
+    void handle_cname(const std::vector<src_cname_pair> &result_cnames);
     void handle_recurse(const HostAddress &name_server);
+    void handle_recurse_without_ip(const std::string &name_server);
     void cname_resolve_callback(const bool was_success,
                                 const int recursion_count);
     void recursive_resolve_callback(const uint32_t min_ttl,
                                     const bool was_success,
                                     const int recursion_count);
+    void name_server_resolve_callback(const bool was_success,
+                                      const int recursion_count);
+    void schedule_retry();
     void finalize_resolve(const bool success, const int recursion_count=0);
     void stop_trying();
     void wait_timer_timeout_handler(const boost::system::error_code &error);
     void gather_results( const boost::net::dns::rr_list_t *answers,
                          std::vector<host_addr_pair> *result_ips,
-                         std::vector<string_pair> *result_cnames,
+                         std::vector<src_cname_pair> *result_cnames,
                          std::vector<string_pair> *result_nameservers ) const;
 
 // variables
index 9696843..d5fdf18 100644 (file)
@@ -28,7 +28,6 @@ on this file might be covered by the GNU General Public License.
 #include <boost/serialization/access.hpp>
 #include <boost/serialization/nvp.hpp>
 #include <boost/serialization/split_member.hpp>
-#include <boost/date_time/posix_time/posix_time.hpp>
 
 #include "dns/timetolive.h"
 
@@ -66,28 +65,18 @@ private:
     void save(Archive & ar, const unsigned int version) const
     {
         std::string ip = Ip.to_string();
-        std::string ttl_creation_time = boost::posix_time::to_iso_string(
-                                                                Ttl.TtlSetTime);
         ar & BOOST_SERIALIZATION_NVP(ip);
-        ar & BOOST_SERIALIZATION_NVP(Ttl.Ttl);
-        ar & BOOST_SERIALIZATION_NVP(ttl_creation_time);
+        ar & BOOST_SERIALIZATION_NVP(Ttl);
     }
 
     template<class Archive>
     void load(Archive & ar, const unsigned int version)
     {
         std::string ip;
-        uint32_t ttl_seconds;
-        std::string ttl_creation_time;
         ar & BOOST_SERIALIZATION_NVP(ip);
-        ar & BOOST_SERIALIZATION_NVP(ttl_seconds);
-        ar & BOOST_SERIALIZATION_NVP(ttl_creation_time);
-
-        // now convert to Ip and Ttl
+        ar & BOOST_SERIALIZATION_NVP(Ttl);
         Ip = boost::asio::ip::address::from_string(ip);
-        Ttl = TimeToLive();
-        Ttl.Ttl = ttl_seconds;
-        Ttl.TtlSetTime = boost::posix_time::from_iso_string(ttl_creation_time);
+
     }
 
     BOOST_SERIALIZATION_SPLIT_MEMBER()
index 721fc1e..d28b6eb 100644 (file)
@@ -52,30 +52,33 @@ ResolverBase::ResolverBase(const IoServiceItem &io_serv,
     , CallbackList()
 {}
 
+std::string ResolverBase::get_hostname() const
+{   return Hostname;   }
+
 void ResolverBase::update_cache( const std::string &hostname,
                                  const HostAddressVec &new_results ) const
 {   Cache->update( hostname, new_results );  }
 
 void ResolverBase::update_cache( const std::string &hostname,
-                                 const std::string &cname ) const
+                                 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 std::string &cname ) const
+void ResolverBase::update_cache( const Cname &cname ) const
 {   Cache->update( Hostname, cname );  }
 
-void ResolverBase::update_cache_ttl( const uint32_t ttl ) const
-{   Cache->update_ttl( Hostname, ttl );  }
+void ResolverBase::update_cache( const uint32_t ttl ) const
+{   Cache->update( Hostname, ttl );  }
 
-HostAddressVec& ResolverBase::get_cached_ips_recursively(const std::string host)
-                                                                           const
+HostAddressVec ResolverBase::get_cached_ips_recursively(const std::string host,
+                                                    bool check_up_to_date) const
 {
     if (host.empty())
-        return Cache->get_ips_recursive(Hostname);
+        return Cache->get_ips_recursive(Hostname, check_up_to_date);
     else
-        return Cache->get_ips_recursive(host);
+        return Cache->get_ips_recursive(host, check_up_to_date);
 }
 
 void ResolverBase::schedule_callbacks(const bool was_success,
index a063c6c..c731b66 100644 (file)
@@ -55,6 +55,8 @@ public:
     virtual bool have_up_to_date_ip() = 0;
     virtual int get_resolved_ip_count() = 0;
 
+    std::string get_hostname() const;
+
 protected:
     ResolverBase(const IoServiceItem &io_serv,
                  const std::string &hostname,
@@ -63,7 +65,7 @@ protected:
 // variables
 protected:
     IoServiceItem IoService;
-    std::string Hostname;
+    const std::string Hostname;
     DnsCacheItem Cache;
     callback_list_type CallbackList;
 
@@ -71,15 +73,17 @@ protected:
 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 std::string &cname ) const;
+    void update_cache( const Cname &cname ) const;
     void update_cache( const std::string &hostname,
                        const HostAddressVec &new_results ) const;
     void update_cache( const std::string &hostname,
-                       const std::string &cname ) const;
-    void update_cache_ttl( const uint32_t ttl ) const;
+                       const Cname &cname ) const;
+    void update_cache( const uint32_t ttl ) const;
 
-    HostAddressVec& get_cached_ips_recursively(const std::string host="") const;
+    HostAddressVec get_cached_ips_recursively(const std::string host="",
+                                       const bool check_up_to_date=false) const;
 
     void schedule_callbacks(const bool was_success,
                             const int cname_count);
index e56d9eb..f1162dd 100644 (file)
@@ -23,7 +23,10 @@ on this file might be covered by the GNU General Public License.
 #include <stdint.h>
 
 #include <boost/asio.hpp>
-//#include "dns/hostaddress.h"
+#include <boost/serialization/access.hpp>
+#include <boost/serialization/nvp.hpp>
+#include <boost/serialization/split_member.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
 
 // forward declaration
 class HostAddress;
@@ -57,6 +60,32 @@ private:
     /// the time when the time-to-live was set, so it is possible to know the
     /// elapsed time
     boost::posix_time::ptime TtlSetTime;
+
+    // serialization
+    friend class boost::serialization::access;
+    template<class Archive>
+    void save(Archive & ar, const unsigned int version) const
+    {
+        std::string ttl_creation_time = boost::posix_time::to_iso_string(
+                                                                    TtlSetTime);
+        ar & BOOST_SERIALIZATION_NVP(Ttl);
+        ar & BOOST_SERIALIZATION_NVP(ttl_creation_time);
+    }
+
+    template<class Archive>
+    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_creation_time);
+
+        // now convert to Ip and Ttl
+        Ttl = ttl_seconds;
+        TtlSetTime = boost::posix_time::from_iso_string(ttl_creation_time);
+    }
+
+    BOOST_SERIALIZATION_SPLIT_MEMBER()
 };
 
 #endif // TIME_TO_LIVE_H