From: Christian Herdtweck Date: Wed, 8 Apr 2015 16:54:25 +0000 (+0200) Subject: continuing with dns rewrite; today realized that cache is better as own class and... X-Git-Url: http://developer.intra2net.com/git/?a=commitdiff_plain;h=96779587cbf686466ef841d4ebec8c9d9a9cb82e;p=pingcheck continuing with dns rewrite; today realized that cache is better as own class and ResolverBase gets more powerfull --- diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2003089..e956995 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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 index 0000000..9e1b004 --- /dev/null +++ b/src/dns_neww/dnscache.cpp @@ -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 +#include +#include +#include + +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 index 0000000..4e5d889 --- /dev/null +++ b/src/dns_neww/dnscache.h @@ -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 + +#include +#include +#include + +#include "host/pinger.h" // for IoserviceItem +#include "dns/hostaddress.h" + +typedef std::map 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 DnsCacheItem; + +#endif + +// (created using vim -- the world's best text editor) + diff --git a/src/dns_neww/dnsmaster.cpp b/src/dns_neww/dnsmaster.cpp index 625a9d3..17b3dde 100644 --- a/src/dns_neww/dnsmaster.cpp +++ b/src/dns_neww/dnsmaster.cpp @@ -27,45 +27,42 @@ #include #include +#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) diff --git a/src/dns_neww/dnsmaster.h b/src/dns_neww/dnsmaster.h index bad242b..28ad1f4 100644 --- a/src/dns_neww/dnsmaster.h +++ b/src/dns_neww/dnsmaster.h @@ -20,81 +20,70 @@ 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 -#include -#include -#include +#include #include -#include -#include -#include -#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 DnsMasterItem; -typedef std::map resolver_map_type +typedef boost::shared_ptr DnsMasterItem; -typedef boost::posix_time::ptime timestamp_type; -typedef std::pair host_with_timestamp; -typedef std::vector host_ttl_time_list; -typedef std::map cache_data_type; +typedef std::map 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 diff --git a/src/dns_neww/dnsresolver.cpp b/src/dns_neww/dnsresolver.cpp index c85eed7..bbdc066 100644 --- a/src/dns_neww/dnsresolver.cpp +++ b/src/dns_neww/dnsresolver.cpp @@ -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 rr_item_type; typedef boost::shared_ptr cname_item_type; - std::vector ip_list; + HostAddressList ip_list; std::vector 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) diff --git a/src/dns_neww/dnsresolver.h b/src/dns_neww/dnsresolver.h index 5c481b9..6141d92 100644 --- a/src/dns_neww/dnsresolver.h +++ b/src/dns_neww/dnsresolver.h @@ -25,35 +25,42 @@ #include "dns_neww/dnsmaster.h" -#include +#include #include +#include +#include #include +#include +#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_list_type; -typedef std::vector IpTtlVec; +typedef std::queue 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) diff --git a/src/dns_neww/ippseudoresolver.h b/src/dns_neww/ippseudoresolver.h index 015a312..72edf8f 100644 --- a/src/dns_neww/ippseudoresolver.h +++ b/src/dns_neww/ippseudoresolver.h @@ -26,6 +26,11 @@ #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 diff --git a/src/dns_neww/resolverbase.h b/src/dns_neww/resolverbase.h index 0003520..8daab9f 100644 --- a/src/dns_neww/resolverbase.h +++ b/src/dns_neww/resolverbase.h @@ -25,16 +25,54 @@ #include +#include "dns/hostaddress.h" +#include "dns_neww/dnscache.h" +#include "dns_neww/dnsmaster.h" + + +class ResolverBase; +typedef boost::shared_ptr 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 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 diff --git a/src/icmp/icmppinger.h b/src/icmp/icmppinger.h index 85e61ea..d3463c4 100644 --- a/src/icmp/icmppinger.h +++ b/src/icmp/icmppinger.h @@ -30,7 +30,6 @@ typedef boost::shared_ptr IcmpPingerItem; class IcmpPacketDistributor; typedef boost::shared_ptr IcmpPacketDistributorItem; -typedef boost::shared_ptr IoServiceItem; typedef boost::shared_ptr SocketItem; //-----------------------------------------------------------------------------