continuing with dns rewrite; today realized that cache is better as own class and...
authorChristian Herdtweck <christian.herdtweck@intra2net.com>
Wed, 8 Apr 2015 16:54:25 +0000 (18:54 +0200)
committerChristian Herdtweck <christian.herdtweck@intra2net.com>
Mon, 4 May 2015 14:57:57 +0000 (16:57 +0200)
src/CMakeLists.txt
src/dns_neww/dnscache.cpp [new file with mode: 0644]
src/dns_neww/dnscache.h [new file with mode: 0644]
src/dns_neww/dnsmaster.cpp
src/dns_neww/dnsmaster.h
src/dns_neww/dnsresolver.cpp
src/dns_neww/dnsresolver.h
src/dns_neww/ippseudoresolver.h
src/dns_neww/resolverbase.h
src/icmp/icmppinger.h

index 2003089..e956995 100644 (file)
@@ -61,6 +61,9 @@ set(SOURCES
     config/option/pingreplytimeoutoption.cpp
     config/option/maxaddressresolutionattemptsoption.cpp
     config/option/resolvedipttlthresholdoption.cpp
+    dns_neww/dnscache.cpp
+    dns_neww/dnsmaster.cpp
+    dns_neww/dnsresolver.cpp
     dns/dnsresolver.cpp
     dns/dnsresolverfactory.cpp
     dns/hostaddress.cpp
diff --git a/src/dns_neww/dnscache.cpp b/src/dns_neww/dnscache.cpp
new file mode 100644 (file)
index 0000000..9e1b004
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ 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
+ */
+
+#include "dns_neww/dnscache.h"
+
+#include <logfunc.hpp>
+#include <boost/bind.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+#include <boost/asio/placeholders.hpp>
+
+using boost::bind;
+using boost::posix_time::seconds;
+using I2n::Logger::GlobalLogger;
+
+namespace Config
+{
+    int SaveTimerSeconds = 60;
+}
+
+
+
+DnsCache::DnsCache(const IoServiceItem &io_serv,
+                   const std::string &cache_file)
+    : DataCache()
+    , SaveTimer( *io_serv )
+    , CacheFile( cache_file )
+    , HasChanged( false )
+{
+    // load cache from file
+    load_from_cachefile();
+
+    // schedule next save
+    (void) SaveTimer.expires_from_now( seconds( Config::SaveTimerSeconds ) );
+    SaveTimer.async_wait( bind( &DnsCache::schedule_save, this,
+                                boost::asio::placeholders::error ) );
+}
+
+
+DnsCache::~DnsCache()
+{
+    // save one last time without re-scheduling the next save
+    save_to_cachefile();
+
+    // cancel save timer
+    SaveTimer.cancel();
+}
+
+
+void DnsCache::schedule_save(const boost::system::error_code &error)
+{
+    // just in case: ensure SaveTimer is cancelled
+    SaveTimer.cancel();  // (will do nothing if already expired/cancelled)
+
+    if ( error ==  boost::asio::error::operation_aborted )   // cancelled
+    {
+        GlobalLogger.error() << "DNS Cache: SaveTimer was cancelled "
+                             << "--> no save and no re-schedule of saving!";
+        return;
+    }
+    else if (error)
+    {
+        GlobalLogger.error() << "DNS Cache: Received error " << error
+                             << " in schedule_save "
+                             << "--> no save now but re-schedule saving";
+    }
+    else
+        save_to_cachefile();
+
+    // schedule next save
+    (void) SaveTimer.expires_from_now( seconds( Config::SaveTimerSeconds ) );
+    SaveTimer.async_wait( bind( &DnsCache::schedule_save, this,
+                                boost::asio::placeholders::error ) );
+}
+
+void DnsCache::save_to_cachefile()
+{
+    if (!HasChanged)
+    {
+        GlobalLogger.info() << "DNS Cache: skip saving because has not changed";
+        return;
+    }
+
+    // TODO (see trusted_net_helper, boost serialization, xml)
+    GlobalLogger.error() << "DNS Cache: Actual saving not implemented yet!";
+    HasChanged = false;
+}
+
+
+void DnsCache::load_from_cachefile()
+{
+    // TODO: some boost serialization and xml stuff, see trusted_net_helper
+    GlobalLogger.error() << "DNS Cache: Actual loading not implemented yet!";
+}
+
+/**
+ * 
+ * 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 HostAddressList &new_data)
+{
+    GlobalLogger.info() << "DNS Cache: update IPs for " << hostname
+                        << " to " << new_data.size() << "-list";
+    DataCache[hostname] = new_data;
+    HasChanged = true;
+}
+
+
+HostAddressList DnsCache::get_data(const std::string &hostname)
+{
+    GlobalLogger.info() << "DNS Cache: request IPs for " << hostname
+                        << " --> " << DataCache[hostname].size() << "-list";
+    return DataCache[hostname];
+}
+
+// (created using vim -- the world's best text editor)
+
diff --git a/src/dns_neww/dnscache.h b/src/dns_neww/dnscache.h
new file mode 100644 (file)
index 0000000..4e5d889
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ 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_CACHE_H
+#define DNS_CACHE_H
+
+#include <map>
+
+#include <boost/shared_ptr.hpp>
+#include <boost/asio/deadline_timer.hpp>
+#include <boost/system/error_code.hpp>
+
+#include "host/pinger.h"    // for IoserviceItem
+#include "dns/hostaddress.h"
+
+typedef std::map<std::string, HostAddressList> cache_map_type;
+
+class DnsCache
+{
+public:
+    DnsCache( const IoServiceItem &io_serv,
+              const std::string &cache_file );
+    ~DnsCache();
+
+    // accessed from ResolverBase subclasses
+    void update(const std::string &host_name, const HostAddressList &new_data);
+    HostAddressList get_data(const std::string &hostname);
+
+// variables
+private:
+    cache_map_type DataCache;
+    boost::asio::deadline_timer SaveTimer;
+    std::string CacheFile;
+    bool HasChanged;
+
+// functions
+private:
+    void schedule_save(const boost::system::error_code &error);
+    void save_to_cachefile();
+    void load_from_cachefile();
+
+};
+
+typedef boost::shared_ptr<DnsCache> DnsCacheItem;
+
+#endif
+
+// (created using vim -- the world's best text editor)
+
index 625a9d3..17b3dde 100644 (file)
 #include <boost/date_time/posix_time/posix_time.hpp>
 #include <boost/asio/placeholders.hpp>
 
+#include "dns_neww/ippseudoresolver.h"
+#include "dns_neww/dnsresolver.h"
+
 using boost::bind;
 using boost::posix_time::seconds;
+using boost::posix_time::minutes;
 using I2n::Logger::GlobalLogger;
 
+
 DnsMasterItem DnsMaster::TheOnlyInstance;
 
-void DnsMaster::create_master(const IoServiceImte &io_serv,
-                              const boost::ip::address &name_server,
+void DnsMaster::create_master(const IoServiceItem &io_serv,
+                              const boost::asio::ip::address &name_server,
                               const std::string &cache_file)
 {
     if (TheOnlyInstance)
     {
         GlobalLogger.warning()
-            << "Attempting to create another DnsMaster instance!";
-        return false;
+            << "Blocking attempt to create another DnsMaster instance!";
+        return;
     }
 
-    TheOnlyInstance.reset( new DnsMaster(io_serv, name_server, cache_file) );
-    return true;
+    GlobalLogger.info() << "Creating DNS Master";
+    DnsCacheItem cache( new DnsCache(io_serv, cache_file) );
+    TheOnlyInstance.reset( new DnsMaster(io_serv, name_server, cache) );
 }
 
 
 DnsMaster::DnsMaster(const IoServiceItem &io_serv,
-                     const boost::ip::address &name_server,
-                     const std::string &cache_file)
+                     const boost::asio::ip::address &name_server,
+                     const DnsCacheItem &cache)
     : IoService( io_serv )
-    , NameServer( name_server )
-    , SaveTimer( *io_serv )
-    , CacheFile( cache_file )
     , ResolverMap()
-    , DataCache()
+    , NameServer( name_server )
+    , Cache(cache)
 {
-    // load cache from file
-    load_from_cachefile();
-
-    // schedule next save
-    (void) SaveTimer.expires_from_now( seconds( SaveTimerSeconds ) );
-    SaveTimer.async_wait( bind( &DnsMaster::schedule_save, this,
-                                boost::asio::placeholders::error ) );
 }
 
 
@@ -79,56 +76,7 @@ DnsMasterItem& DnsMaster::get_instance()
 
 
 
-DnsMaster::~DnsMaster()
-{
-    // save one last time without re-scheduling the next save
-    save_to_cachefile();
-
-    // cancel save timer
-    SaveTimer.cancel();
-}
-
-void DnsMaster::schedule_save(const boost::system::error_code &error)
-{
-    // just in case: ensure SaveTimer is cancelled
-    SaveTimer.cancel();  // (will do nothing if already expired/cancelled)
-
-    if ( error ==  boost::asio::error::operation_aborted )   // cancelled
-    {
-        GlobalLogger.error() << "SaveTimer was cancelled "
-                             << "--> no save and no re-schedule of saving!";
-        return;
-    }
-    else if (error)
-    {
-        GlobalLogger.error() << "Received error " << error
-                             << " in schedule_save "
-                             << "--> no save now but re-schedule saving";
-    }
-    else
-        save_to_cachefile();
-
-    // schedule next save
-    (void) SaveTimer.expires_from_now( seconds( SaveTimerSeconds ) );
-    SaveTimer.async_wait( bind( &DnsMaster::schedule_save, this,
-                                boost::asio::placeholders::error ) );
-}
-
-void DnsMaster::save_to_cachefile() const
-{   // now do the saving
-    // TODO (see trusted_net_helper, boost serialization, xml)
-    GlobalLogger.error() << "Actual saving not implemented yet!";
-}
-
-
-bool DnsMaster::load_from_cachefile()
-{
-    // TODO: some boost serialization and xml stuff, see trusted_net_helper
-    GlobalLogger.error() << "Actual loading not implemented yet!";
-    return true;
-}
-
-ResolverItem DnsMaster::get_resolver_for( const std::string &hostname )
+ResolverItem& DnsMaster::get_resolver_for( const std::string &hostname )
 {
     DnsMasterItem master = get_instance();
     if ( master->ResolverMap.count(hostname) == 0 )
@@ -137,14 +85,16 @@ ResolverItem DnsMaster::get_resolver_for( const std::string &hostname )
         // check if it is an ip address, so can create a simple pseudo resolver
         if ( master->is_ip(hostname) )
         {
-            ResolverItem new_resolver( new IpPseudoResolver(hostname) );
+            GlobalLogger.info() << "Creating PseudoResolver for IP "
+                                << hostname;
+            ResolverItem new_resolver( new IpPseudoResolver(hostname, Cache) );
             master->ResolverMap[hostname] = new_resolver;
         }
         else
         {
-            ResolverItem new_resolver( new DnsResolver(IoService,
-                                                       NameServer,
-                                                       hostname) );
+            GlobalLogger.info() << "Creating Resolver for host " << hostname;
+            ResolverItem new_resolver( new DnsResolver(hostname,  Cache,
+                                                       IoService, NameServer) );
             master->ResolverMap[hostname] = new_resolver;
         }
     }
@@ -154,13 +104,14 @@ ResolverItem DnsMaster::get_resolver_for( const std::string &hostname )
 /**
  * return true if given hostname string actually is an IP
  *
- * delegates decision to boost::ip::address::from_string
+ * delegates decision to boost::asio::ip::address::from_string
  */
 bool DnsMaster::is_ip(const std::string &hostname) const
 {
     try
     {
-        boost::ip::address ip = boost::ip::address::from_string(hostname);
+        boost::asio::ip::address ip = boost::asio::ip::address::from_string(
+                                                                      hostname);
         return ip.is_v4() || ip.is_v6();
     }
     catch ( const std::exception &ex )
@@ -169,5 +120,17 @@ bool DnsMaster::is_ip(const std::string &hostname) const
     }
 }
 
+void DnsMaster::unregister_resolver(const std::string &hostname)
+{
+    int n_erased_reslv = ResolverMap.erase(hostname);
+    if (n_erased_reslv == 1)
+        GlobalLogger.info() << "Unregistered resolver for " << hostname
+                            << " from DNS master";
+    else
+        GlobalLogger.warning() << "Unregistered " << n_erased_reslv
+                               << "(!) resolvers for " << hostname
+                               << " from DNS master!";
+}
+
 // (created using vim -- the world's best text editor)
 
index bad242b..28ad1f4 100644 (file)
  Christian Herdtweck, Intra2net AG 2015
  */
 
+/**
+ * Two in one: a DNS resolver factory and a DNS cache
+ *
+ * Put these two things into one class because it is easier this way to avoid
+ *   sync problems if e.g. there are 2 resolvers for the same host name
+ *
+ * This class is a Singleton. In case there are problems with this approach,
+ * there is an alternative:
+ * give every ResolverBase object a DnsMasterItem as variable, that is set
+ *   during construction and call update / get_cached_results on that instance
+ */
+
 #ifndef DNS_MASTER_H
 #define DNS_MASTER_H
 
 #include <map>
-#include <pair>
-#include <list>
 
-#include <boost/smart_ptr.hpp>
+#include <boost/shared_ptr.hpp>
 #include <boost/noncopyable.hpp>
-#include <boost/date_time/posix_time/posix_time_types.hpp>
-#include <boost/asio/deadline_timer.hpp>
-#include <boost/system/error_code.hpp>
 
-#include "dns_neww/dnsresolver.h"
+#include "host/pinger.h"    // for IoserviceItem
+#include "dns_neww/dnscache.h"
+#include "dns_neww/resolverbase.h"
 
 class DnsMaster;
-typedef boost::smart_ptr<DnsMaster> DnsMasterItem;
 
-typedef std::map<std::string, ResolverItem> resolver_map_type
+typedef boost::shared_ptr<DnsMaster> DnsMasterItem;
 
-typedef boost::posix_time::ptime timestamp_type;
-typedef std::pair<HostAddress, timestamp_type> host_with_timestamp;
-typedef std::vector<host_with_timestamp> host_ttl_time_list;
-typedef std::map<std::string, host_ttl_time_list> cache_data_type;
+typedef std::map<std::string, ResolverItem> resolver_map_type;
 
 
 class DnsMaster : boost::noncopyable
 {
+public:
 // the two functions called during init
 public:
     static void create_master(const IoServiceItem &io_serv,
-                              const boost::ip::address &name_server,
+                              const boost::asio::ip::address &name_server,
                               const std::string &cache_file);
-    static ResolverItem get_resolver_for(const std::string &hostname);
-
-
-// friendly functions in DnsResolver and their friends here
-public:
-    friend void DnsResolver::update_master(const AddressList &ips);
-    friend std::string DnsResolver::get_next_ip();
-    friend void cname_resolve_callback(const boost::system::error_code &error,
-                                       const bool was_success,
-                                       const int cname_count)
-private:
-    void update_ips(const AddressList &ips);
-    host_ttl_time_list get_cached_results(std::string &hostname);
-
+    ResolverItem& get_resolver_for(const std::string &hostname);   // factory!
+    void unregister_resolver(const std::string &hostname);
 
 // implementation of singleton
 private:
     static DnsMasterItem TheOnlyInstance;
 
     DnsMaster(const IoServiceItem &io_serv,
-              const boost::ip::address &name_server,
+              const boost::asio::ip::address &name_server,
               const std::string &cache_file);
 public:
+    static DnsMasterItem& get_instance();
     ~DnsMaster();
 
-
 // variables
 private:
     IoServiceItem IoService;
-    const boost::ip::address NameServer;
-    std::string CacheFile;
-    cache_data_type DataCache;
+    DnsCacheItem Cache;
     resolver_map_type ResolverMap;
-    boost::asio::deadline_timer SaveTimer;
+    const boost::asio::ip::address NameServer;
 
 
 // functions
 private:
-    void schedule_save(const boost::system::error_code &error);
-    void save_to_cachefile() const;
-    void load_from_cachefile();
-
+    bool is_ip(const std::string &hostname) const;
 };
 
 #endif
index c85eed7..bbdc066 100644 (file)
@@ -27,15 +27,20 @@ using I2n::Logger::GlobalLogger;
 
 namespace Config
 {
-    const int ResolveTimeoutSeconds = 10;
+    const int ResolveTimeoutSeconds = 5;
+    const int PauseBeforeRetrySeconds = 10;
+    const int StaleDataLongtermMinutes = 15;
     const int DNS_PORT = 53;
     const int UniqueID = 0xaffe;
+    const int MaxRetryCount = 5;
 }
 
-DnsResolver::DnsResolver(IoServiceItem &io_serv,
-                         const boost::ip::address &name_server,
-                         const std::string &hostname)
-    : Hostname( hostname )
+DnsResolver::DnsResolver(const std::string &hostname,
+                         const DnsCacheItem cache,
+                         IoServiceItem &io_serv,
+                         const boost::asio::ip::address &name_server)
+    : ResolverBase( hostname, cache )
+    , IoService( io_serv )
     , Socket( *io_serv, ip::udp::endpoint(ip::udp::v4(), 0) )
     , ReplyBuffer()
     , NameServer( name_server, DNS_PORT )
@@ -43,30 +48,37 @@ DnsResolver::DnsResolver(IoServiceItem &io_serv,
     , PauseBeforeRetryTimer( *io_serv )
     , StaleDataLongtermTimer( *io_serv )
     , CallbackList()
-    , host_ttl_time_list::const_iterator()
+    , HostAddressList::const_iterator()
     , RetryCount( 0 )
     , IsResolving( false )
-{
-    // add name server to resolver
-    resolver.addServer( nameServer );
-}
+{ }
+
+
+//==============================================================================
+// ASYNC RESOLVE
+//==============================================================================
 
 /**
  * copied here code from boost::net::dns::resolve.hpp, since want async 
  * operation and that is used only internally, there
  * --> give credit to Andreas Haberstroh (andreas at ibusy dot com)
  *     from https://github.com/softwareace/Boost.DNS
+ *     
+ * callbacks should be of type
+ * void resolve_callback(const boost::system::error_code &error,
+ *                       const bool was_success,
+ *                       const int cname_count)
  */
-DnsResolver::async_resolve(const callback_type callback, const int cname_count)
+DnsResolver::async_resolve(const callback_type callback)
 {
     // remember callback
-    CallbackList.push_back(callback, cname_count);
+    CallbackList.push(callback);
 
     // check if resolving already
     if (isResolving)
     {
-        GlobalLogger().info() << "Call to async_resolve(" << Hostname
-                              << ") ignored since resolving already";
+        GlobalLogger.info()
+            << "Call to async_resolve ignored since resolving already";
         return;
     }
 
@@ -96,7 +108,7 @@ DnsResolver::async_resolve(const callback_type callback, const int cname_count)
 
     // schedule timeout
     (void) ResolveTimeoutTimer.expires_from_now(seconds(ResolveTimeoutSeconds));
-    ResolveTimeout.async_wait( bind( &DnsResolver::wait_timer_timeout_handler,
+    ResolveTimeout.async_wait( bind( &DnsResolver::handle_resolve_timeout,
                                      this, boost::asio::placeholders::error) );
 
     // send dns request
@@ -110,14 +122,13 @@ void handle_dns_result(const boost::system::error_code &error,
 {
     if ( error ==  boost::asio::error::operation_aborted )   // cancelled
     {
-        GlobalLogger.info() << "DNS resolve operation was cancelled";
+        resolve_log.info() << "DNS resolve operation was cancelled";
         bool was_success = false;
-        bool inc_cname = false;
-        finalize_resolve(was_success, inc_cname);
+        finalize_resolve(was_success);
     }
     else if (error)
     {
-        GlobalLogger.info() << "DNS resolve resulted in error " << error
+        resolve_log.info() << "DNS resolve resulted in error " << error
                             << " --> treat like unavailable";
         handle_unavailable();
         return;
@@ -135,7 +146,7 @@ void handle_dns_result(const boost::system::error_code &error,
     // loop over answers, remembering ips and cnames
     typedef boost::shared_ptr<resource_base_t> rr_item_type;
     typedef boost::shared_ptr<cname_resource> cname_item_type;
-    std::vector<HostAddress> ip_list;
+    HostAddressList ip_list;
     std::vector<cname_item_type> cname_list;
     BOOST_FOREACH( rr_item_type rr_item, result_message.answers() )
     {
@@ -198,26 +209,21 @@ void handle_dns_result(const boost::system::error_code &error,
 }
 
 
-void DnsResolver::handle_ips(const boost::system::error_code &error,
-                             const IpTtlVec &ips)
+void DnsResolver::handle_ips(const HostAddressList &ips)
 {
-    if (error)
-        GlobalLogger.warning() << "Received error " << error
-                               << " in handle_ips --> exit!";
     // save in cache
-    update_master(ips);
+    ResolverBase::update_cache( ips );
 
     // clean up
     bool was_success = true;
-    bool inc_cname = false;
-    finalize_resolve(was_success, inc_cname);
+    finalize_resolve(was_success);
 }
 
 
 void DnsResolver::handle_unavailable()
 {
     // schedule new attempt in quite a while
-    StaleDataLongtermTimer.expires_from_now(seconds(StaleDataLongtermSeconds));
+    StaleDataLongtermTimer.expires_from_now(minutes(StaleDataLongtermMinutes));
     StaleDataLongtermTimer.async_wait(
             bind( &DnsResolver::wait_timer_timeout_handler,
                   this, boost::asio::placeholders::error
@@ -226,18 +232,18 @@ void DnsResolver::handle_unavailable()
 
     // for now, admit failure
     bool was_success = false;
-    bool inc_cname = false;
-    finalize_resolve(was_success, inc_cname);
+    finalize_resolve(was_success);
 }
 
 void DnsResolver::handle_cname(const std::string &canonical_name)
 {
     // get resolver for canonical name
-    ResolverItem resolver = DnsMaster.get_instance()
+    ResolverItem resolver = DnsMaster::get_instance()
                             ->get_resolver_for(canonical_name);
     resolver->async_resolve(
             bind( &DnsResolver::cname_resolve_callback,
-                  this, boost::asio::placeholders::error, _1, _2
+                  this, boost::asio::placeholders::error,
+                  canonical_name, _1, _2
             )
     );
 
@@ -246,26 +252,144 @@ void DnsResolver::handle_cname(const std::string &canonical_name)
 
 
 void cname_resolve_callback(const boost::system::error_code &error,
+                            const std::string &canonical_name,
                             const bool was_success,
                             const int cname_count)
 {
-    if (error)
-        GlobalLogger.error() << "todo"; // TODO
-    if (!was_success)
-        GlobalLogger.error() << "todo"; // TODO
+    bool was_success = true;
+
+    if ( error ==  boost::asio::error::operation_aborted )   // cancelled
+    {
+        GlobalLogger.warning()
+            << "Recursive resolution of cname was cancelled!";
+        was_success = false;
+    }
+    else if (error)
+    {
+        GlobalLogger.warning() << "Error " << error
+            << " waiting for callback from cname resolution!"
+        was_success = false;
+    }
+    if (was_success)
+    {   // tell cache to return cname's ips if queried for our hostname
+        ResolverBase::update_cache(
+                             ResolverBase::get_cached_results(canonical_name) );
+    }
+    else
+    {
+        GlobalLogger.info() << "Cname resolution failed";
+        was_success = false;
+    }
+
+    finalize_resolve(was_success, cname_count+1);
+}
+
+
+void DnsResolver::finalize_resolve(const bool was_success,
+                                   const int cname_count)
+{
+    // stop timers
+    if (cname_count > 0)
+        stop_trying();
+    // else was called already from handle_cname
+
+    // schedule callbacks, clearing callback list
+    while ( !CallbackList.empty )
+    {
+        IoService.post( bind( CallbackList.front(),
+                              boost::asio::placeholders::error,
+                              was_success, cname_count ) );
+        CallbackList.pop();
+    }
+
+    // finalize
+    GlobalLogger.notice() << "Done resolving"
+                          << " with success = " << was_success
+                          << " and cname_count = " << cname_count;
+    IsResolving = false;
+}
+
+void DnsResolver::stop_trying()
+{
+    // cancel timers
+    GlobalLogger.debug() << "Cancelling timers";
+    ResolveTimeoutTimer.cancel();
+    PauseBeforeRetryTimer.cancel();
+    StaleDataLongtermTimer.cancel();
+
+    // clean up
+    RetryCount = 0;
+}
+
+viod DnsResolver::handle_resolve_timeout(const boost::system::error_code &error)
+{
+    if ( error ==  boost::asio::error::operation_aborted )   // cancelled
+    {
+        GlobalLogger.warning() << "Resolve timeout timer was cancelled!";
+        return;
+    }
+    else if (error)
+    {
+        GlobalLogger.warning() << "resolve timeout handler received error "
+                               << error;
+        return;
+    }
+
+    GlobalLogger.notice() << "DNS resolving timed out";
 
-    // get all results
-    host_ttl_time_list results = DnsMaster.get_instance()->get_cached_results();
+    // increment timer
+    ++RetryCount;
+
+    if (RetryCount > MaxRetryCount)
+    {
+        handle_unavailable();
+        RetryCount = 0;
+    }
+    else
+    {   // schedule retry
+        PauseBeforeRetryTimer.expires_from_now(
+                seconds(PauseBeforeRetrySeconds));
+        PauseBeforeRetryTimer.async_wait(
+                bind( &DnsResolver::wait_timer_timeout_handler,
+                      this, boost::asio::placeholders::error) );
+    }
+}
+
+void DnsResolver::wait_timer_timeout_handler(
+                                         const boost::system::error_code &error)
+{
+    if ( error ==  boost::asio::error::operation_aborted )   // cancelled
+        GlobalLogger.warning() << "Resolve timeout timer was cancelled!";
+    else if (error)
+        GlobalLogger.warning() << "resolve timeout handler received error "
+                               << error;
+    else
+    {
+        GlobalLogger.info() << "Done waiting --> re-try resolve";
+        async_resolve();
+    }
 }
 
 
-DnsResolver::get_next_ip()
+//==============================================================================
+// RETRIEVAL
+//==============================================================================
+
+HostAddress& DnsResolver::get_next_ip()
 {
     // get cached data
-    host_ttl_time_list cached_data = DnsMaster::get_instance()
-                                     ->get_cached_results(Hostname);
+    ResolverBase::get_cached_results();
+
+    // if no results cached, return default-constructed HostAddress (0.0.0.0)
+    if ( cached_data.empty() )
+        return HostAddress;
 
+    // check validity of index (cache may have changed since last call)
+    if (NextIpIndex >= cached_data.size())
+        NextIpIndex = 0;
 
+    // return next IP
+    return cached_data[NextIpIndex++];
 }
 
 // (created using vim -- the world's best text editor)
index 5c481b9..6141d92 100644 (file)
 
 #include "dns_neww/dnsmaster.h"
 
-#include <list>
+#include <queue>
 #include <boost/asio/deadline_timer.hpp>
+#include <boost/asio/ip/udp.hpp>
+#include <boost/asio/ip/address.hpp>
 #include <boost/system/error_code.hpp>
+#include <boost/net/network_array.hpp>
 
+#include "host/pinger.h"    // for IoServiceItem
 #include "dns_neww/resolverbase.h"
 #include "dns_neww/dnsmaster.h"
+#include "dns_neww/dnscache.h"
 
-typedef callback_type
 typedef std::list<callback_type> callback_list_type;
 
-typedef std::vector<HostAddress> IpTtlVec;
+typedef std::queue<HostAddress> IpTtlVec;
 
 class DnsResolver : public ResolverBase
 {
-// constructor accessible from friend DnsMaster
 public:
-    friend DnsMaster::get_resolver_for(const std::string &hostname);
+    ~DnsResolver();
 
+// constructor accessible from friend DnsMaster
+public:
+    friend DnsResolverItem DnsMaster::get_resolver_for(const std::string&);
 private:
-    DnsResolver(IoServiceItem &io_serv,
-                const boost::ip::address &name_server,
-                const std::string &hostname);
+    DnsResolver(const std::string &hostname,
+                const DnsCacheItem cache,
+                IoServiceItem &io_serv,
+                const boost::asio::ip::address &name_server);
 
-// only real public function (called from pingers)
+// only real public functions (called from pingers)
 public:
-    std::string get_next_ip();
+    void async_resolve(const callback_type &callback);
+    HostAddress& get_next_ip();
 
 private:
-    void async_resolve(const callback_type &callback, const int cname_count=0);
     void handle_resolve_timeout(const boost::system::error_code &error);
     void handle_dns_result(const boost::system::error_code &error,
                            const std::size_t bytes_transferred);
@@ -64,23 +71,24 @@ private:
     void cname_resolve_callback(const boost::system::error_code &error,
                                 const bool was_success,
                                 const int cname_count);
-    void update_master(const AddressList &ips);
-    void finalize_resolve(const bool success, const bool inc_cname);
+    void finalize_resolve(const bool success, const int cname_count=0);
+    void stop_trying();
+    void wait_timer_timeout_handler(const boost::system::error_code &error);
 
 private:
-    const std::string Hostname;
-    ip::udp::socket Socket;
-    dns_buffer_t ReplyBuffer;
-    ip::udp::endpoint NameServer;
+    IoServiceItem IoService;
+    boost::asio::ip::udp::socket Socket;
+    boost::net::dns_buffer_t ReplyBuffer;
+    boost::asio::ip::udp::endpoint NameServer;
     boost::asio::deadline_timer ResolveTimeoutTimer;
     boost::asio::deadline_timer PauseBeforeRetryTimer;
     boost::asio::deadline_timer StaleDataLongtermTimer;
     callback_list_type CallbackList;
-    host_ttl_time_list::const_iterator NextIpIter;
+    int NextIpIndex;
     int RetryCount;
     bool IsResolving;
+};
 
-}
 #endif
 // (created using vim -- the world's best text editor)
 
index 015a312..72edf8f 100644 (file)
 #include "dns_neww/resolverbase.h"
 #include "dns_neww/dnsmaster.h"
 
+namespace Config
+{
+    uint32_t DefaultTtl = 60*60*24*356;  // 1 year in seconds (approx)
+}
+
 /** @brief Degenerate case of a resolver: hostname is already an IP
  * 
  * created by DnsMaster if given an IP address as hostname
@@ -39,16 +44,25 @@ class IpPseudoResolver : public ResolverBase
 {
 // constructor accessible from friend DnsMaster
 public:
-    friend DnsMaster::create_resolver_for(const std::string &hostname);
+    friend ResolverItem& DnsMaster::get_resolver_for(
+                                                   const std::string &hostname);
 private:
-    IpPseudoResolver(const std::string ip) : IP(ip)  {}
+    IpPseudoResolver(const std::string &ip,
+                     const DnsCacheItem &cache )
+        : ResolverBase( ip, cache )
+        , Ip( ip, Config::DefaultTtl )
+    {}
 
 private:
-    const std::string IP;
+    HostAddress Ip;
 
-// only function, inherited from ResolverBase
+// only functions, inherited from ResolverBase
 public:
-    std::string get_next_ip()  { return IP;  }
+    HostAddress& get_next_ip()  { return Ip;  }
+    void async_resolve(const callback_type &callback)
+    {
+        IoService.post( bind(callback, true, 0) );
+    }
 };
 #endif
 
index 0003520..8daab9f 100644 (file)
 
 #include <boost/shared_ptr.hpp>
 
+#include "dns/hostaddress.h"
+#include "dns_neww/dnscache.h"
+#include "dns_neww/dnsmaster.h"
+
+
+class ResolverBase;
+typedef boost::shared_ptr<ResolverBase> ResolverItem;
+
+typedef void (*callback_type)(const bool, const int);
+
 /**
  * @brief: abstract base class for DnsResolver and IpPseudoResolver
  */
 class ResolverBase
 {
 public:
-    virtual std::string get_next_ip() = 0;
-};
+    virtual HostAddress& get_next_ip() = 0;
+    virtual void async_resolve(const callback_type &callback) = 0;
+    virtual ~ResolverBase()
+    {
+        DnsMaster::get_instance()->unregister(Hostname);
+    }
 
-typedef boost::shared_ptr<ResolverBase> ResolverItem;
+protected:
+    ResolverBase(const std::string &hostname,
+                 const DnsCacheItem &cache )
+        : Hostname( hostname )
+        , Cache( cache )
+    {}
+
+// variables
+private:
+    std::string Hostname;
+    DnsCacheItem Cache;
+
+// functions for subclasses
+protected:
+    void update_cache( const HostAddressList &new_results ) const
+    {   Cache->update( Hostname, new_results );  }
+
+    HostAddressList get_cached_results(const std::string host="") const
+    {
+        if (host.empty())
+            return Cache->get_data( Hostname );
+        else
+            return Cache->get_data( host );
+    }
+};
 
 #endif
 
index 85e61ea..d3463c4 100644 (file)
@@ -30,7 +30,6 @@ typedef boost::shared_ptr<IcmpPinger> IcmpPingerItem;
 class IcmpPacketDistributor;
 typedef boost::shared_ptr<IcmpPacketDistributor> IcmpPacketDistributorItem;
 
-typedef boost::shared_ptr<boost::asio::io_service> IoServiceItem;
 typedef boost::shared_ptr<icmp::socket> SocketItem;
 
 //-----------------------------------------------------------------------------