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;
47 const int ResolveTimeoutSeconds = 2;
48 const int PauseBeforeRetrySeconds = 1;
49 const int StaleDataLongtermSeconds = 5*60;
50 const int DNS_PORT = 53;
53 DnsResolver::DnsResolver(IoServiceItem &io_serv,
54 const std::string &hostname,
55 const DnsIpProtocol &protocol,
56 const DnsCacheItem cache,
57 const boost::asio::ip::address &name_server)
58 : ResolverBase( io_serv, hostname, protocol, cache )
59 , Socket( *io_serv, ip::udp::endpoint(ip::udp::v4(), 0))
60 // just connect to anything, will specify sender/receiver later
63 , NameServer( name_server, Config::DNS_PORT )
64 , ResolveTimeoutTimer( *io_serv )
65 , PauseBeforeRetryTimer( *io_serv )
66 , StaleDataLongtermTimer( *io_serv )
69 , IsResolving( false )
70 , LogPrefix( "DnsResolver" )
73 , OperationCancelled( false )
74 , LongtermTimerIsActive( false )
76 std::stringstream temp;
77 temp << "Dns(" << ResolverBase::Hostname << "): ";
78 LogPrefix = temp.str();
82 DnsResolver::~DnsResolver()
84 boost::system::error_code error;
85 //Socket.shutdown(boost::asio::ip::udp::socket::shutdown_both, error);
87 // GlobalLogger.info() << LogPrefix << "Received error " << error
88 // << " when shutting down socket for DNS";
89 // in IcmpPinger always gave an error system:9 (EBADF: Bad file descriptor)
90 // Here gives error system:107 ENOTCONN: Transport endpoint is not connected
94 GlobalLogger.info() << LogPrefix << "Received error " << error
95 << " when closing socket for DNS";
100 //==============================================================================
102 //==============================================================================
105 * copied here code from boost::net::dns::resolve.hpp, since want async
106 * operation and that is used only internally, there
108 void DnsResolver::do_resolve(const int recursion_count)
110 // check if resolving already
113 GlobalLogger.info() << LogPrefix
114 << "Call to do_resolve ignored since resolving already";
118 OperationCancelled = false;
120 GlobalLogger.info() << LogPrefix << "start resolving for IPs of type "
121 << to_string(Protocol) << " using name server " << NameServer;
123 // just to be sure: cancel timers
124 ResolveTimeoutTimer.cancel();
125 PauseBeforeRetryTimer.cancel();
126 StaleDataLongtermTimer.cancel();
127 LongtermTimerIsActive = false;
129 // create DNS request
130 boost::net::dns::message dns_message( ResolverBase::Hostname, Protocol );
131 dns_message.recursive(true);
132 dns_message.action(boost::net::dns::message::query);
133 dns_message.opcode(boost::net::dns::message::squery);
135 // create random ID for message
136 boost::uuids::uuid message_id = RandomIdGenerator();
137 memcpy( &RequestId, message_id.data, sizeof(RequestId) );
138 dns_message.id( RequestId );
139 GlobalLogger.debug() << LogPrefix << "Request has ID "
140 << std::showbase << std::hex << dns_message.id();
142 // setup receipt of reply
143 Socket.async_receive_from(
144 boost::asio::buffer(ReceiveBuffer.get_array()),
146 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, recursion_count,
158 boost::asio::placeholders::error) );
161 dns_message.encode(RequestBuffer);
165 bytes_sent = Socket.send_to(
166 boost::asio::buffer(RequestBuffer.get_array()),
169 catch (boost::system::system_error &err)
171 GlobalLogger.info() << LogPrefix
172 << "Sending of DNS request message failed: "
174 schedule_retry(recursion_count);
178 if ( bytes_sent == 0 )
180 GlobalLogger.info() << LogPrefix << "Empty DNS request sent!";
181 schedule_retry(recursion_count);
187 void DnsResolver::handle_dns_result(const int recursion_count,
188 const boost::system::error_code &error,
189 const std::size_t bytes_transferred)
193 GlobalLogger.info() << LogPrefix << "DNS resolve resulted in error "
194 << error << " --> request retry";
195 schedule_retry(recursion_count);
198 else if ( OperationCancelled )
199 { // async_resolve was cancelled --> callbacks already called
200 GlobalLogger.info() << LogPrefix
201 << "Ignoring DNS results since we were cancelled";
205 GlobalLogger.debug() << LogPrefix << "Handling DNS result ("
206 << bytes_transferred << " bytes transferred)";
208 // next 3(+1) lines copied from boost/net/dns/resolver.hpp:
209 // clamp the recvBuffer with the number of bytes transferred or decode buffr
210 ReceiveBuffer.length(bytes_transferred);
211 boost::net::dns::message result_message;
212 result_message.decode( ReceiveBuffer );
216 { // system DNS or firewall might have longer timeout than me
217 // --> might receive replies for old requests whose time-out has expired
218 // --> have already called callbacks etc, so nothing to do with result
219 // TODO: did receive probably same data several times --> reset buffer?
220 GlobalLogger.info() << LogPrefix << "Ignoring reply to old DNS request "
221 << "(reply has ID " << std::showbase << std::hex
222 << result_message.id() << " or buffer not reset)";
225 else if (RequestId != result_message.id())
227 GlobalLogger.info() << LogPrefix << "Received answer for request ID "
228 << std::showbase << std::hex << result_message.id()
229 << " but expected ID " << RequestId << " --> ignore and retry";
230 schedule_retry(recursion_count);
234 GlobalLogger.debug() << LogPrefix << "Result has correct ID "
235 << std::showbase << std::hex << RequestId;
238 // loop over answers, remembering ips and cnames
239 // work with a regular pointer to list of answers since result_message is
240 // owner of data and that exists until end of function
241 // Items in answers list are shared_ptr to resource_base_t
242 std::vector<host_addr_pair> result_ips;
243 std::vector<src_cname_pair> result_cnames;
244 std::vector<string_pair> result_name_servers;
246 GlobalLogger.debug() << LogPrefix <<"Checking ANSWERS section of dns reply";
247 gather_results(result_message.answers(), &result_ips, &result_cnames,
248 &result_name_servers);
250 // remember cname list (if there were any)
251 // results should have the logical order
252 // Hostname [ --> cname1 --> cname2 --> ... --> cnameN ] [ --> ips ];
253 // otherwise just have unneccessary cnames in cache
254 BOOST_FOREACH( const src_cname_pair &host_and_cname, result_cnames )
255 ResolverBase::update_cache(host_and_cname.first, host_and_cname.second);
257 if ( !result_ips.empty() )
258 handle_ips( recursion_count, result_ips );
259 else if ( !result_cnames.empty() )
260 // no IPs but at least one cname --> find the "last" cname and
261 // re-start resolving with that
262 handle_cname(recursion_count, result_cnames);
264 { // no answers --> cannot proceed
265 GlobalLogger.info() << LogPrefix << "No IP nor CNAME received! "
266 << "--> request retry";
267 schedule_retry(recursion_count);
272 * gather IPs, CNAMEs and name servers from list of resource records;
274 * can be run on anwers(), autorities() and additional() sections of dns reply
277 * @param rr_list: input list of resource records
278 * @param result_ips: output vector of ips
279 * @param result_cnames: output vector of cnames
280 * @param result_name_servers: output vector of name servers
282 void DnsResolver::gather_results(const boost::net::dns::rr_list_t *rr_list,
283 std::vector<host_addr_pair> *result_ips,
284 std::vector<src_cname_pair> *result_cnames,
285 std::vector<string_pair> *result_name_servers)
288 using boost::net::dns::resource_base_t;
289 boost::posix_time::ptime now =boost::posix_time::second_clock::local_time();
290 BOOST_FOREACH( boost::shared_ptr<resource_base_t> rr_item, *rr_list )
292 boost::net::dns::type_t rr_type = rr_item->rtype();
293 uint32_t ttl = rr_item->ttl();
294 std::string domain = rr_item->domain();
296 boost::posix_time::to_simple_string(now + seconds(ttl));
298 if (rr_type == boost::net::dns::type_a)
299 { // 'A' resource records carry IPv4 addresses
300 if (Protocol == DNS_IPv6)
302 GlobalLogger.info() << LogPrefix << "Ignoring IPv4 address "
303 << "because resolver was configured to only use IPv6.";
306 boost::asio::ip::address_v4 ip =
307 ( dynamic_cast<boost::net::dns::a_resource *> (rr_item.get()) )
309 result_ips->push_back(host_addr_pair(domain, HostAddress(ip, ttl)));
310 GlobalLogger.debug() << LogPrefix << domain << ": IPv4 " << ip
311 << " with TTL " << ttl << "s (until "
314 else if (rr_type == boost::net::dns::type_a6)
315 { // 'AAAA' resource records carry IPv6 addresses
316 if (Protocol == DNS_IPv4)
318 GlobalLogger.info() << LogPrefix << "Ignoring IPv6 address "
319 << "because resolver was configured to only use IPv4.";
322 boost::asio::ip::address_v6 ip =
323 ( dynamic_cast<boost::net::dns::a6_resource *> (rr_item.get()) )
325 result_ips->push_back(host_addr_pair(domain, HostAddress(ip, ttl)));
326 GlobalLogger.debug() << LogPrefix << domain << ": IPv6 " << ip
327 << " with TTL " << ttl << "s (until "
330 else if (rr_type == boost::net::dns::type_cname)
331 { // 'CNAME' resource records that carry aliases
333 (dynamic_cast<boost::net::dns::cname_resource *>(rr_item.get()))
335 result_cnames->push_back( src_cname_pair(domain,
336 Cname(cname, ttl)) );
337 GlobalLogger.debug() << LogPrefix << domain << ": CNAME to "
338 << cname << " with TTL " << ttl << "s (until "
341 else if (rr_type == boost::net::dns::type_ns)
342 { // NS (name_server) resource records
343 std::string name_server =
344 (dynamic_cast<boost::net::dns::ns_resource *>(rr_item.get()))
346 result_name_servers->push_back( string_pair(domain, name_server) );
347 GlobalLogger.debug() << LogPrefix << "NameServer " << name_server
348 << " for " << domain << " with TTL " << ttl
349 << "s (until " << expiry << ")";
351 else if (rr_type == boost::net::dns::type_soa)
352 GlobalLogger.debug() << LogPrefix << "SOA resource";
353 else if (rr_type == boost::net::dns::type_ptr)
354 GlobalLogger.debug() << LogPrefix << "ptr resource";
355 else if (rr_type == boost::net::dns::type_hinfo)
356 GlobalLogger.debug() << LogPrefix << "hinfo resource";
357 else if (rr_type == boost::net::dns::type_mx)
358 GlobalLogger.debug() << LogPrefix << "mx resource";
359 else if (rr_type == boost::net::dns::type_txt)
360 GlobalLogger.debug() << LogPrefix << "txt resource";
361 else if (rr_type == boost::net::dns::type_srv)
362 GlobalLogger.debug() << LogPrefix << "srv resource";
363 else if (rr_type == boost::net::dns::type_axfr)
364 GlobalLogger.debug() << LogPrefix << "axfr resource";
366 GlobalLogger.debug() << LogPrefix << "unknown resource type: "
367 << std::showbase << std::hex
368 << static_cast<unsigned>(rr_item->rtype());
373 void DnsResolver::handle_unavailable(const int recursion_count)
375 // schedule new attempt in quite a while
376 StaleDataLongtermTimer.expires_from_now(
377 seconds(Config::StaleDataLongtermSeconds));
378 StaleDataLongtermTimer.async_wait(
379 boost::bind( &DnsResolver::wait_timer_timeout_handler, this,
381 boost::asio::placeholders::error
384 LongtermTimerIsActive = true;
386 // for now, admit failure
387 RequestId = 0; // do not accept answers from old requests
388 bool was_success = false;
389 finalize_resolve(was_success, recursion_count);
393 void DnsResolver::handle_ips(const int recursion_count,
394 const std::vector<host_addr_pair> &result_ips)
396 // received at least one IP which could be for the queried host name
397 // or the cname at the "end" of the cname list;
398 // but all IPs should be for the same
399 HostAddressVec addr_list;
400 std::string only_host_for_ips = result_ips[0].first;
401 BOOST_FOREACH( const host_addr_pair &host_and_addr, result_ips)
403 if ( host_and_addr.first != only_host_for_ips )
404 GlobalLogger.info() << LogPrefix
405 << "Received IPs for different hosts " << only_host_for_ips
406 << " and " << host_and_addr.first << " in one DNS result! "
407 << "--> ignore second";
410 GlobalLogger.info() << LogPrefix << "Found IP "
411 << host_and_addr.second.get_ip() << " with TTL "
412 << host_and_addr.second.get_ttl().get_value() << "s";
413 addr_list.push_back(host_and_addr.second);
416 ResolverBase::update_cache( only_host_for_ips, addr_list );
419 bool was_success = true;
420 finalize_resolve(was_success, recursion_count);
424 void DnsResolver::handle_cname(const int recursion_count,
425 const std::vector<src_cname_pair> &result_cnames)
427 // find the "last" cname in the list
428 // Hostname --> cname1 --> cname2 --> ... --> cnameN
429 // We assume here that this list might not be in order but that all cnames
430 // form a single list (form one connected list and not several independent
433 std::string last_cname = "";
435 BOOST_REVERSE_FOREACH( const src_cname_pair &host_and_cname, result_cnames )
437 could_be_last = true;
438 BOOST_REVERSE_FOREACH( const src_cname_pair &other, result_cnames )
440 if (other.first == host_and_cname.second.Host)
441 { // found cname for current cname
442 could_be_last = false;
448 last_cname = host_and_cname.second.Host;
453 if (last_cname.empty())
455 GlobalLogger.info() << LogPrefix
456 << "Could not identify \"last\" CNAME to handle -- "
457 << "maybe we encountered a CNAME loop? Anyway, cannot proceed!";
458 GlobalLogger.info() << LogPrefix << "Result CNAMEs were:";
459 BOOST_FOREACH( const src_cname_pair &host_and_cname, result_cnames )
460 GlobalLogger.info() << LogPrefix << host_and_cname.first << " --> "
461 << host_and_cname.second.Host;
462 handle_unavailable(recursion_count);
465 { // check cache for IP for this cname
466 bool check_up_to_date = true;
467 HostAddressVec cached_data = get_cached_ips_recursively(last_cname,
469 if ( !cached_data.empty() )
471 bool was_success = true;
472 finalize_resolve(was_success, recursion_count+1);
475 { // get resolver for canonical name
476 ResolverItem resolver = DnsMaster::get_instance()
477 ->get_resolver_for(last_cname, Protocol);
478 callback_type callback = boost::bind(
479 &DnsResolver::cname_resolve_callback,
481 resolver->async_resolve( callback, recursion_count+1 );
483 // treat a CNAME as a partial result: not enough to run callbacks
484 // from finalize_resolve, but enough to stop timers and reset
485 // RetryCount --> name resolution can take longer
493 * the recursion_count here is really the one from the recursion, not the one
494 * forwarded from async_resolve!
496 void DnsResolver::cname_resolve_callback(const bool was_success,
497 const int recursion_count)
499 if ( OperationCancelled )
500 { // async_resolve was cancelled --> callbacks already called
501 GlobalLogger.info() << LogPrefix
502 << "Ignoring CNAME results since we were cancelled";
505 else if (was_success)
507 GlobalLogger.debug() << LogPrefix << "CNAME resolution succeeded after "
508 << recursion_count << " recursions";
509 finalize_resolve(was_success, recursion_count);
513 GlobalLogger.info() << LogPrefix << "CNAME resolution failed after "
514 << recursion_count << " recursions";
515 // no use to schedule retry in this case since cname resolver must have
516 // failed several times and we can only re-start the same procedure with
517 // the same information. But can re-try later
518 handle_unavailable(recursion_count);
524 * @brief always called at end of resolving process
526 * runs callbacks, resets timers and checks state consistency; only thing that
527 * is "left alive" is the long-term timer that might cause a re-start of
528 * resolution after a while
530 * @param was_success: indicates whether resolution was successfull
531 * @param recursion_count number of recursions or (if not successfull) negative
532 * value indicating who called this function
534 void DnsResolver::finalize_resolve(const bool was_success,
535 const int recursion_count)
537 // some consistency checks; failure might indicate a situation I had not
538 // anticipated during programming but might not be harmfull yet
540 GlobalLogger.warning() << LogPrefix << "Consistency check failed: "
541 << "not resolving any more!";
542 if ( OperationCancelled )
543 GlobalLogger.warning() << LogPrefix << "Consistency check failed: "
544 << " was cancelled!";
545 if ( ResolverBase::CallbackList.empty() )
546 GlobalLogger.warning() << LogPrefix << "Consistency check failed: "
548 if ( RequestId != 0 )
549 GlobalLogger.warning() << LogPrefix << "Consistency check failed: "
550 << "waiting for DNS reply!";
553 stop_trying(was_success);
555 // schedule callbacks, clearing callback list
556 ResolverBase::schedule_callbacks(was_success, recursion_count);
559 GlobalLogger.info() << LogPrefix << "finalized resolve"
560 << " with success = " << was_success
561 << " and recursion_count = " << recursion_count;
567 * arg was_success determines if stop trying forever or just for the moment
568 * --> determines if we cancel StaleDataLongtermTimer or not
570 void DnsResolver::stop_trying(bool was_success)
573 GlobalLogger.debug() << LogPrefix << "Cancelling timers";
574 ResolveTimeoutTimer.cancel();
575 PauseBeforeRetryTimer.cancel();
579 StaleDataLongtermTimer.cancel();
580 LongtermTimerIsActive = false;
589 * return true if resolver is currently resolving
591 * Is true from call to async_resolve until callbacks
592 * --> returns true if waiting for result or (short-term) retry
594 * However, does NOT tell you if the (long-term) stale timeout is active!
595 * That timer has no effect on result, need to check is_waiting_to_resolve
598 bool DnsResolver::is_resolving() const
605 * returns true if either is_resolving or the long-term timer is active
607 * is_resolving returns true if the short-term retry timer is active
609 bool DnsResolver::is_waiting_to_resolve() const
611 return IsResolving || LongtermTimerIsActive;
616 * cancel a earlier call to async_resolve
618 * callbacks will be called with was_success=false; all internal operations
619 * will be cancelled and internal callbacks (timers, dns results) have no
620 * effect any more; cancels also the long-term stale-data timer
622 void DnsResolver::cancel_resolve()
624 if ( !IsResolving && !LongtermTimerIsActive)
626 GlobalLogger.info() << LogPrefix << "Cancel called on non-resolving, "
627 << "non-waiting resolver -- ignore";
630 else if (OperationCancelled)
632 GlobalLogger.info() << LogPrefix
633 << "Cancel called on cancelled resolver -- ignore";
636 GlobalLogger.info() << LogPrefix << "Cancel resolver";
638 // set before finalize_resolve so can check in finalize_resolve that ID is
639 // always 0; ID is not used any more since handle_dns_result stops if
640 // OperationCancelled is true
645 bool was_success = false;
646 int recursion_count = -1;
647 finalize_resolve(was_success, recursion_count);
650 // also cancel the long-term timer
651 StaleDataLongtermTimer.cancel();
652 LongtermTimerIsActive = false;
654 // set after finalize_resolve, so can check in finalize_resolve that
655 // OperationCancelled is never true
656 OperationCancelled = true;
661 void DnsResolver::handle_resolve_timeout(const int recursion_count,
662 const boost::system::error_code &error)
664 if ( error == boost::asio::error::operation_aborted ) // cancelled
666 GlobalLogger.debug() << LogPrefix
667 << "Resolve timeout timer was cancelled!";
672 GlobalLogger.info() << LogPrefix
673 << "resolve timeout handler received error "
674 << error << " --> request retry";
675 schedule_retry(recursion_count);
677 else if ( OperationCancelled )
678 { // async_resolve was cancelled --> callbacks already called
679 GlobalLogger.info() << LogPrefix
680 << "Ignoring DNS timeout since we were cancelled";
685 GlobalLogger.info() << LogPrefix << "DNS resolving timed out";
686 schedule_retry(recursion_count);
691 void DnsResolver::schedule_retry(const int recursion_count)
694 ResolveTimeoutTimer.cancel();
695 PauseBeforeRetryTimer.cancel();
700 if ( RetryCount > DnsMaster::get_instance()
701 ->get_max_address_resolution_attempts() )
702 { // too many re-tries
703 GlobalLogger.info() << LogPrefix << "Not scheduling a retry since "
704 << "RetryCount " << RetryCount << " too high";
705 handle_unavailable(recursion_count); // will call stop_trying
706 } // --> reset RetryCount
709 GlobalLogger.info() << LogPrefix << "Scheduling a retry (RetryCount="
710 << RetryCount << ")";
711 PauseBeforeRetryTimer.expires_from_now(
712 seconds(Config::PauseBeforeRetrySeconds));
713 PauseBeforeRetryTimer.async_wait(
714 boost::bind( &DnsResolver::wait_timer_timeout_handler,
715 this, recursion_count,
716 boost::asio::placeholders::error) );
720 void DnsResolver::wait_timer_timeout_handler( const int recursion_count,
721 const boost::system::error_code &error)
723 if ( error == boost::asio::error::operation_aborted ) // cancelled
724 { // assume that our code cancelled this timer, so callbacks will be
726 GlobalLogger.debug() << LogPrefix
727 << "Resolve wait timer was cancelled! ";
730 { // not sure what to do here, but callers waiting forever for a callback
731 // is probably the worst thing to happen, so call finalize_resolve
732 GlobalLogger.info() << LogPrefix
733 << "resolve wait handler received error "
734 << error << "! Try to finalize resolve";
735 bool was_success = false;
736 finalize_resolve(was_success, recursion_count);
738 else if ( OperationCancelled )
739 { // async_resolve was cancelled --> callbacks already called
740 GlobalLogger.info() << LogPrefix
741 << "Ignoring waiting timeout since we were cancelled";
746 GlobalLogger.info() << LogPrefix << "Done waiting --> re-try resolve";
747 IsResolving = false; // will be set to true immediately in do_resolve
748 do_resolve(recursion_count);
753 //==============================================================================
755 //==============================================================================
757 HostAddress DnsResolver::get_next_ip(bool check_up_to_date)
760 // (do not use arg check_up_to_date here in order to give NextIpIndex
761 // a chance to stay above number of outdated IPs)
762 HostAddressVec cached_data = ResolverBase::get_cached_ips_recursively();
764 // if no results cached, return default-constructed HostAddress (0.0.0.0)
765 HostAddress return_candidate;
766 if ( cached_data.empty() )
768 GlobalLogger.debug() << LogPrefix << "Get next IP: nothing cached";
769 return return_candidate;
772 std::size_t n_iter = 0;
773 std::size_t n_ips = cached_data.size();
774 uint32_t ttl_thresh = static_cast<uint32_t>( DnsMaster::get_instance()
775 ->get_resolved_ip_ttl_threshold() );
777 GlobalLogger.info() << LogPrefix << "Get next IP from cached result of "
778 << n_ips << " IPs; first index to consider is " << NextIpIndex
779 << "; TTL thresh=" << ttl_thresh << "s is used: " << check_up_to_date;
781 // loop until we have found a cached result (that is up to date)
782 // or until we have tried all cached IPs
785 // check index since cache size may have changed since last call
786 if (NextIpIndex >= n_ips)
788 GlobalLogger.debug() << LogPrefix << "Reset NextIpIndex";
791 else if ( n_iter >= n_ips)
793 GlobalLogger.debug() << LogPrefix << "No IP found";
794 return HostAddress(); // have checked all candidates
797 { // there are candidates left to consider
798 GlobalLogger.debug() << LogPrefix << "Check IP candidate at index "
800 return_candidate = cached_data[NextIpIndex++];
801 if (!check_up_to_date)
803 GlobalLogger.debug() << LogPrefix << "not checking ttl, accept";
804 return return_candidate;
806 else if (return_candidate.get_ttl().get_updated_value()
809 GlobalLogger.debug() << LogPrefix << "is up to date, accept";
810 return return_candidate;
814 GlobalLogger.debug() << LogPrefix << "is out of date ("
815 << return_candidate.get_ttl().get_updated_value()
816 << "s <= " << ttl_thresh << "s), continue";
823 bool DnsResolver::have_up_to_date_ip()
825 return get_resolved_ip_count(true) > 0;
828 int DnsResolver::get_resolved_ip_count(const bool check_up_to_date)
830 // run with empty hostname --> uses internal var Hostname
831 return ResolverBase::get_cached_ips_recursively("",check_up_to_date).size();