From 36ad976b04836437fd7307d4dd31d68e7bd3e4cd Mon Sep 17 00:00:00 2001 From: Christian Herdtweck Date: Wed, 1 Apr 2015 18:33:36 +0200 Subject: [PATCH] added first half-ready versions of new DNS files in temp directory dns_neww to avoid overwriting of old reference code --- src/dns_neww/dnsmaster.cpp | 173 +++++++++++++++++++++++++ src/dns_neww/dnsmaster.h | 103 +++++++++++++++ src/dns_neww/dnsresolver.cpp | 272 +++++++++++++++++++++++++++++++++++++++ src/dns_neww/dnsresolver.h | 86 ++++++++++++ src/dns_neww/ippseudoresolver.h | 56 ++++++++ src/dns_neww/resolverbase.h | 42 ++++++ 6 files changed, 732 insertions(+), 0 deletions(-) create mode 100644 src/dns_neww/dnsmaster.cpp create mode 100644 src/dns_neww/dnsmaster.h create mode 100644 src/dns_neww/dnsresolver.cpp create mode 100644 src/dns_neww/dnsresolver.h create mode 100644 src/dns_neww/ippseudoresolver.h create mode 100644 src/dns_neww/resolverbase.h diff --git a/src/dns_neww/dnsmaster.cpp b/src/dns_neww/dnsmaster.cpp new file mode 100644 index 0000000..625a9d3 --- /dev/null +++ b/src/dns_neww/dnsmaster.cpp @@ -0,0 +1,173 @@ +/* + 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/dnsmaster.h" + +#include +#include +#include +#include + +using boost::bind; +using boost::posix_time::seconds; +using I2n::Logger::GlobalLogger; + +DnsMasterItem DnsMaster::TheOnlyInstance; + +void DnsMaster::create_master(const IoServiceImte &io_serv, + const boost::ip::address &name_server, + const std::string &cache_file) +{ + if (TheOnlyInstance) + { + GlobalLogger.warning() + << "Attempting to create another DnsMaster instance!"; + return false; + } + + TheOnlyInstance.reset( new DnsMaster(io_serv, name_server, cache_file) ); + return true; +} + + +DnsMaster::DnsMaster(const IoServiceItem &io_serv, + const boost::ip::address &name_server, + const std::string &cache_file) + : IoService( io_serv ) + , NameServer( name_server ) + , SaveTimer( *io_serv ) + , CacheFile( cache_file ) + , ResolverMap() + , DataCache() +{ + // 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 ) ); +} + + +DnsMasterItem& DnsMaster::get_instance() +{ + if ( !TheOnlyInstance ) + GlobalLogger.error() + << "Request to return DnsMaster instance before creating it!"; + return TheOnlyInstance; +} + + + +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 ) +{ + DnsMasterItem master = get_instance(); + if ( master->ResolverMap.count(hostname) == 0 ) + { // need to create a resolver + + // check if it is an ip address, so can create a simple pseudo resolver + if ( master->is_ip(hostname) ) + { + ResolverItem new_resolver( new IpPseudoResolver(hostname) ); + master->ResolverMap[hostname] = new_resolver; + } + else + { + ResolverItem new_resolver( new DnsResolver(IoService, + NameServer, + hostname) ); + master->ResolverMap[hostname] = new_resolver; + } + } + return master->ResolverMap[hostname]; +} + +/** + * return true if given hostname string actually is an IP + * + * delegates decision to boost::ip::address::from_string + */ +bool DnsMaster::is_ip(const std::string &hostname) const +{ + try + { + boost::ip::address ip = boost::ip::address::from_string(hostname); + return ip.is_v4() || ip.is_v6(); + } + catch ( const std::exception &ex ) + { + return false; + } +} + +// (created using vim -- the world's best text editor) + diff --git a/src/dns_neww/dnsmaster.h b/src/dns_neww/dnsmaster.h new file mode 100644 index 0000000..bad242b --- /dev/null +++ b/src/dns_neww/dnsmaster.h @@ -0,0 +1,103 @@ +/* + 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_MASTER_H +#define DNS_MASTER_H + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "dns_neww/dnsresolver.h" + +class DnsMaster; +typedef boost::smart_ptr DnsMasterItem; + +typedef std::map resolver_map_type + +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; + + +class DnsMaster : boost::noncopyable +{ +// the two functions called during init +public: + static void create_master(const IoServiceItem &io_serv, + const boost::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); + + +// implementation of singleton +private: + static DnsMasterItem TheOnlyInstance; + + DnsMaster(const IoServiceItem &io_serv, + const boost::ip::address &name_server, + const std::string &cache_file); +public: + ~DnsMaster(); + + +// variables +private: + IoServiceItem IoService; + const boost::ip::address NameServer; + std::string CacheFile; + cache_data_type DataCache; + resolver_map_type ResolverMap; + boost::asio::deadline_timer SaveTimer; + + +// functions +private: + void schedule_save(const boost::system::error_code &error); + void save_to_cachefile() const; + void load_from_cachefile(); + +}; + +#endif + +// (created using vim -- the world's best text editor) + diff --git a/src/dns_neww/dnsresolver.cpp b/src/dns_neww/dnsresolver.cpp new file mode 100644 index 0000000..c85eed7 --- /dev/null +++ b/src/dns_neww/dnsresolver.cpp @@ -0,0 +1,272 @@ +/* + 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/dnsresolver.h" + +#include +using I2n::Logger::GlobalLogger; + +namespace Config +{ + const int ResolveTimeoutSeconds = 10; + const int DNS_PORT = 53; + const int UniqueID = 0xaffe; +} + +DnsResolver::DnsResolver(IoServiceItem &io_serv, + const boost::ip::address &name_server, + const std::string &hostname) + : Hostname( hostname ) + , Socket( *io_serv, ip::udp::endpoint(ip::udp::v4(), 0) ) + , ReplyBuffer() + , NameServer( name_server, DNS_PORT ) + , ResolveTimeoutTimer( *io_serv ) + , PauseBeforeRetryTimer( *io_serv ) + , StaleDataLongtermTimer( *io_serv ) + , CallbackList() + , host_ttl_time_list::const_iterator() + , RetryCount( 0 ) + , IsResolving( false ) +{ + // add name server to resolver + resolver.addServer( nameServer ); +} + +/** + * 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 + */ +DnsResolver::async_resolve(const callback_type callback, const int cname_count) +{ + // remember callback + CallbackList.push_back(callback, cname_count); + + // check if resolving already + if (isResolving) + { + GlobalLogger().info() << "Call to async_resolve(" << Hostname + << ") ignored since resolving already"; + return; + } + + // just to be sure: cancel timers + ResolveTimeoutTimer.cancel(); + PauseBeforeRetryTimer.cancel(); + StaleDataLongtermTimer.cancel(); + + // create DNS request + boost::net::dns::message dns_message( host_dns_address, + boost::net::dns::type_all ); + dns_message.recursive(false); + dns_message.action(dns::message::query); + dns_message.opcode(dns::message::squery); + dns_message.id(UniqueID); + dns_buffer_t request_buffer; + dns_message.encode(request_buffer); + + // setup receipt of reply + Socket.async_receive_from( + boost::asio::buffer(ReceiveBuffer.get_array()), + NameServer, + boost::bind( &DnsResolver::handle_dns_result, this, + boost::asio::placeholders::error, + boost::asio::placeholders::bytes_transferred) + ) + + // schedule timeout + (void) ResolveTimeoutTimer.expires_from_now(seconds(ResolveTimeoutSeconds)); + ResolveTimeout.async_wait( bind( &DnsResolver::wait_timer_timeout_handler, + this, boost::asio::placeholders::error) ); + + // send dns request + Socket.send_to( boost::asio::buffer(request_buffer.get_array()), + NameServer ); +} + + +void handle_dns_result(const boost::system::error_code &error, + const std::size_t bytes_transferred) +{ + if ( error == boost::asio::error::operation_aborted ) // cancelled + { + GlobalLogger.info() << "DNS resolve operation was cancelled"; + bool was_success = false; + bool inc_cname = false; + finalize_resolve(was_success, inc_cname); + } + else if (error) + { + GlobalLogger.info() << "DNS resolve resulted in error " << error + << " --> treat like unavailable"; + handle_unavailable(); + return; + } + + // 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); + message result_message(); + result_message.decode( ReceiveBuffer ); + + if (result_message.answers().size == 0) + handle_unavailable(); + + // 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; + std::vector cname_list; + BOOST_FOREACH( rr_item_type rr_item, result_message.answers() ) + { + GlobalLogger.debug() << std::showbase << std::hex << rr_item->rtype() + << ": "; + uint32_t ttl = rr_item->ttl(); + type_t rr_type = rr_item->rtype(); + + if (rr_type == boost::net::dns::type_a) + { // 'A' resource records carry IPv4 addresses + std::string ip = ( dynamic_cast (rr_item.get()) ) + ->address(); + ip_list.push_back( HostAddress(ip, ttl) ); + } + else if (rr_type == boost::net::dns::type_a6) + { // 'AAAA' resource records carry IPv6 addresses + std::string ip = ( dynamic_cast (rr_item.get()) ) + ->address(); + ip_list.push_back( HostAddress(ip, ttl) ); + } + else if (rr_type == boost::net::dns::type_cname) + { // 'CNAME' resource records that carry aliases + cname_list.push_back(dynamic_cast(rr_item)); + } + else if (rr_type == boost::net::dns::type_ns) + GlobalLogger.debug() << "NS resource"; + else if (rr_type == boost::net::dns::type_soa) + GlobalLogger.debug() << "SOA resource"; + else if (rr_type == boost::net::dns::type_ptr) + GlobalLogger.debug() << "ptr resource"; + else if (rr_type == boost::net::dns::type_hinfo) + GlobalLogger.debug() << "hinfo resource"; + else if (rr_type == boost::net::dns::type_mx) + GlobalLogger.debug() << "mx resource"; + else if (rr_type == boost::net::dns::type_txt) + GlobalLogger.debug() << "txt resource"; + else if (rr_type == boost::net::dns::type_srv) + GlobalLogger.debug() << "srv resource"; + else if (rr_type == boost::net::dns::type_axfr) + GlobalLogger.debug() << "axfr resource"; + else + GlobalLogger.debug() << "unknown resource type"; + } + + GlobalLogger.info() << "Have " << ip_list.size() << " IPs and " + << cname_list.size() << " CNAMEs"; + + // We expect either one single CNAME and no IPs or a list of IPs. + // But deal with other cases as well + if (ip_list.empty() && cname_list.empty()) + handle_unavailable(); // we just got crap, this is a dead end + else if ( !ip_list.empty() && !cname_list.empty()) + GlobalLogger.warning() << "Have CNAMEs AND IPs --> deal with both!"; + + BOOST_FOREACH( const cname_resource &cname, cname_list ) + handle_cname(cname); // will schedule another DNS call + + if ( !ip_list.empty() ) + handle_ips(ip_list); +} + + +void DnsResolver::handle_ips(const boost::system::error_code &error, + const IpTtlVec &ips) +{ + if (error) + GlobalLogger.warning() << "Received error " << error + << " in handle_ips --> exit!"; + // save in cache + update_master(ips); + + // clean up + bool was_success = true; + bool inc_cname = false; + finalize_resolve(was_success, inc_cname); +} + + +void DnsResolver::handle_unavailable() +{ + // schedule new attempt in quite a while + StaleDataLongtermTimer.expires_from_now(seconds(StaleDataLongtermSeconds)); + StaleDataLongtermTimer.async_wait( + bind( &DnsResolver::wait_timer_timeout_handler, + this, boost::asio::placeholders::error + ) + ); + + // for now, admit failure + bool was_success = false; + bool inc_cname = false; + finalize_resolve(was_success, inc_cname); +} + +void DnsResolver::handle_cname(const std::string &canonical_name) +{ + // get resolver for canonical name + 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 + ) + ); + + stop_trying(); +} + + +void cname_resolve_callback(const boost::system::error_code &error, + const bool was_success, + const int cname_count) +{ + if (error) + GlobalLogger.error() << "todo"; // TODO + if (!was_success) + GlobalLogger.error() << "todo"; // TODO + + // get all results + host_ttl_time_list results = DnsMaster.get_instance()->get_cached_results(); +} + + +DnsResolver::get_next_ip() +{ + // get cached data + host_ttl_time_list cached_data = DnsMaster::get_instance() + ->get_cached_results(Hostname); + + +} + +// (created using vim -- the world's best text editor) + diff --git a/src/dns_neww/dnsresolver.h b/src/dns_neww/dnsresolver.h new file mode 100644 index 0000000..5c481b9 --- /dev/null +++ b/src/dns_neww/dnsresolver.h @@ -0,0 +1,86 @@ +/* + 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_RESOLVER_H +#define DNS_RESOLVER_H + +#include "dns_neww/dnsmaster.h" + +#include +#include +#include + +#include "dns_neww/resolverbase.h" +#include "dns_neww/dnsmaster.h" + +typedef callback_type +typedef std::list callback_list_type; + +typedef std::vector IpTtlVec; + +class DnsResolver : public ResolverBase +{ +// constructor accessible from friend DnsMaster +public: + friend DnsMaster::get_resolver_for(const std::string &hostname); + +private: + DnsResolver(IoServiceItem &io_serv, + const boost::ip::address &name_server, + const std::string &hostname); + +// only real public function (called from pingers) +public: + std::string 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); + void handle_unavailable(const boost::system::error_code &error); + void handle_ips(const boost::system::error_code &error, + const IpTtlVec &ips); + void handle_cname(const boost::system::error_code &error); + 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); + +private: + const std::string Hostname; + ip::udp::socket Socket; + dns_buffer_t ReplyBuffer; + 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 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 new file mode 100644 index 0000000..015a312 --- /dev/null +++ b/src/dns_neww/ippseudoresolver.h @@ -0,0 +1,56 @@ +/* + 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 IP_PSEUDO_RESOLVER_H +#define IP_PSEUDO_RESOLVER_H + +#include "dns_neww/resolverbase.h" +#include "dns_neww/dnsmaster.h" + +/** @brief Degenerate case of a resolver: hostname is already an IP + * + * created by DnsMaster if given an IP address as hostname + * + * Will do nothing, just remember that IP and return it for every call to + * get_next_ip + * + * Since this is so boring, I did not create an own .cpp for it + */ +class IpPseudoResolver : public ResolverBase +{ +// constructor accessible from friend DnsMaster +public: + friend DnsMaster::create_resolver_for(const std::string &hostname); +private: + IpPseudoResolver(const std::string ip) : IP(ip) {} + +private: + const std::string IP; + +// only function, inherited from ResolverBase +public: + std::string get_next_ip() { return IP; } +}; +#endif + +// (created using vim -- the world's best text editor) + diff --git a/src/dns_neww/resolverbase.h b/src/dns_neww/resolverbase.h new file mode 100644 index 0000000..0003520 --- /dev/null +++ b/src/dns_neww/resolverbase.h @@ -0,0 +1,42 @@ +/* + 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 RESOLVER_BASE_H +#define RESOLVER_BASE_H + +#include + +/** + * @brief: abstract base class for DnsResolver and IpPseudoResolver + */ +class ResolverBase +{ +public: + virtual std::string get_next_ip() = 0; +}; + +typedef boost::shared_ptr ResolverItem; + +#endif + +// (created using vim -- the world's best text editor) + -- 1.7.1