changed how dns deals with cnames and recursion: remember cnames and implement recurs...
authorChristian Herdtweck <christian.herdtweck@intra2net.com>
Thu, 16 Apr 2015 16:08:32 +0000 (18:08 +0200)
committerChristian Herdtweck <christian.herdtweck@intra2net.com>
Mon, 4 May 2015 14:57:57 +0000 (16:57 +0200)
Experiments with own name server and recursive cnames showed that TTLs are not always the minimum.
This way we have better control about resulting TTLs
and debugging is easier since DNS caching is avoided.

Also create unique id for each dns message and check reply
and in main warn if using debug option max_exceptions.

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/resolverbase.cpp
src/dns/resolverbase.h
src/host/pingrotate.cpp
src/main.cpp

index d180a89..9e8d1e0 100644 (file)
@@ -48,7 +48,8 @@ namespace Config
 
 DnsCache::DnsCache(const IoServiceItem &io_serv,
                    const std::string &cache_file)
-    : DataCache()
+    : IpCache()
+    , CnameCache()
     , SaveTimer( *io_serv )
     , CacheFile( cache_file )
     , HasChanged( false )
@@ -82,13 +83,13 @@ void DnsCache::schedule_save(const boost::system::error_code &error)
 
     if ( error ==  boost::asio::error::operation_aborted )   // cancelled
     {
-        GlobalLogger.error() << "DNS Cache: SaveTimer was cancelled "
+        GlobalLogger.error() << "DnsCache: SaveTimer was cancelled "
                              << "--> no save and no re-schedule of saving!";
         return;
     }
     else if (error)
     {
-        GlobalLogger.error() << "DNS Cache: Received error " << error
+        GlobalLogger.error() << "DnsCache: Received error " << error
                              << " in schedule_save "
                              << "--> no save now but re-schedule saving";
     }
@@ -105,13 +106,13 @@ void DnsCache::save_to_cachefile()
 {
     if (!HasChanged)
     {
-        GlobalLogger.info() << "DNS Cache: skip saving because has not changed";
+        GlobalLogger.info() << "DnsCache: skip saving because has not changed";
         return;
     }
     else if (CacheFile.empty())
     {
         GlobalLogger.warning()
-                           << "DNS Cache: skip saving because file name empty!";
+                           << "DnsCache: skip saving because file name empty!";
         return;
     }
 
@@ -119,8 +120,9 @@ void DnsCache::save_to_cachefile()
     {
         std::ofstream ofs( CacheFile.c_str() );
         boost::archive::xml_oarchive oa(ofs);
-        oa << boost::serialization::make_nvp("DataCache", DataCache);
-        GlobalLogger.info() << "DNS Cache: saved to cache file " << CacheFile;
+        oa << boost::serialization::make_nvp("IpCache", IpCache);
+        oa << boost::serialization::make_nvp("CnameCache", CnameCache);
+        GlobalLogger.info() << "DnsCache: saved to cache file " << CacheFile;
 
         HasChanged = false;
     }
@@ -136,12 +138,12 @@ void DnsCache::load_from_cachefile()
     if (CacheFile.empty())
     {
         GlobalLogger.warning()
-                  << "DNS Cache: cannot load because cache file name is empty!";
+                  << "DnsCache: cannot load because cache file name is empty!";
         return;
     }
     else if ( !I2n::file_exists(CacheFile) )
     {
-        GlobalLogger.warning() << "DNS Cache: cannot load because cache file "
+        GlobalLogger.warning() << "DnsCache: cannot load because cache file "
                                << CacheFile << " does not exist!";
         return;
     }
@@ -152,43 +154,79 @@ void DnsCache::load_from_cachefile()
         std::ifstream ifs( CacheFile.c_str() );
         boost::archive::xml_iarchive ia(ifs);
 
-        ia >> boost::serialization::make_nvp("DataCache", cache);
-        GlobalLogger.info() << "DNS Cache: loaded from file " << CacheFile;
+        ia >> boost::serialization::make_nvp("IpCache", cache);
+        ia >> boost::serialization::make_nvp("CnameCache", cache);
+        GlobalLogger.info() << "DnsCache: loaded from file " << CacheFile;
     }
     catch (boost::archive::archive_exception &exc)
     {
-        GlobalLogger.warning() << "DNS Cache: archive exception loading from "
+        GlobalLogger.warning() << "DnsCache: archive exception loading from "
                                << CacheFile << ": " << exc.what();
     }
     catch (std::exception &exc)
     {
-        GlobalLogger.warning() << "DNS Cache: exception while loading from "
+        GlobalLogger.warning() << "DnsCache: exception while loading from "
                                << CacheFile << ": " << exc.what();
     }
 }
 
-/**
- * 
- * in case the cached set is equal to the given new one, the old one is kept
- *   with TTLs updated
- * @returns true if changed cache; returns false if new_data is same as cache
- */
 void DnsCache::update(const std::string &hostname,
                       const HostAddressVec &new_data)
 {
-    GlobalLogger.info() << "DNS Cache: update IPs for " << hostname
+    GlobalLogger.info() << "DnsCache: update IPs for " << hostname
                         << " to " << new_data.size() << "-list";
-    DataCache[hostname] = new_data;
+    IpCache[hostname] = new_data;
     HasChanged = true;
 }
 
 
-HostAddressVec& DnsCache::get_data(const std::string &hostname)
+void DnsCache::update(const std::string &hostname,
+                      const std::string &cname)
+{
+    GlobalLogger.info() << "DnsCache: update CNAME for " << hostname
+                        << " to " << cname;
+    CnameCache[hostname] = cname;
+    HasChanged = true;
+}
+
+
+void DnsCache::update_ttl(const std::string &hostname,
+                          const uint32_t new_ttl)
+{
+    GlobalLogger.info() << "DnsCache: ensure TTL for IPs for " << hostname
+                        << " is below " << new_ttl;
+    HostAddressVec ips = IpCache[hostname];
+    uint32_t current_ttl;
+    TimeToLive current_ttl;
+    BOOST_FOREACH( HostAddress &addr, ips )
+    {
+        current_ttl = addr.get_ttl();
+        if (current_ttl.get_value() > new_ttl)
+        {
+            current_ttl.set_value(new_ttl);
+            addr.set_ttl(current_ttl);
+            HasChanged = true;
+        }
+    }
+    IpCache[hostname] = ip;
+}
+
+
+HostAddressVec& DnsCache::get_ips(const std::string &hostname)
 {
-    GlobalLogger.info() << "DNS Cache: request IPs for " << hostname
-                        << " --> " << DataCache[hostname].size() << "-list";
-    return DataCache[hostname];
+    GlobalLogger.info() << "DnsCache: request IPs for " << hostname
+                        << " --> " << IpCache[hostname].size() << "-list";
+    return IpCache[hostname];
 }
 
+std::string& DnsCache::get_cname(const std::string &hostname)
+{
+    GlobalLogger.info() << "DnsCache: request CNAME for " << hostname
+                        << " --> " << CnameCache[hostname];
+    return CnameCache[hostname];
+}
+
+
+
 // (created using vim -- the world's best text editor)
 
index 9600901..d48f6c2 100644 (file)
@@ -33,7 +33,8 @@
 #include "dns/hostaddress.h"
 
 typedef std::vector<HostAddress> HostAddressVec;
-typedef std::map<std::string, HostAddressVec> cache_map_type;
+typedef std::map<std::string, HostAddressVec> ip_map_type;
+typedef std::map<std::string, std::string> cname_map_type;
 
 class DnsCache
 {
@@ -43,12 +44,16 @@ public:
     ~DnsCache();
 
     // accessed from ResolverBase subclasses
-    void update(const std::string &host_name, const HostAddressVec &new_data);
-    HostAddressVec& get_data(const std::string &hostname);
+    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);
 
 // variables
 private:
-    cache_map_type DataCache;
+    ip_map_type IpCache;
+    cname_map_type CnameCache;
     boost::asio::deadline_timer SaveTimer;
     std::string CacheFile;
     bool HasChanged;
index 31814e4..5fa5139 100644 (file)
@@ -36,10 +36,10 @@ using I2n::Logger::GlobalLogger;
 DnsMasterItem DnsMaster::TheOnlyInstance;
 
 void DnsMaster::create_master(const IoServiceItem &io_serv,
-                              const boost::asio::ip::address &name_server,
-                              const int resolved_ip_ttl_threshold,
-                              const int max_address_resolution_attempts,
-                              const std::string &cache_file)
+                            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)
 {
     if (TheOnlyInstance)
     {
@@ -51,7 +51,7 @@ void DnsMaster::create_master(const IoServiceItem &io_serv,
     GlobalLogger.info() << "Creating DNS Cache and Master";
     DnsCacheItem cache( new DnsCache(io_serv, cache_file) );
     TheOnlyInstance.reset( new DnsMaster(io_serv,
-                                         name_server,
+                                         default_name_server,
                                          resolved_ip_ttl_threshold,
                                          max_address_resolution_attempts,
                                          cache)
@@ -60,12 +60,12 @@ void DnsMaster::create_master(const IoServiceItem &io_serv,
 
 
 DnsMaster::DnsMaster(const IoServiceItem &io_serv,
-                     const boost::asio::ip::address &name_server,
+                     const boost::asio::ip::address &default_name_server,
                      const int resolved_ip_ttl_threshold,
                      const int max_address_resolution_attempts,
                      const DnsCacheItem &cache)
     : IoService( io_serv )
-    , NameServer( name_server )
+    , DefaultNameServer( default_name_server )
     , ResolvedIpTtlThreshold( resolved_ip_ttl_threshold )
     , MaxAddressResolutionAttempts( max_address_resolution_attempts )
     , Cache(cache)
@@ -108,8 +108,8 @@ ResolverItem& DnsMaster::get_resolver_for( const std::string &hostname,
 }
 
 
-ResolverItem& DnsMaster::get_resolver_for( const std::string &hostname,
-                                           const DnsIpProtocol &protocol )
+ResolverItem& DnsMaster::get_resolver_for(const std::string &hostname,
+                                    const DnsIpProtocol &protocol)
 {
     DnsMasterItem master = get_instance();
 
@@ -141,13 +141,26 @@ ResolverItem& DnsMaster::get_resolver_for( const std::string &hostname,
                                                        hostname,
                                                        protocol,
                                                        Cache,
-                                                       NameServer) );
+                                                       DefaultNameServer) );
             master->ResolverMap[key] = new_resolver;
         }
     }
     return master->ResolverMap[key];
 }
 
+// create resolver but do not remember it in ResolverMap
+ResolverItem DnsMaster::get_recursor_for(const std::string &hostname,
+                                         const DnsIpProtocol &protocol,
+                                    const boost::asio::ip::address &name_server)
+{
+    ResolverItem new_resolver( new DnsResolver(IoService,
+                                               hostname,
+                                               protocol,
+                                               Cache,
+                                               name_server) );
+    return new_resolver;
+}
+
 /**
  * return true if given hostname string actually is an IP
  *
index e431104..c47a3d2 100644 (file)
@@ -67,23 +67,26 @@ public:
     ResolverItem& get_resolver_for(const std::string &hostname,
                                    const PingProtocol &ping_protocol);
     ResolverItem& get_resolver_for(const std::string &hostname,
-                                   const DnsIpProtocol &protocol);
+                                   const DnsIpProtocol &protocol)
+    ResolverItem get_recursor_for(const std::string &hostname,
+                                  const DnsIpProtocol &protocol,
+                                  const boost::asio::ip::address &name_server);
 
 // implementation of singleton
 private:
     static DnsMasterItem TheOnlyInstance;
 
     DnsMaster(const IoServiceItem &io_serv,
-              const boost::asio::ip::address &name_server,
+              const boost::asio::ip::address &default_name_server,
               const int resolved_ip_ttl_threshold,
               const int max_address_resolution_attempts,
               const DnsCacheItem &cache);
 public:
     static void create_master(const IoServiceItem &io_serv,
-                              const boost::asio::ip::address &name_server,
-                              const int resolved_ip_ttl_threshold,
-                              const int max_address_resolution_attempts,
-                              const std::string &cache_file);
+                            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);
     static DnsMasterItem& get_instance();
     ~DnsMaster();
 
@@ -96,7 +99,7 @@ public:
 // variables
 private:
     IoServiceItem IoService;
-    const boost::asio::ip::address NameServer;
+    const boost::asio::ip::address DefaultNameServer;
     const int ResolvedIpTtlThreshold;
     const int MaxAddressResolutionAttempts;
     DnsCacheItem Cache;
index c215b77..b127c3d 100644 (file)
@@ -34,6 +34,8 @@
 #include <boost/function.hpp>
 #include <boost/net/dns.hpp>
 #include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/uuid/uuid.hpp>
+#include <boost/uuid/uuid_io.hpp>
 
 #include <logfunc.hpp>
 
@@ -47,7 +49,6 @@ namespace Config
     const int PauseBeforeRetrySeconds = 10;
     const int StaleDataLongtermMinutes = 15;
     const int DNS_PORT = 53;
-    const int UniqueID = 0xaffe;
 }
 
 DnsResolver::DnsResolver(IoServiceItem &io_serv,
@@ -59,6 +60,7 @@ DnsResolver::DnsResolver(IoServiceItem &io_serv,
     , Socket( *io_serv, ip::udp::endpoint(ip::udp::v4(), 0))
         // just connect to anything, will specify sender/receiver later
     , ReceiveBuffer()
+    , RequestBuffer()
     , Protocol( protocol )
     , NameServer( name_server, Config::DNS_PORT )
     , ResolveTimeoutTimer( *io_serv )
@@ -68,10 +70,13 @@ DnsResolver::DnsResolver(IoServiceItem &io_serv,
     , RetryCount( 0 )
     , IsResolving( false )
     , LogPrefix( "DnsResolver" )
+    , RandomIdGenerator()
+    , RequestId( 0 )
 {
     std::stringstream temp;
     temp << "Dns(" << ResolverBase::Hostname << "): ";
     LogPrefix = temp.str();
+
 }
 
 DnsResolver::~DnsResolver()
@@ -109,6 +114,7 @@ void DnsResolver::do_resolve()
             << "Call to do_resolve ignored since resolving already";
         return;
     }
+    IsResolving = true;
 
     GlobalLogger.info() << LogPrefix << "start resolving";
 
@@ -119,13 +125,17 @@ void DnsResolver::do_resolve()
 
     // create DNS request
     boost::net::dns::message dns_message( ResolverBase::Hostname,
-                                          boost::net::dns::type_all );
+                                          boost::net::dns::type_a); //all ); DEBUG
     dns_message.recursive(false);
     dns_message.action(boost::net::dns::message::query);
     dns_message.opcode(boost::net::dns::message::squery);
-    dns_message.id(Config::UniqueID);
-    boost::net::dns_buffer_t request_buffer;
-    dns_message.encode(request_buffer);
+
+    // create random ID for message
+    boost::uuids::uuid message_id = RandomIdGenerator();
+    memcpy( &RequestId, message_id.data, sizeof(RequestId) );
+    dns_message.id( RequestId );
+    GlobalLogger.debug() << LogPrefix << "Request has ID "
+                               << std::showbase << std::hex << dns_message.id();
 
     // setup receipt of reply
     Socket.async_receive_from(
@@ -144,11 +154,12 @@ void DnsResolver::do_resolve()
                                       this, boost::asio::placeholders::error) );
 
     // send dns request
+    dns_message.encode(RequestBuffer);
     size_t bytes_sent;
     try
     {
         bytes_sent = Socket.send_to(
-                                boost::asio::buffer(request_buffer.get_array()),
+                                boost::asio::buffer(RequestBuffer.get_array()),
                                 NameServer );
     }
     catch (boost::system::system_error &err)
@@ -172,7 +183,7 @@ void DnsResolver::do_resolve()
 
 
 void DnsResolver::handle_dns_result(const boost::system::error_code &error,
-                       const std::size_t bytes_transferred)
+                                    const std::size_t bytes_transferred)
 {
     if ( error ==  boost::asio::error::operation_aborted )   // cancelled
     {
@@ -189,29 +200,137 @@ void DnsResolver::handle_dns_result(const boost::system::error_code &error,
         return;
     }
 
+    GlobalLogger.debug() << LogPrefix << "Handling DNS result ("
+                         << bytes_transferred << " bytes transferred)";
+
     // next 3(+1) lines copied from boost/net/dns/resolver.hpp:
     // clamp the recvBuffer with the number of bytes transferred or decode buffr
     ReceiveBuffer.length(bytes_transferred);
     boost::net::dns::message result_message;
     result_message.decode( ReceiveBuffer );
 
+    // check ID
+    if (RequestId != result_message.id())
+        GlobalLogger.warning() << "Received answer for request ID "
+           << std::showbase << std::hex << result_message.id()
+           << " but expected ID " << RequestId;
+    else
+        GlobalLogger.debug() << LogPrefix << "Result has correct ID "
+                                      << std::showbase << std::hex << RequestId;
+
+    // loop over answers, remembering ips and cnames
     // work with a regular pointer to list of answers since result_message is
     //   owner of data and that exists until end of function
     // Items in answers list are shared_ptr to resource_base_t
-    boost::net::dns::rr_list_t *answers = result_message.answers();
-    if (answers->size() == 0)
-        handle_unavailable();
+    HostAddressVec ip_list;
+    std::vector<std::string> hosts_for_ips;
+    std::vector<string_pair> result_cnames;
+    std::vector<string_pair> result_nameservers;
 
-    // loop over answers, remembering ips and cnames
-    HostAddressVec result_ips;
-    std::vector<std::string> result_cnames;
+    gather_results(result_message.answers(), &ip_list, &hosts_for_ips,
+                   &result_cnames, &result_nameservers);
+
+    // remember cname tree (if there were any)
+    // assume each cname points to next ( source --> destination )
+    std::string source = ResolverBase::Hostname;
+    BOOST_FOREACH( const std::string &cname, result_cnames )
+    {
+        update_cache( source, cname );
+        source = cname;
+    }
+
+    // IPs point to last CNAME (or Hostname if no cnames given)
+    if ( !ip_list.empty() )
+    {
+        update_cache( source, ip_list );
+
+        // clean up
+        bool was_success = true;
+        finalize_resolve(was_success);
+    }
+    else if ( !result_cnames.empty() )
+    {   // no IPs but a cname --> re-start resolving with that
+        handle_cname(source);
+    }
+    else
+    {   // no answers --> check for nameservers in authorities section
+        // and corresponding IPs in additional section
+        if ( !result_nameservers.empty() )
+            GlobalLogger.warning() << "Received NS records in answers! "
+                                   << "That is quite unexpected..."
+        gather_results(result_message.authorites(), &ip_list, &hosts_for_ips,
+                       &result_cnames, &result_nameservers);
+        gather_results(result_message.additionals(), &ip_list, &hosts_for_ips,
+                       &result_cnames, &result_nameservers);
+
+        int index, index_found=-1;
+        // go through name servers
+        BOOST_FOREACH( const string_pair &nameserver, result_nameservers )
+        {
+            index = 0;
+            // go through ips and look for match
+            BOOST_FOREACH( const std::string &ip_host, hosts_for_ips )
+            {
+                if (nameserver.second == ip_host)
+                {
+                    index_found = index;
+                    break;
+                }
+                ++index;
+            }
+
+            if (index_found > -1)
+                break;
+        }
+        if (index_found > -1)
+        {   // have a name server with ip
+            handle_recurse(ip_list[index_found]);
+
+
+    GlobalLogger.info() << LogPrefix << "Have " << result_ips.size()
+                        << " IPs and " << result_cnames.size() << " CNAMEs";
+
+    // We expect either one single CNAME and no IPs or a list of IPs.
+    // But deal with other cases as well
+    if (result_ips.empty() && result_cnames.empty())
+        handle_unavailable();   // we just got crap, this is a dead end
+
+    bool do_resolve_cnames = !Config::DnsRequestsAreRecursive;
+
+    if (Config::DnsRequestsAreRecursive && !result_cnames.empty()
+                                        && result_ips.empty() )
+    {
+        GlobalLogger.warning() << LogPrefix << "CNAMES appear to be unresolved"
+            << " although DNS requests are recursive! --> try on our own";
+        do_resolve_cnames = true;
+    }
+    else
+        GlobalLogger.info() << LogPrefix << "Ignore CNAMES, assume they were "
+                                         << "resolved";
+
+    if (do_resolve_cnames)
+    {
+        BOOST_FOREACH( const std::string &cname, result_cnames )
+            handle_cname(cname);  // will schedule another DNS call
+    }
+
+    if ( !result_ips.empty() )
+        handle_ips(result_ips);
+}
+
+void DnsResolver::gather_results(const boost::net::dns::rr_list_t *answers,
+                                 HostAddressVec *result_ips,
+                                 std::vector<std::string> *hosts_for_ips,
+                                 std::vector<string_pair> *result_cnames,
+                                 std::vector<string_pair> *result_nameservers)
+                                                                           const
+{
     using boost::net::dns::resource_base_t;
     BOOST_FOREACH( boost::shared_ptr<resource_base_t> rr_item, *answers )
     {
-        GlobalLogger.debug() << LogPrefix << std::showbase << std::hex
-                             << static_cast<unsigned>(rr_item->rtype()) << ": ";
-        uint32_t ttl = rr_item->ttl();
         boost::net::dns::type_t rr_type = rr_item->rtype();
+        uint32_t ttl = rr_item->ttl();
+        std::string domain = rr_item->domain();
 
         if (rr_type == boost::net::dns::type_a)
         {    // 'A' resource records carry IPv4 addresses
@@ -224,7 +343,10 @@ void DnsResolver::handle_dns_result(const boost::system::error_code &error,
             boost::asio::ip::address_v4 ip =
                 ( dynamic_cast<boost::net::dns::a_resource *> (rr_item.get()) )
                 ->address();
-            result_ips.push_back( HostAddress(ip, ttl) );
+            hosts_for_ips->push_back( domain );
+            result_ips->push_back( HostAddress(ip, ttl) );
+            GlobalLogger.debug() << LogPrefix << "IPv4 " << ip << " with TTL "
+                                 << ttl << "s for " << domain;
         }
         else if (rr_type == boost::net::dns::type_a6)
         {   // 'AAAA' resource records carry IPv6 addresses
@@ -237,17 +359,29 @@ void DnsResolver::handle_dns_result(const boost::system::error_code &error,
             boost::asio::ip::address_v6 ip =
                 ( dynamic_cast<boost::net::dns::a6_resource *> (rr_item.get()) )
                 ->address();
-            result_ips.push_back( HostAddress(ip, ttl) );
+            hosts_for_ips->push_back( domain );
+            result_ips->push_back( HostAddress(ip, ttl) );
+            GlobalLogger.debug() << LogPrefix << "IPv6 " << ip << " with TTL "
+                                 << ttl << "s for " << domain;
         }
         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( cname );
+            result_cnames->push_back( string_pair(domain, cname) );
+            GlobalLogger.debug() << LogPrefix << "CNAME " << cname
+                                 << " with TTL " << ttl << "s for " << domain;
         }
         else if (rr_type == boost::net::dns::type_ns)
-            GlobalLogger.debug() << LogPrefix << "NS resource";
+        {   // NS (nameserver) resource records
+            std::string nameserver =
+                (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;
+        }
         else if (rr_type == boost::net::dns::type_soa)
             GlobalLogger.debug() << LogPrefix << "SOA resource";
         else if (rr_type == boost::net::dns::type_ptr)
@@ -263,25 +397,10 @@ void DnsResolver::handle_dns_result(const boost::system::error_code &error,
         else if (rr_type == boost::net::dns::type_axfr)
             GlobalLogger.debug() << LogPrefix << "axfr resource";
         else
-            GlobalLogger.debug() << LogPrefix << "unknown resource type";
+            GlobalLogger.debug() << LogPrefix << "unknown resource type: "
+                                 << std::showbase << std::hex
+                                 << static_cast<unsigned>(rr_item->rtype());
     }
-
-    GlobalLogger.info() << LogPrefix << "Have " << result_ips.size()
-                        << " IPs and " << result_cnames.size() << " CNAMEs";
-
-    // We expect either one single CNAME and no IPs or a list of IPs.
-    // But deal with other cases as well
-    if (result_ips.empty() && result_cnames.empty())
-        handle_unavailable();   // we just got crap, this is a dead end
-    else if ( !result_ips.empty() && !result_cnames.empty())
-        GlobalLogger.warning() << LogPrefix << "Have CNAMEs AND IPs "
-                               << "--> deal with both!";
-
-    BOOST_FOREACH( const std::string &cname, result_cnames )
-        handle_cname(cname);  // will schedule another DNS call
-
-    if ( !result_ips.empty() )
-        handle_ips(result_ips);
 }
 
 
@@ -327,7 +446,7 @@ void DnsResolver::handle_cname(const std::string &canonical_name)
 
 void DnsResolver::cname_resolve_callback(const std::string &canonical_name,
                                          const bool was_success,
-                                         const int cname_count)
+                                         const int recursion_count)
 {
     if (was_success)
         // tell cache to return cname's ips if queried for our hostname
@@ -336,25 +455,69 @@ void DnsResolver::cname_resolve_callback(const std::string &canonical_name,
     else
         GlobalLogger.info() << LogPrefix << "Cname resolution failed";
 
-    finalize_resolve(was_success, cname_count+1);
+    // cname counts like one recursion step more...
+    finalize_resolve(was_success, recursion_count+1);
+}
+
+void DnsResolver::handle_recurse(const HostAddress &name_server)
+{
+    // get resolver for same hostname but using a different name server
+    if (Recursor)
+    {
+        GlobalLogger.warning() << "Recursor has not been reset!";
+        Recursor.reset();
+    }
+
+    Recursor = DnsMaster::get_instance()->get_recursor_for(
+                                      Hostname, Protocol, name_server.get_ip());
+    callback_type callback = boost::bind(
+                                       &DnsResolver::recursive_resolve_callback,
+                                       this, name_server.get_ttl().get_value(),
+                                       _1, _2 );
+    Recursor->async_resolve( callback );
+
+    stop_trying();
+}
+
+
+
+void DnsResolver::recursive_resolve_callback(const uint32_t min_ttl,
+                                             const bool was_success,
+                                             const int recursion_count)
+{
+    if (was_success)
+        // make sure the saved TTL is not larger than the one we found here
+        ResolverBase::update_cache_ttl(min_ttl);
+    else
+        GlobalLogger.info() << LogPrefix << "Recursive resolution failed";
+
+    // do not need recursor any more; next time re-create from different random
+    //    name server
+    if ( !Recursor )
+        GlobalLogger.warning() << "Recursor was reset before callback!";
+    else
+        Recursor.reset();
+
+    finalize_resolve(was_success, recursion_count+1);
+
 }
 
 
 void DnsResolver::finalize_resolve(const bool was_success,
-                                   const int cname_count)
+                                   const int recursion_count)
 {
     // stop timers
-    if (cname_count > 0)
+    if (recursion_count > 0)
         stop_trying();
     // else was called already from handle_cname
 
     // schedule callbacks, clearing callback list
-    ResolverBase::schedule_callbacks(was_success, cname_count);
+    ResolverBase::schedule_callbacks(was_success, recursion_count);
 
     // finalize
     GlobalLogger.notice() << LogPrefix << "Done resolving"
                           << " with success = " << was_success
-                          << " and cname_count = " << cname_count;
+                          << " and recursion_count = " << recursion_count;
     IsResolving = false;
 }
 
index a75ea7f..7069610 100644 (file)
 #include <boost/asio/ip/address.hpp>
 #include <boost/system/error_code.hpp>
 #include <boost/net/network_array.hpp>   // dns_buffer_t
+#include <boost/uuid/uuid_generators.hpp>
 
 #include "dns/resolverbase.h"
 #include "dns/dnsmaster.h"
 #include "dns/dnscache.h"
 
 
+typedef std::pair<std::string, std::string> string_pair;
+
+
 class DnsResolver : public ResolverBase
 {
 public:
@@ -71,17 +75,26 @@ private:
     void handle_unavailable();
     void handle_ips(const HostAddressVec &ips);
     void handle_cname(const std::string &canonical_name);
+    void handle_recurse(const HostAddress &name_server);
     void cname_resolve_callback(const std::string &canonical_name,
                                 const bool was_success,
-                                const int cname_count);
-    void finalize_resolve(const bool success, const int cname_count=0);
+                                const int recursion_count);
+    void recursive_resolve_callback(const uint32_t min_ttl,
+                                    const bool was_success,
+                                    const int recursion_count);
+    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,
+                         HostAddressVec *result_ips,
+                         std::vector<string_pair> *result_cnames,
+                         std::vector<string_pair> *result_nameservers ) const;
 
 // variables
 private:
     boost::asio::ip::udp::socket Socket;
     boost::net::dns_buffer_t ReceiveBuffer;
+    boost::net::dns_buffer_t RequestBuffer;
     DnsIpProtocol Protocol;
     boost::asio::ip::udp::endpoint NameServer;
     boost::asio::deadline_timer ResolveTimeoutTimer;
@@ -91,6 +104,9 @@ private:
     int RetryCount;
     bool IsResolving;
     std::string LogPrefix;
+    boost::uuids::random_generator RandomIdGenerator;
+    uint16_t RequestId;
+    ResolverItem Recursor;
 };
 
 #endif
index dc0a6a6..6060729 100644 (file)
@@ -55,6 +55,12 @@ ResolverBase::ResolverBase(const IoServiceItem &io_serv,
 void ResolverBase::update_cache( const HostAddressVec &new_results ) const
 {   Cache->update( Hostname, new_results );  }
 
+void ResolverBase::update_cache( const std::string &cname ) const
+{   Cache->update( Hostname, cname );  }
+
+void ResolverBase::update_cache_ttl( const uint32_t ttl ) const
+{   Cache->update_ttl( Hostname, ttl );  }
+
 HostAddressVec& ResolverBase::get_cached_results(const std::string host) const
 {
     if (host.empty())
index d5f5e45..2dd435e 100644 (file)
@@ -70,6 +70,8 @@ protected:
     virtual void do_resolve() = 0;
 
     void update_cache( const HostAddressVec &new_results ) const;
+    void update_cache( const std::string &cname ) const;
+    void update_cache_ttl( const uint32_t ttl ) const;
 
     HostAddressVec& get_cached_results(const std::string host="") const;
 
index d3166a3..708f37d 100644 (file)
@@ -212,11 +212,12 @@ void PingRotate::update_dns_resolver( PingProtocol current_protocol )
 }
 
 void PingRotate::dns_resolve_callback(const bool was_success,
-                                      const int cname_count)
+                                      const int recursion_count)
 {
     GlobalLogger.info() << "PingRotate: dns resolution finished "
                         << "with success = " << was_success << " "
-                        << "and cname_count = " << cname_count;
+                        << "and recursion_count = " << recursion_count;
+    throw std::runtime_error("Only debugging DNS -- exit by error");
     if (DnsResolutionFinished)
     {   // there were probably several calls to async_resolve before it could
         // finish --> ignore this callback
index 19f6f38..46d0f58 100644 (file)
@@ -457,7 +457,8 @@ int main( int argc, const char *argv[] )
     IoServiceItem io_service;
     int ret_code = 0;
     unsigned n_exceptions = 0;
-    unsigned max_exceptions = 0;
+    unsigned max_exceptions = 1;
+
     try
     {
         GetConfigReturnType success_and_config = get_configuration( argc, argv );
@@ -531,6 +532,11 @@ int main( int argc, const char *argv[] )
     if ( ret_code == 0 )
     {
         GlobalLogger.info() << "starting io_service main loop" << endl;
+
+        if (max_exceptions > 0)
+            GlobalLogger.warning() << "Limited number of acceptable exceptions,"
+                                   << " this is a debugging option!";
+
         // call boost::asio main event loop, catching exceptions
         while ( !signal_data.stopped )
         {