on this file might be covered by the GNU General Public License.
Christian Herdtweck, Intra2net AG 2015
+
+ with code copied from boost::net::dns::resolve.hpp
+ by Andreas Haberstroh (andreas at ibusy dot com)
+ from https://github.com/softwareace/Boost.DNS
*/
#include "dns_neww/dnsresolver.h"
+#include <boost/foreach.hpp>
+#include <boost/bind.hpp>
+#include <boost/function.hpp>
+#include <boost/net/dns.hpp>
+#include <boost/date_time/posix_time/posix_time.hpp>
+
#include <logfunc.hpp>
+
using I2n::Logger::GlobalLogger;
+using boost::posix_time::seconds;
+using boost::posix_time::minutes;
namespace Config
{
const int MaxRetryCount = 5;
}
-DnsResolver::DnsResolver(const std::string &hostname,
+DnsResolver::DnsResolver(IoServiceItem &io_serv,
+ const std::string &hostname,
const DnsCacheItem cache,
- IoServiceItem &io_serv,
const boost::asio::ip::address &name_server)
- : ResolverBase( hostname, cache )
- , IoService( io_serv )
+ : ResolverBase( io_serv, hostname, cache )
, Socket( *io_serv, ip::udp::endpoint(ip::udp::v4(), 0) )
- , ReplyBuffer()
- , NameServer( name_server, DNS_PORT )
+ , ReceiveBuffer()
+ , NameServer( name_server, Config::DNS_PORT )
, ResolveTimeoutTimer( *io_serv )
, PauseBeforeRetryTimer( *io_serv )
, StaleDataLongtermTimer( *io_serv )
- , CallbackList()
- , HostAddressList::const_iterator()
+ , NextIpIndex( 0 )
, RetryCount( 0 )
, IsResolving( false )
{ }
/**
* 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)
+void DnsResolver::do_resolve()
{
- // remember callback
- CallbackList.push(callback);
-
// check if resolving already
- if (isResolving)
+ if (IsResolving)
{
GlobalLogger.info()
- << "Call to async_resolve ignored since resolving already";
+ << "Call to do_resolve ignored since resolving already";
return;
}
StaleDataLongtermTimer.cancel();
// create DNS request
- boost::net::dns::message dns_message( host_dns_address,
+ boost::net::dns::message dns_message( ResolverBase::Hostname,
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.action(boost::net::dns::message::query);
+ dns_message.opcode(boost::net::dns::message::squery);
+ dns_message.id(Config::UniqueID);
+ boost::net::dns_buffer_t request_buffer;
dns_message.encode(request_buffer);
// setup receipt of reply
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::handle_resolve_timeout,
- this, boost::asio::placeholders::error) );
+ (void) ResolveTimeoutTimer.expires_from_now(
+ seconds(Config::ResolveTimeoutSeconds));
+ ResolveTimeoutTimer.async_wait( boost::bind(
+ &DnsResolver::handle_resolve_timeout,
+ this, boost::asio::placeholders::error) );
// send dns request
Socket.send_to( boost::asio::buffer(request_buffer.get_array()),
}
-void handle_dns_result(const boost::system::error_code &error,
+void DnsResolver::handle_dns_result(const boost::system::error_code &error,
const std::size_t bytes_transferred)
{
if ( error == boost::asio::error::operation_aborted ) // cancelled
{
- resolve_log.info() << "DNS resolve operation was cancelled";
+ GlobalLogger.info() << "DNS resolve operation was cancelled";
bool was_success = false;
finalize_resolve(was_success);
}
else if (error)
{
- resolve_log.info() << "DNS resolve resulted in error " << 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();
+ boost::net::dns::message result_message;
result_message.decode( ReceiveBuffer );
- if (result_message.answers().size == 0)
+ // work with a regular pointer to list of answers since result_message is
+ // owner of data and that exists until end of function
+ // Items in answers list are shared_ptr to resource_base_t
+ boost::net::dns::rr_list_t *answers = result_message.answers();
+ if (answers->size() == 0)
handle_unavailable();
// 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;
- HostAddressList ip_list;
- std::vector<cname_item_type> cname_list;
- BOOST_FOREACH( rr_item_type rr_item, result_message.answers() )
+ HostAddressVec result_ips;
+ std::vector<std::string> result_cnames;
+ using boost::net::dns::resource_base_t;
+ BOOST_FOREACH( boost::shared_ptr<resource_base_t> rr_item, *answers )
{
- GlobalLogger.debug() << std::showbase << std::hex << rr_item->rtype()
- << ": ";
+ GlobalLogger.debug() << std::showbase << std::hex
+ << static_cast<unsigned>(rr_item->rtype()) << ": ";
uint32_t ttl = rr_item->ttl();
- type_t rr_type = rr_item->rtype();
+ boost::net::dns::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) );
+ boost::asio::ip::address_v4 ip =
+ ( dynamic_cast<boost::net::dns::a_resource *> (rr_item.get()) )
+ ->address();
+ result_ips.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) );
+ boost::asio::ip::address_v6 ip =
+ ( dynamic_cast<boost::net::dns::a6_resource *> (rr_item.get()) )
+ ->address();
+ result_ips.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));
+ std::string cname =
+ (dynamic_cast<boost::net::dns::cname_resource *>(rr_item.get()))
+ ->canonicalname();
+ result_cnames.push_back( cname );
}
else if (rr_type == boost::net::dns::type_ns)
GlobalLogger.debug() << "NS resource";
GlobalLogger.debug() << "unknown resource type";
}
- GlobalLogger.info() << "Have " << ip_list.size() << " IPs and "
- << cname_list.size() << " CNAMEs";
+ GlobalLogger.info() << "Have " << result_ips.size() << " IPs and "
+ << result_cnames.size() << " CNAMEs";
// We expect either one single CNAME and no IPs or a list of IPs.
// But deal with other cases as well
- if (ip_list.empty() && cname_list.empty())
+ if (result_ips.empty() && result_cnames.empty())
handle_unavailable(); // we just got crap, this is a dead end
- else if ( !ip_list.empty() && !cname_list.empty())
+ else if ( !result_ips.empty() && !result_cnames.empty())
GlobalLogger.warning() << "Have CNAMEs AND IPs --> deal with both!";
- BOOST_FOREACH( const cname_resource &cname, cname_list )
+ BOOST_FOREACH( const std::string &cname, result_cnames )
handle_cname(cname); // will schedule another DNS call
- if ( !ip_list.empty() )
- handle_ips(ip_list);
+ if ( !result_ips.empty() )
+ handle_ips(result_ips);
}
-void DnsResolver::handle_ips(const HostAddressList &ips)
+void DnsResolver::handle_ips(const HostAddressVec &ips)
{
// save in cache
ResolverBase::update_cache( ips );
void DnsResolver::handle_unavailable()
{
// schedule new attempt in quite a while
- StaleDataLongtermTimer.expires_from_now(minutes(StaleDataLongtermMinutes));
+ StaleDataLongtermTimer.expires_from_now(
+ minutes(Config::StaleDataLongtermMinutes));
StaleDataLongtermTimer.async_wait(
- bind( &DnsResolver::wait_timer_timeout_handler,
- this, boost::asio::placeholders::error
+ boost::bind( &DnsResolver::wait_timer_timeout_handler,
+ this, boost::asio::placeholders::error
)
);
// 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,
- canonical_name, _1, _2
- )
- );
+ callback_type callback = boost::bind( &DnsResolver::cname_resolve_callback,
+ this, canonical_name, _1, _2 );
+ resolver->async_resolve( callback );
stop_trying();
}
-void cname_resolve_callback(const boost::system::error_code &error,
- const std::string &canonical_name,
- const bool was_success,
- const int cname_count)
+void DnsResolver::cname_resolve_callback(const std::string &canonical_name,
+ const bool was_success,
+ const int cname_count)
{
- 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
+ // 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);
}
// 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();
- }
+ ResolverBase::schedule_callbacks(was_success, cname_count);
// finalize
GlobalLogger.notice() << "Done resolving"
RetryCount = 0;
}
-viod DnsResolver::handle_resolve_timeout(const boost::system::error_code &error)
+void DnsResolver::handle_resolve_timeout(const boost::system::error_code &error)
{
if ( error == boost::asio::error::operation_aborted ) // cancelled
{
// increment timer
++RetryCount;
- if (RetryCount > MaxRetryCount)
+ if (RetryCount > Config::MaxRetryCount)
{
handle_unavailable();
RetryCount = 0;
else
{ // schedule retry
PauseBeforeRetryTimer.expires_from_now(
- seconds(PauseBeforeRetrySeconds));
+ seconds(Config::PauseBeforeRetrySeconds));
PauseBeforeRetryTimer.async_wait(
- bind( &DnsResolver::wait_timer_timeout_handler,
- this, boost::asio::placeholders::error) );
+ boost::bind( &DnsResolver::wait_timer_timeout_handler,
+ this, boost::asio::placeholders::error) );
}
}
else
{
GlobalLogger.info() << "Done waiting --> re-try resolve";
- async_resolve();
+ do_resolve();
}
}
// RETRIEVAL
//==============================================================================
-HostAddress& DnsResolver::get_next_ip()
+HostAddress DnsResolver::get_next_ip()
{
// get cached data
- ResolverBase::get_cached_results();
+ HostAddressVec cached_data = ResolverBase::get_cached_results();
// if no results cached, return default-constructed HostAddress (0.0.0.0)
if ( cached_data.empty() )
- return HostAddress;
+ {
+ HostAddress return_value;
+ return return_value;
+ }
// check validity of index (cache may have changed since last call)
if (NextIpIndex >= cached_data.size())
#include "dns_neww/dnsmaster.h"
-#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 std::list<callback_type> callback_list_type;
-
-typedef std::queue<HostAddress> IpTtlVec;
class DnsResolver : public ResolverBase
{
// constructor accessible from friend DnsMaster
public:
- friend DnsResolverItem DnsMaster::get_resolver_for(const std::string&);
+ friend ResolverItem& DnsMaster::get_resolver_for(
+ const std::string &hostname);
private:
- DnsResolver(const std::string &hostname,
+ DnsResolver(IoServiceItem &io_serv,
+ const std::string &hostname,
const DnsCacheItem cache,
- IoServiceItem &io_serv,
const boost::asio::ip::address &name_server);
-// only real public functions (called from pingers)
+// only real public function (called from pingers)
public:
- void async_resolve(const callback_type &callback);
- HostAddress& get_next_ip();
+ HostAddress get_next_ip();
+
+// implementation of ResolverBase::async_resolve
+protected:
+ void do_resolve();
private:
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,
+ void handle_unavailable();
+ void handle_ips(const HostAddressVec &ips);
+ void handle_cname(const std::string &canonical_name);
+ void cname_resolve_callback(const std::string &canonical_name,
const bool was_success,
const int cname_count);
void finalize_resolve(const bool success, const int cname_count=0);
void wait_timer_timeout_handler(const boost::system::error_code &error);
private:
- IoServiceItem IoService;
boost::asio::ip::udp::socket Socket;
- boost::net::dns_buffer_t ReplyBuffer;
+ boost::net::dns_buffer_t ReceiveBuffer;
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;
- int NextIpIndex;
+ std::size_t NextIpIndex;
int RetryCount;
bool IsResolving;
};