--- /dev/null
+/*
+ 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 <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;
+
+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)
+
--- /dev/null
+/*
+ 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 <map>
+#include <pair>
+#include <list>
+
+#include <boost/smart_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"
+
+class DnsMaster;
+typedef boost::smart_ptr<DnsMaster> DnsMasterItem;
+
+typedef std::map<std::string, ResolverItem> resolver_map_type
+
+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;
+
+
+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)
+
--- /dev/null
+/*
+ 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 <logfunc.hpp>
+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<resource_base_t> rr_item_type;
+ typedef boost::shared_ptr<cname_resource> cname_item_type;
+ std::vector<HostAddress> ip_list;
+ std::vector<cname_item_type> 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<a_resource *> (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<a6_resource *> (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<cname_item_type>(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)
+
--- /dev/null
+/*
+ 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 <list>
+#include <boost/asio/deadline_timer.hpp>
+#include <boost/system/error_code.hpp>
+
+#include "dns_neww/resolverbase.h"
+#include "dns_neww/dnsmaster.h"
+
+typedef callback_type
+typedef std::list<callback_type> callback_list_type;
+
+typedef std::vector<HostAddress> 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)
+
--- /dev/null
+/*
+ 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)
+
--- /dev/null
+/*
+ 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 <boost/shared_ptr.hpp>
+
+/**
+ * @brief: abstract base class for DnsResolver and IpPseudoResolver
+ */
+class ResolverBase
+{
+public:
+ virtual std::string get_next_ip() = 0;
+};
+
+typedef boost::shared_ptr<ResolverBase> ResolverItem;
+
+#endif
+
+// (created using vim -- the world's best text editor)
+