2 The software in this package is distributed under the GNU General
3 Public License version 2 (with a special exception described below).
5 A copy of GNU General Public License (GPL) is included in this distribution,
6 in the file COPYING.GPL.
8 As a special exception, if other files instantiate templates or use macros
9 or inline functions from this file, or you compile this file and link it
10 with other works to produce a work based on this file, this file
11 does not by itself cause the resulting work to be covered
12 by the GNU General Public License.
14 However the source code for this file must still be made available
15 in accordance with section (3) of the GNU General Public License.
17 This exception does not invalidate any other reasons why a work based
18 on this file might be covered by the GNU General Public License.
20 Christian Herdtweck, Intra2net AG 2015
22 with code copied from boost::net::dns::resolve.hpp
23 by Andreas Haberstroh (andreas at ibusy dot com)
24 from https://github.com/softwareace/Boost.DNS
27 #include "dns/dnsresolver.h"
32 #include <boost/foreach.hpp>
33 #include <boost/bind.hpp>
34 #include <boost/function.hpp>
35 #include <boost/net/dns.hpp>
36 #include <boost/date_time/posix_time/posix_time.hpp>
37 #include <boost/uuid/uuid.hpp>
38 #include <boost/uuid/uuid_io.hpp>
40 #include <logfunc.hpp>
42 using I2n::Logger::GlobalLogger;
43 using boost::posix_time::seconds;
44 using boost::posix_time::minutes;
48 const int ResolveTimeoutSeconds = 3;
49 const int PauseBeforeRetrySeconds = 10;
50 const int StaleDataLongtermMinutes = 15;
51 const int DNS_PORT = 53;
54 DnsResolver::DnsResolver(IoServiceItem &io_serv,
55 const std::string &hostname,
56 const DnsIpProtocol &protocol,
57 const DnsCacheItem cache,
58 const boost::asio::ip::address &name_server)
59 : ResolverBase( io_serv, hostname, cache )
60 , Socket( *io_serv, ip::udp::endpoint(ip::udp::v4(), 0))
61 // just connect to anything, will specify sender/receiver later
64 , Protocol( protocol )
65 , NameServer( name_server, Config::DNS_PORT )
66 , ResolveTimeoutTimer( *io_serv )
67 , PauseBeforeRetryTimer( *io_serv )
68 , StaleDataLongtermTimer( *io_serv )
71 , IsResolving( false )
72 , LogPrefix( "DnsResolver" )
76 , OperationCancelled( false )
78 std::stringstream temp;
79 temp << "Dns(" << ResolverBase::Hostname << "): ";
80 LogPrefix = temp.str();
84 DnsResolver::~DnsResolver()
86 boost::system::error_code error;
87 //Socket.shutdown(boost::asio::ip::udp::socket::shutdown_both, error);
89 // GlobalLogger.warning() << LogPrefix << "Received error " << error
90 // << " when shutting down socket for DNS";
91 // in IcmpPinger always gave an error system:9 (EBADF: Bad file descriptor)
92 // Here gives error system:107 ENOTCONN: Transport endpoint is not connected
96 GlobalLogger.warning() << LogPrefix << "Received error " << error
97 << " when closing socket for DNS";
102 //==============================================================================
104 //==============================================================================
107 * copied here code from boost::net::dns::resolve.hpp, since want async
108 * operation and that is used only internally, there
110 void DnsResolver::do_resolve()
112 // check if resolving already
115 GlobalLogger.info() << LogPrefix
116 << "Call to do_resolve ignored since resolving already";
120 OperationCancelled = false;
122 GlobalLogger.info() << LogPrefix << "start resolving for IPs of type "
123 << to_string(Protocol) << " using name server " << NameServer;
125 // just to be sure: cancel timers
126 ResolveTimeoutTimer.cancel();
127 PauseBeforeRetryTimer.cancel();
128 StaleDataLongtermTimer.cancel();
130 // create DNS request
131 boost::net::dns::message dns_message( ResolverBase::Hostname, Protocol );
132 dns_message.recursive(false);
133 dns_message.action(boost::net::dns::message::query);
134 dns_message.opcode(boost::net::dns::message::squery);
136 // create random ID for message
137 boost::uuids::uuid message_id = RandomIdGenerator();
138 memcpy( &RequestId, message_id.data, sizeof(RequestId) );
139 dns_message.id( RequestId );
140 GlobalLogger.debug() << LogPrefix << "Request has ID "
141 << std::showbase << std::hex << dns_message.id();
143 // setup receipt of reply
144 Socket.async_receive_from(
145 boost::asio::buffer(ReceiveBuffer.get_array()),
147 boost::bind( &DnsResolver::handle_dns_result, this,
148 boost::asio::placeholders::error,
149 boost::asio::placeholders::bytes_transferred)
153 (void) ResolveTimeoutTimer.expires_from_now(
154 seconds(Config::ResolveTimeoutSeconds));
155 ResolveTimeoutTimer.async_wait( boost::bind(
156 &DnsResolver::handle_resolve_timeout,
157 this, boost::asio::placeholders::error) );
160 dns_message.encode(RequestBuffer);
164 bytes_sent = Socket.send_to(
165 boost::asio::buffer(RequestBuffer.get_array()),
168 catch (boost::system::system_error &err)
170 GlobalLogger.warning() << LogPrefix
171 << "Sending of DNS request message failed: "
177 if ( bytes_sent == 0 )
179 GlobalLogger.warning() << LogPrefix << "Empty DNS request sent!";
186 void DnsResolver::handle_dns_result(const boost::system::error_code &error,
187 const std::size_t bytes_transferred)
191 GlobalLogger.info() << LogPrefix << "DNS resolve resulted in error "
192 << error << " --> try again after a little while";
196 else if ( OperationCancelled )
197 { // async_resolve was cancelled --> callbacks already called
198 GlobalLogger.info() << LogPrefix
199 << "Ignoring DNS results since we were cancelled";
203 GlobalLogger.debug() << LogPrefix << "Handling DNS result ("
204 << bytes_transferred << " bytes transferred)";
206 // next 3(+1) lines copied from boost/net/dns/resolver.hpp:
207 // clamp the recvBuffer with the number of bytes transferred or decode buffr
208 ReceiveBuffer.length(bytes_transferred);
209 boost::net::dns::message result_message;
210 result_message.decode( ReceiveBuffer );
213 if (RequestId != result_message.id())
214 GlobalLogger.warning() << LogPrefix << "Received answer for request ID "
215 << std::showbase << std::hex << result_message.id()
216 << " but expected ID " << RequestId;
218 GlobalLogger.debug() << LogPrefix << "Result has correct ID "
219 << std::showbase << std::hex << RequestId;
222 // loop over answers, remembering ips and cnames
223 // work with a regular pointer to list of answers since result_message is
224 // owner of data and that exists until end of function
225 // Items in answers list are shared_ptr to resource_base_t
226 std::vector<host_addr_pair> result_ips;
227 std::vector<src_cname_pair> result_cnames;
228 std::vector<string_pair> result_name_servers;
230 GlobalLogger.debug() << "Checking ANSWERS section of dns reply";
231 gather_results(result_message.answers(), &result_ips, &result_cnames,
232 &result_name_servers);
233 // results should have the logical order
234 // Hostname [ --> cname1 --> cname2 --> ... --> cnameN ] [ --> ips ]
236 // remember cname list (if there were any)
237 BOOST_FOREACH( const src_cname_pair &host_and_cname, result_cnames )
239 GlobalLogger.debug() << LogPrefix << "Remember CNAME "
240 << host_and_cname.first << " --> " << host_and_cname.second.first;
241 ResolverBase::update_cache(host_and_cname.first, host_and_cname.second);
244 if ( !result_ips.empty() )
245 handle_ips( result_ips );
246 else if ( !result_cnames.empty() )
247 // no IPs but at least one cname --> find the "last" cname and
248 // re-start resolving with that
249 handle_cname(result_cnames);
251 { // no answers --> check for name_servers in authorities section
252 if ( !result_name_servers.empty() )
253 GlobalLogger.warning() << LogPrefix
254 << "Received NS records in answers! "
255 << "That is quite unexpected...";
256 GlobalLogger.debug() << "Checking AUTHORITIES section of dns reply";
257 gather_results(result_message.authorites(), &result_ips,
258 &result_cnames, &result_name_servers);
259 GlobalLogger.debug() << "Checking ADDITIONALS section of dns reply";
260 gather_results(result_message.additionals(), &result_ips,
261 &result_cnames, &result_name_servers);
263 // search for a name_server for which an IP is given
264 bool have_recursed = false;
265 BOOST_FOREACH( const string_pair &name_server, result_name_servers )
267 if (name_server.second == Hostname)
268 { // in case we try to resolve IP for name server, do not use same
269 GlobalLogger.debug() << LogPrefix
270 << "Name server found is same as hostname --> skip";
274 // go through ips and look for match
275 BOOST_FOREACH( const host_addr_pair &host_and_addr, result_ips )
277 if (name_server.second == host_and_addr.first)
279 GlobalLogger.info() << LogPrefix << "Ask next name_server "
280 << name_server.second << " with IP "
281 << host_and_addr.second.get_ip() << " (responsible for "
282 << name_server.first << ")";
283 have_recursed = true;
284 handle_recurse( host_and_addr.second );
292 if ( !have_recursed )
293 { // no name_server with ip found -- strange
294 if (result_name_servers.empty())
296 GlobalLogger.error() << LogPrefix << "Result contained neither "
297 << "IP nor CNAME nor name server --> cannot proceed!";
298 handle_unavailable();
302 GlobalLogger.warning() << LogPrefix
303 << "There are " << result_name_servers.size()
304 << " name_servers given but none with IP!";
305 handle_recurse_without_ip(result_name_servers[0].second);
311 void DnsResolver::gather_results(const boost::net::dns::rr_list_t *answers,
312 std::vector<host_addr_pair> *result_ips,
313 std::vector<src_cname_pair> *result_cnames,
314 std::vector<string_pair> *result_name_servers)
317 using boost::net::dns::resource_base_t;
318 boost::posix_time::ptime now =boost::posix_time::second_clock::local_time();
319 BOOST_FOREACH( boost::shared_ptr<resource_base_t> rr_item, *answers )
321 boost::net::dns::type_t rr_type = rr_item->rtype();
322 uint32_t ttl = rr_item->ttl();
323 std::string domain = rr_item->domain();
325 boost::posix_time::to_simple_string(now + seconds(ttl));
327 if (rr_type == boost::net::dns::type_a)
328 { // 'A' resource records carry IPv4 addresses
329 if (Protocol == DNS_IPv6)
331 GlobalLogger.info() << LogPrefix << "Ignoring IPv4 address "
332 << "because resolver was configured to only use IPv6.";
335 boost::asio::ip::address_v4 ip =
336 ( dynamic_cast<boost::net::dns::a_resource *> (rr_item.get()) )
338 result_ips->push_back(host_addr_pair(domain, HostAddress(ip, ttl)));
339 GlobalLogger.debug() << LogPrefix << domain << ": IPv4 " << ip
340 << " with TTL " << ttl << "s (until "
343 else if (rr_type == boost::net::dns::type_a6)
344 { // 'AAAA' resource records carry IPv6 addresses
345 if (Protocol == DNS_IPv4)
347 GlobalLogger.info() << LogPrefix << "Ignoring IPv6 address "
348 << "because resolver was configured to only use IPv4.";
351 boost::asio::ip::address_v6 ip =
352 ( dynamic_cast<boost::net::dns::a6_resource *> (rr_item.get()) )
354 result_ips->push_back(host_addr_pair(domain, HostAddress(ip, ttl)));
355 GlobalLogger.debug() << LogPrefix << domain << ": IPv6 " << ip
356 << " with TTL " << ttl << "s (until "
359 else if (rr_type == boost::net::dns::type_cname)
360 { // 'CNAME' resource records that carry aliases
362 (dynamic_cast<boost::net::dns::cname_resource *>(rr_item.get()))
364 result_cnames->push_back( src_cname_pair(domain,
365 Cname(cname, ttl)) );
366 GlobalLogger.debug() << LogPrefix << domain << ": CNAME to "
367 << cname << " with TTL " << ttl << "s (until "
370 else if (rr_type == boost::net::dns::type_ns)
371 { // NS (name_server) resource records
372 std::string name_server =
373 (dynamic_cast<boost::net::dns::ns_resource *>(rr_item.get()))
375 result_name_servers->push_back( string_pair(domain, name_server) );
376 GlobalLogger.debug() << LogPrefix << "NameServer " << name_server
377 << " for " << domain << " with TTL " << ttl
378 << "s (until " << expiry << ")";
380 else if (rr_type == boost::net::dns::type_soa)
381 GlobalLogger.debug() << LogPrefix << "SOA resource";
382 else if (rr_type == boost::net::dns::type_ptr)
383 GlobalLogger.debug() << LogPrefix << "ptr resource";
384 else if (rr_type == boost::net::dns::type_hinfo)
385 GlobalLogger.debug() << LogPrefix << "hinfo resource";
386 else if (rr_type == boost::net::dns::type_mx)
387 GlobalLogger.debug() << LogPrefix << "mx resource";
388 else if (rr_type == boost::net::dns::type_txt)
389 GlobalLogger.debug() << LogPrefix << "txt resource";
390 else if (rr_type == boost::net::dns::type_srv)
391 GlobalLogger.debug() << LogPrefix << "srv resource";
392 else if (rr_type == boost::net::dns::type_axfr)
393 GlobalLogger.debug() << LogPrefix << "axfr resource";
395 GlobalLogger.debug() << LogPrefix << "unknown resource type: "
396 << std::showbase << std::hex
397 << static_cast<unsigned>(rr_item->rtype());
402 void DnsResolver::handle_unavailable()
404 // schedule new attempt in quite a while
405 StaleDataLongtermTimer.expires_from_now(
406 minutes(Config::StaleDataLongtermMinutes));
407 StaleDataLongtermTimer.async_wait(
408 boost::bind( &DnsResolver::wait_timer_timeout_handler,
409 this, boost::asio::placeholders::error
413 // for now, admit failure
414 bool was_success = false;
415 finalize_resolve(was_success);
419 void DnsResolver::handle_ips(const std::vector<host_addr_pair> &result_ips)
421 // received at least one IP which could be for the queried host name
422 // or the cname at the "end" of the cname list;
423 // but all IPs should be for the same
424 HostAddressVec addr_list;
425 std::string only_host_for_ips = result_ips[0].first;
426 BOOST_FOREACH( const host_addr_pair &host_and_addr, result_ips)
428 if ( host_and_addr.first != only_host_for_ips )
429 GlobalLogger.warning() << LogPrefix
430 << "Received IPs for different hosts " << only_host_for_ips
431 << " and " << host_and_addr.first << " in one DNS result! "
432 << "--> ignore second";
435 GlobalLogger.notice() << LogPrefix << "Found IP "
436 << host_and_addr.second.get_ip() << " with TTL "
437 << host_and_addr.second.get_ttl().get_value() << "s";
438 addr_list.push_back(host_and_addr.second);
441 ResolverBase::update_cache( only_host_for_ips, addr_list );
444 bool was_success = true;
445 finalize_resolve(was_success);
449 void DnsResolver::handle_cname(const std::vector<src_cname_pair> &result_cnames)
451 // find the "last" cname in the list
452 // Hostname --> cname1 --> cname2 --> ... --> cnameN
453 // We assume here that this list might not be in order but that all cnames
454 // form a single list (form one connected list and not several isolated)
456 // host_and_cname.second is a Cname, which is a pair (destination, ttl)
457 std::string last_cname = "";
459 BOOST_REVERSE_FOREACH( const src_cname_pair &host_and_cname, result_cnames )
461 could_be_last = true;
462 BOOST_REVERSE_FOREACH( const src_cname_pair &other, result_cnames )
464 if (other.first == host_and_cname.second.first)
465 { // found cname for current cname
466 could_be_last = false;
472 last_cname = host_and_cname.second.first;
477 if (last_cname.empty())
479 GlobalLogger.error() << LogPrefix
480 << "Could not identify \"last\" CNAME to handle -- "
481 << "maybe we encountered a CNAME loop? Anyway, cannot proceed!";
482 GlobalLogger.info() << LogPrefix << "Result CNAMEs were:";
483 BOOST_FOREACH( const src_cname_pair &host_and_cname, result_cnames )
484 GlobalLogger.info() << LogPrefix << host_and_cname.first << " --> "
485 << host_and_cname.second.first;
486 handle_unavailable();
489 { // check cache for IP for this cname
490 bool check_up_to_date = true;
491 HostAddressVec cached_data = Cache->get_ips_recursive(last_cname,
493 if ( !cached_data.empty() )
495 bool was_success = true;
496 int recursion_count = 1; // define cache access as only 1
497 finalize_resolve(was_success, recursion_count);
500 { // get resolver for canonical name
501 // as opposed to the interal Recursor variable used in
502 // handle_recurse, this is a "proper" resolver that is maintained
503 // and cached by DnsMaster --> independent of this Resolver
504 ResolverItem resolver = DnsMaster::get_instance()
505 ->get_resolver_for(last_cname, Protocol);
506 callback_type callback = boost::bind(
507 &DnsResolver::cname_resolve_callback,
509 resolver->async_resolve( callback );
511 // treat a CNAME as a partial result: not enough to run callbacks
512 // from finalize_resolve, but enough to stop timers and reset
513 // RetryCount --> name resolution can take longer
520 void DnsResolver::cname_resolve_callback(const bool was_success,
521 const int recursion_count)
523 if ( OperationCancelled )
524 { // async_resolve was cancelled --> callbacks already called
525 GlobalLogger.info() << LogPrefix
526 << "Ignoring CNAME results since we were cancelled";
529 else if (was_success)
530 GlobalLogger.debug() << LogPrefix << "CNAME resolution succeeded";
532 GlobalLogger.info() << LogPrefix << "CNAME resolution failed";
533 // no use to schedule retry in this case since cname resolver must have
534 // failed several times and we can just re-start the same procedure with
535 // the same information (in recursion can try different name server)
536 // --> no schedule_retry
538 // cname counts like one more recursion step ...
539 finalize_resolve(was_success, recursion_count+1);
543 void DnsResolver::handle_recurse(const HostAddress &name_server)
545 // get resolver for same hostname but using a different name server
548 if (Recursor->is_resolving())
550 GlobalLogger.warning() << LogPrefix << "Recursor is resolving! "
551 << "Will cancel and reset";
552 Recursor->cancel_resolve();
555 GlobalLogger.warning() << LogPrefix
556 << "Recursor has not been reset!";
560 Recursor = DnsMaster::get_instance()->get_recursor_for(
561 Hostname, Protocol, name_server.get_ip());
562 callback_type callback = boost::bind(
563 &DnsResolver::recursive_resolve_callback,
564 this, name_server.get_ttl().get_value(),
566 Recursor->async_resolve( callback );
568 // do not cancel timers or reset RetryCount
573 void DnsResolver::recursive_resolve_callback(const uint32_t min_ttl,
574 const bool was_success,
575 const int recursion_count)
578 << "Recursion back at request with name server " << NameServer;
580 // do not need recursor any more; next time re-create from different random
581 // name server; may have been reset already in cancel_resolve(), so that
582 // is ok. If not, issue a warning
585 if ( !OperationCancelled )
586 GlobalLogger.warning() << LogPrefix
587 << "Recursor was reset before callback!";
592 if ( OperationCancelled )
593 { // async_resolve was cancelled --> callbacks already called
594 GlobalLogger.info() << LogPrefix
595 << "Ignoring recursion results since we were cancelled";
598 else if (was_success)
600 // make sure the saved TTL is not larger than the one we found here
601 ResolverBase::update_cache(min_ttl);
602 finalize_resolve(was_success, recursion_count+1);
606 GlobalLogger.info() << LogPrefix << "Recursive resolution failed";
612 void DnsResolver::handle_recurse_without_ip(const std::string &name_server)
614 // get resolver for name_server
615 // save in Recursor although it is a "proper" resolver (so result is cached)
618 if (Recursor->is_resolving())
620 GlobalLogger.warning() << LogPrefix << "Recursor is resolving! "
621 << "Will cancel and reset";
622 Recursor->cancel_resolve();
625 GlobalLogger.warning() << LogPrefix
626 << "Recursor has not been reset!";
629 Recursor = DnsMaster::get_instance()->get_resolver_for(name_server,
632 // check for IPs in cache
633 if (Recursor->get_resolved_ip_count() == 0)
635 GlobalLogger.info() << LogPrefix
636 << "Start to resolve address of name server " << name_server;
637 callback_type callback = boost::bind(
638 &DnsResolver::name_server_resolve_callback,
640 Recursor->async_resolve( callback );
644 GlobalLogger.info() << LogPrefix << "Use cached ip for name server "
646 HostAddress ip = Recursor->get_next_ip();
652 void DnsResolver::name_server_resolve_callback(const bool was_success,
653 const int recursion_count)
655 if (OperationCancelled)
657 GlobalLogger.info() << LogPrefix
658 << "Ignoring name server IP results since we were cancelled";
661 else if (was_success)
663 HostAddress ip = Recursor->get_next_ip();
664 GlobalLogger.info() << LogPrefix << "Found IP " << ip.get_ip()
665 << " for name server " << Recursor->get_hostname();
671 GlobalLogger.info() << LogPrefix << "Failed to find IP for name server"
672 << Recursor->get_hostname() << " --> schedule retry";
679 void DnsResolver::finalize_resolve(const bool was_success,
680 const int recursion_count)
682 // some consistency checks; failure might indicate a situation I had not
683 // anticipated during programming but might not be harmfull yet
685 GlobalLogger.warning() << LogPrefix << "Consistency check failed: "
686 << "not resolving any more!";
687 if ( OperationCancelled )
688 GlobalLogger.warning() << LogPrefix << "Consistency check failed: "
689 << " was cancelled!";
690 if ( ResolverBase::CallbackList.empty() )
691 GlobalLogger.warning() << LogPrefix << "Consistency check failed: "
693 if ( RequestId != 0 )
694 GlobalLogger.warning() << LogPrefix << "Consistency check failed: "
695 << "waiting for DNS reply!";
700 // schedule callbacks, clearing callback list
701 ResolverBase::schedule_callbacks(was_success, recursion_count);
704 GlobalLogger.notice() << LogPrefix << "finalized resolve"
705 << " with success = " << was_success
706 << " and recursion_count = " << recursion_count;
711 void DnsResolver::stop_trying()
714 GlobalLogger.debug() << LogPrefix << "Cancelling timers";
715 ResolveTimeoutTimer.cancel();
716 PauseBeforeRetryTimer.cancel();
717 StaleDataLongtermTimer.cancel();
724 bool DnsResolver::is_resolving()
731 * cancel a earlier call to async_resolve
733 * callbacks will be called with was_success=false; all internal operations
734 * will be cancelled and internal callbacks (timers, dns results) have no
737 void DnsResolver::cancel_resolve()
741 GlobalLogger.info() << LogPrefix
742 << "Cancel called on non-resolving resolver -- ignore";
745 else if (OperationCancelled)
747 GlobalLogger.info() << LogPrefix
748 << "Cancel called on cancelled resolver -- ignore";
753 Recursor->cancel_resolve(); // does not hurt even if it is not resolving
755 // set before finalize_resolve so can check in finalize_resolve that ID is
756 // always 0; ID is not used any more since handle_dns_result stops if
757 // OperationCancelled is true
760 bool was_success = false;
761 int recursion_count = 1;
762 finalize_resolve(was_success, recursion_count);
764 // set after finalize_resolve, so can check in finalize_resolve that
765 // OperationCancelled is never true
766 OperationCancelled = true;
771 void DnsResolver::handle_resolve_timeout(const boost::system::error_code &error)
773 if ( error == boost::asio::error::operation_aborted ) // cancelled
775 GlobalLogger.warning() << LogPrefix
776 << "Resolve timeout timer was cancelled!";
781 GlobalLogger.warning() << LogPrefix
782 << "resolve timeout handler received error "
783 << error << " --> retry";
786 else if ( OperationCancelled )
787 { // async_resolve was cancelled --> callbacks already called
788 GlobalLogger.info() << LogPrefix
789 << "Ignoring DNS timeout since we were cancelled";
794 GlobalLogger.notice() << LogPrefix << "DNS resolving timed out";
800 void DnsResolver::schedule_retry()
802 // this function is called in all sorts of error cases
803 // --> need to clean up a bit
806 Recursor->cancel_resolve();
809 ResolveTimeoutTimer.cancel();
810 PauseBeforeRetryTimer.cancel();
815 if ( RetryCount > DnsMaster::get_instance()
816 ->get_max_address_resolution_attempts() )
817 { // too many re-tries
818 GlobalLogger.info() << LogPrefix << "Not scheduling a retry since "
819 << "RetryCount " << RetryCount << " too high";
820 handle_unavailable(); // will call stop_trying i.e. reset RetryCount
824 GlobalLogger.info() << LogPrefix << "Scheduling a retry (RetryCount="
825 << RetryCount << ")";
826 PauseBeforeRetryTimer.expires_from_now(
827 seconds(Config::PauseBeforeRetrySeconds));
828 PauseBeforeRetryTimer.async_wait(
829 boost::bind( &DnsResolver::wait_timer_timeout_handler,
830 this, boost::asio::placeholders::error) );
834 void DnsResolver::wait_timer_timeout_handler(
835 const boost::system::error_code &error)
837 if ( error == boost::asio::error::operation_aborted ) // cancelled
838 { // assume that our code cancelled this timer, so callbacks will be
840 GlobalLogger.warning() << LogPrefix
841 << "Resolve wait timer was cancelled! ";
844 { // not sure what to do here, but callers waiting forever for a callback
845 // is probably the worst thing to happen, so call finalize_resolve
846 GlobalLogger.warning() << LogPrefix
847 << "resolve wait handler received error "
848 << error << "! Try to finalize resolve";
849 bool was_success = false;
850 finalize_resolve(was_success);
852 else if ( OperationCancelled )
853 { // async_resolve was cancelled --> callbacks already called
854 GlobalLogger.info() << LogPrefix
855 << "Ignoring waiting timeout since we were cancelled";
860 GlobalLogger.info() << LogPrefix << "Done waiting --> re-try resolve";
866 //==============================================================================
868 //==============================================================================
870 HostAddress DnsResolver::get_next_ip()
873 HostAddressVec cached_data = ResolverBase::get_cached_ips_recursively();
875 // if no results cached, return default-constructed HostAddress (0.0.0.0)
876 if ( cached_data.empty() )
878 HostAddress return_value;
882 // check validity of index (cache may have changed since last call)
883 if (NextIpIndex >= cached_data.size())
887 return cached_data[NextIpIndex++];
890 bool DnsResolver::have_up_to_date_ip()
892 return ! ResolverBase::get_cached_ips_recursively("", true).empty();
895 int DnsResolver::get_resolved_ip_count()
897 return ResolverBase::get_cached_ips_recursively().size();
900 // (created using vim -- the world's best text editor)