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, cache )
59 , Socket( *io_serv, ip::udp::endpoint(ip::udp::v4(), 0))
60 // just connect to anything, will specify sender/receiver later
63 , Protocol( protocol )
64 , NameServer( name_server, Config::DNS_PORT )
65 , ResolveTimeoutTimer( *io_serv )
66 , PauseBeforeRetryTimer( *io_serv )
67 , StaleDataLongtermTimer( *io_serv )
70 , IsResolving( false )
71 , LogPrefix( "DnsResolver" )
74 , OperationCancelled( false )
75 , LongtermTimerIsActive( false )
77 std::stringstream temp;
78 temp << "Dns(" << ResolverBase::Hostname << "): ";
79 LogPrefix = temp.str();
83 DnsResolver::~DnsResolver()
85 boost::system::error_code error;
86 //Socket.shutdown(boost::asio::ip::udp::socket::shutdown_both, error);
88 // GlobalLogger.warning() << LogPrefix << "Received error " << error
89 // << " when shutting down socket for DNS";
90 // in IcmpPinger always gave an error system:9 (EBADF: Bad file descriptor)
91 // Here gives error system:107 ENOTCONN: Transport endpoint is not connected
95 GlobalLogger.warning() << LogPrefix << "Received error " << error
96 << " when closing socket for DNS";
101 //==============================================================================
103 //==============================================================================
106 * copied here code from boost::net::dns::resolve.hpp, since want async
107 * operation and that is used only internally, there
109 void DnsResolver::do_resolve(const int recursion_count)
111 // check if resolving already
114 GlobalLogger.info() << LogPrefix
115 << "Call to do_resolve ignored since resolving already";
119 OperationCancelled = false;
121 GlobalLogger.info() << LogPrefix << "start resolving for IPs of type "
122 << to_string(Protocol) << " using name server " << NameServer;
124 // just to be sure: cancel timers
125 ResolveTimeoutTimer.cancel();
126 PauseBeforeRetryTimer.cancel();
127 StaleDataLongtermTimer.cancel();
128 LongtermTimerIsActive = false;
130 // create DNS request
131 boost::net::dns::message dns_message( ResolverBase::Hostname, Protocol );
132 dns_message.recursive(true);
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,
149 boost::asio::placeholders::error,
150 boost::asio::placeholders::bytes_transferred)
154 (void) ResolveTimeoutTimer.expires_from_now(
155 seconds(Config::ResolveTimeoutSeconds));
156 ResolveTimeoutTimer.async_wait( boost::bind(
157 &DnsResolver::handle_resolve_timeout,
158 this, recursion_count,
159 boost::asio::placeholders::error) );
162 dns_message.encode(RequestBuffer);
166 bytes_sent = Socket.send_to(
167 boost::asio::buffer(RequestBuffer.get_array()),
170 catch (boost::system::system_error &err)
172 GlobalLogger.warning() << LogPrefix
173 << "Sending of DNS request message failed: "
175 schedule_retry(recursion_count);
179 if ( bytes_sent == 0 )
181 GlobalLogger.warning() << LogPrefix << "Empty DNS request sent!";
182 schedule_retry(recursion_count);
188 void DnsResolver::handle_dns_result(const int recursion_count,
189 const boost::system::error_code &error,
190 const std::size_t bytes_transferred)
194 GlobalLogger.info() << LogPrefix << "DNS resolve resulted in error "
195 << error << " --> request retry";
196 schedule_retry(recursion_count);
199 else if ( OperationCancelled )
200 { // async_resolve was cancelled --> callbacks already called
201 GlobalLogger.info() << LogPrefix
202 << "Ignoring DNS results since we were cancelled";
206 GlobalLogger.debug() << LogPrefix << "Handling DNS result ("
207 << bytes_transferred << " bytes transferred)";
209 // next 3(+1) lines copied from boost/net/dns/resolver.hpp:
210 // clamp the recvBuffer with the number of bytes transferred or decode buffr
211 ReceiveBuffer.length(bytes_transferred);
212 boost::net::dns::message result_message;
213 result_message.decode( ReceiveBuffer );
216 if (RequestId != result_message.id())
217 GlobalLogger.warning() << LogPrefix << "Received answer for request ID "
218 << std::showbase << std::hex << result_message.id()
219 << " but expected ID " << RequestId;
221 GlobalLogger.debug() << LogPrefix << "Result has correct ID "
222 << std::showbase << std::hex << RequestId;
225 // loop over answers, remembering ips and cnames
226 // work with a regular pointer to list of answers since result_message is
227 // owner of data and that exists until end of function
228 // Items in answers list are shared_ptr to resource_base_t
229 std::vector<host_addr_pair> result_ips;
230 std::vector<src_cname_pair> result_cnames;
231 std::vector<string_pair> result_name_servers;
233 GlobalLogger.debug() << LogPrefix <<"Checking ANSWERS section of dns reply";
234 gather_results(result_message.answers(), &result_ips, &result_cnames,
235 &result_name_servers);
237 // remember cname list (if there were any)
238 // results should have the logical order
239 // Hostname [ --> cname1 --> cname2 --> ... --> cnameN ] [ --> ips ];
240 // otherwise just have unneccessary cnames in cache
241 BOOST_FOREACH( const src_cname_pair &host_and_cname, result_cnames )
242 ResolverBase::update_cache(host_and_cname.first, host_and_cname.second);
244 if ( !result_ips.empty() )
245 handle_ips( recursion_count, 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(recursion_count, result_cnames);
251 { // no answers --> cannot proceed
252 GlobalLogger.warning() << LogPrefix << "No IP nor CNAME received! "
253 << "--> request retry";
254 schedule_retry(recursion_count);
259 * gather IPs, CNAMEs and name servers from list of resource records;
261 * can be run on anwers(), autorities() and additional() sections of dns reply
264 * @param rr_list: input list of resource records
265 * @param result_ips: output vector of ips
266 * @param result_cnames: output vector of cnames
267 * @param result_name_servers: output vector of name servers
269 void DnsResolver::gather_results(const boost::net::dns::rr_list_t *rr_list,
270 std::vector<host_addr_pair> *result_ips,
271 std::vector<src_cname_pair> *result_cnames,
272 std::vector<string_pair> *result_name_servers)
275 using boost::net::dns::resource_base_t;
276 boost::posix_time::ptime now =boost::posix_time::second_clock::local_time();
277 BOOST_FOREACH( boost::shared_ptr<resource_base_t> rr_item, *rr_list )
279 boost::net::dns::type_t rr_type = rr_item->rtype();
280 uint32_t ttl = rr_item->ttl();
281 std::string domain = rr_item->domain();
283 boost::posix_time::to_simple_string(now + seconds(ttl));
285 if (rr_type == boost::net::dns::type_a)
286 { // 'A' resource records carry IPv4 addresses
287 if (Protocol == DNS_IPv6)
289 GlobalLogger.info() << LogPrefix << "Ignoring IPv4 address "
290 << "because resolver was configured to only use IPv6.";
293 boost::asio::ip::address_v4 ip =
294 ( dynamic_cast<boost::net::dns::a_resource *> (rr_item.get()) )
296 result_ips->push_back(host_addr_pair(domain, HostAddress(ip, ttl)));
297 GlobalLogger.debug() << LogPrefix << domain << ": IPv4 " << ip
298 << " with TTL " << ttl << "s (until "
301 else if (rr_type == boost::net::dns::type_a6)
302 { // 'AAAA' resource records carry IPv6 addresses
303 if (Protocol == DNS_IPv4)
305 GlobalLogger.info() << LogPrefix << "Ignoring IPv6 address "
306 << "because resolver was configured to only use IPv4.";
309 boost::asio::ip::address_v6 ip =
310 ( dynamic_cast<boost::net::dns::a6_resource *> (rr_item.get()) )
312 result_ips->push_back(host_addr_pair(domain, HostAddress(ip, ttl)));
313 GlobalLogger.debug() << LogPrefix << domain << ": IPv6 " << ip
314 << " with TTL " << ttl << "s (until "
317 else if (rr_type == boost::net::dns::type_cname)
318 { // 'CNAME' resource records that carry aliases
320 (dynamic_cast<boost::net::dns::cname_resource *>(rr_item.get()))
322 result_cnames->push_back( src_cname_pair(domain,
323 Cname(cname, ttl)) );
324 GlobalLogger.debug() << LogPrefix << domain << ": CNAME to "
325 << cname << " with TTL " << ttl << "s (until "
328 else if (rr_type == boost::net::dns::type_ns)
329 { // NS (name_server) resource records
330 std::string name_server =
331 (dynamic_cast<boost::net::dns::ns_resource *>(rr_item.get()))
333 result_name_servers->push_back( string_pair(domain, name_server) );
334 GlobalLogger.debug() << LogPrefix << "NameServer " << name_server
335 << " for " << domain << " with TTL " << ttl
336 << "s (until " << expiry << ")";
338 else if (rr_type == boost::net::dns::type_soa)
339 GlobalLogger.debug() << LogPrefix << "SOA resource";
340 else if (rr_type == boost::net::dns::type_ptr)
341 GlobalLogger.debug() << LogPrefix << "ptr resource";
342 else if (rr_type == boost::net::dns::type_hinfo)
343 GlobalLogger.debug() << LogPrefix << "hinfo resource";
344 else if (rr_type == boost::net::dns::type_mx)
345 GlobalLogger.debug() << LogPrefix << "mx resource";
346 else if (rr_type == boost::net::dns::type_txt)
347 GlobalLogger.debug() << LogPrefix << "txt resource";
348 else if (rr_type == boost::net::dns::type_srv)
349 GlobalLogger.debug() << LogPrefix << "srv resource";
350 else if (rr_type == boost::net::dns::type_axfr)
351 GlobalLogger.debug() << LogPrefix << "axfr resource";
353 GlobalLogger.debug() << LogPrefix << "unknown resource type: "
354 << std::showbase << std::hex
355 << static_cast<unsigned>(rr_item->rtype());
360 void DnsResolver::handle_unavailable(const int recursion_count)
362 // schedule new attempt in quite a while
363 StaleDataLongtermTimer.expires_from_now(
364 seconds(Config::StaleDataLongtermSeconds));
365 StaleDataLongtermTimer.async_wait(
366 boost::bind( &DnsResolver::wait_timer_timeout_handler, this,
368 boost::asio::placeholders::error
371 LongtermTimerIsActive = true;
373 // for now, admit failure
374 bool was_success = false;
375 finalize_resolve(was_success, recursion_count);
379 void DnsResolver::handle_ips(const int recursion_count,
380 const std::vector<host_addr_pair> &result_ips)
382 // received at least one IP which could be for the queried host name
383 // or the cname at the "end" of the cname list;
384 // but all IPs should be for the same
385 HostAddressVec addr_list;
386 std::string only_host_for_ips = result_ips[0].first;
387 BOOST_FOREACH( const host_addr_pair &host_and_addr, result_ips)
389 if ( host_and_addr.first != only_host_for_ips )
390 GlobalLogger.warning() << LogPrefix
391 << "Received IPs for different hosts " << only_host_for_ips
392 << " and " << host_and_addr.first << " in one DNS result! "
393 << "--> ignore second";
396 GlobalLogger.notice() << LogPrefix << "Found IP "
397 << host_and_addr.second.get_ip() << " with TTL "
398 << host_and_addr.second.get_ttl().get_value() << "s";
399 addr_list.push_back(host_and_addr.second);
402 ResolverBase::update_cache( only_host_for_ips, addr_list );
405 bool was_success = true;
406 finalize_resolve(was_success, recursion_count);
410 void DnsResolver::handle_cname(const int recursion_count,
411 const std::vector<src_cname_pair> &result_cnames)
413 // find the "last" cname in the list
414 // Hostname --> cname1 --> cname2 --> ... --> cnameN
415 // We assume here that this list might not be in order but that all cnames
416 // form a single list (form one connected list and not several independent
419 std::string last_cname = "";
421 BOOST_REVERSE_FOREACH( const src_cname_pair &host_and_cname, result_cnames )
423 could_be_last = true;
424 BOOST_REVERSE_FOREACH( const src_cname_pair &other, result_cnames )
426 if (other.first == host_and_cname.second.Host)
427 { // found cname for current cname
428 could_be_last = false;
434 last_cname = host_and_cname.second.Host;
439 if (last_cname.empty())
441 GlobalLogger.error() << LogPrefix
442 << "Could not identify \"last\" CNAME to handle -- "
443 << "maybe we encountered a CNAME loop? Anyway, cannot proceed!";
444 GlobalLogger.info() << LogPrefix << "Result CNAMEs were:";
445 BOOST_FOREACH( const src_cname_pair &host_and_cname, result_cnames )
446 GlobalLogger.info() << LogPrefix << host_and_cname.first << " --> "
447 << host_and_cname.second.Host;
448 handle_unavailable(recursion_count);
451 { // check cache for IP for this cname
452 bool check_up_to_date = true;
453 HostAddressVec cached_data = Cache->get_ips_recursive(last_cname,
455 if ( !cached_data.empty() )
457 bool was_success = true;
458 finalize_resolve(was_success, recursion_count+1);
461 { // get resolver for canonical name
462 ResolverItem resolver = DnsMaster::get_instance()
463 ->get_resolver_for(last_cname, Protocol);
464 callback_type callback = boost::bind(
465 &DnsResolver::cname_resolve_callback,
467 resolver->async_resolve( callback, recursion_count+1 );
469 // treat a CNAME as a partial result: not enough to run callbacks
470 // from finalize_resolve, but enough to stop timers and reset
471 // RetryCount --> name resolution can take longer
479 * the recursion_count here is really the one from the recursion, not the one
480 * forwarded from async_resolve!
482 void DnsResolver::cname_resolve_callback(const bool was_success,
483 const int recursion_count)
485 if ( OperationCancelled )
486 { // async_resolve was cancelled --> callbacks already called
487 GlobalLogger.info() << LogPrefix
488 << "Ignoring CNAME results since we were cancelled";
491 else if (was_success)
493 GlobalLogger.debug() << LogPrefix << "CNAME resolution succeeded after "
494 << recursion_count << " recursions";
495 finalize_resolve(was_success, recursion_count);
499 GlobalLogger.info() << LogPrefix << "CNAME resolution failed after "
500 << recursion_count << " recursions";
501 // no use to schedule retry in this case since cname resolver must have
502 // failed several times and we can only re-start the same procedure with
503 // the same information. But can re-try later
504 handle_unavailable(recursion_count);
510 * @brief always called at end of resolving process
512 * runs callbacks, resets timers and checks state consistency; only thing that
513 * is "left alive" is the long-term timer that might cause a re-start of
514 * resolution after a while
516 * @param was_success: indicates whether resolution was successfull
517 * @param recursion_count number of recursions or (if not successfull) negative
518 * value indicating who called this function
520 void DnsResolver::finalize_resolve(const bool was_success,
521 const int recursion_count)
523 // some consistency checks; failure might indicate a situation I had not
524 // anticipated during programming but might not be harmfull yet
526 GlobalLogger.warning() << LogPrefix << "Consistency check failed: "
527 << "not resolving any more!";
528 if ( OperationCancelled )
529 GlobalLogger.warning() << LogPrefix << "Consistency check failed: "
530 << " was cancelled!";
531 if ( ResolverBase::CallbackList.empty() )
532 GlobalLogger.warning() << LogPrefix << "Consistency check failed: "
534 if ( RequestId != 0 )
535 GlobalLogger.warning() << LogPrefix << "Consistency check failed: "
536 << "waiting for DNS reply!";
539 stop_trying(was_success);
541 // schedule callbacks, clearing callback list
542 ResolverBase::schedule_callbacks(was_success, recursion_count);
545 GlobalLogger.notice() << LogPrefix << "finalized resolve"
546 << " with success = " << was_success
547 << " and recursion_count = " << recursion_count;
553 * arg was_success determines if stop trying forever or just for the moment
554 * --> determines if we cancel StaleDataLongtermTimer or not
556 void DnsResolver::stop_trying(bool was_success)
559 GlobalLogger.debug() << LogPrefix << "Cancelling timers";
560 ResolveTimeoutTimer.cancel();
561 PauseBeforeRetryTimer.cancel();
565 StaleDataLongtermTimer.cancel();
566 LongtermTimerIsActive = false;
575 * return true if resolver is currently resolving
577 * Is true from call to async_resolve until callbacks
578 * --> returns true if waiting for result or (short-term) retry
580 * However, does NOT tell you if the (long-term) stale timeout is active!
581 * That timer has no effect on result, need to check is_waiting_to_resolve
584 bool DnsResolver::is_resolving() const
591 * returns true if either is_resolving or the long-term timer is active
593 * is_resolving returns true if the short-term retry timer is active
595 bool DnsResolver::is_waiting_to_resolve() const
597 return IsResolving || LongtermTimerIsActive;
602 * cancel a earlier call to async_resolve
604 * callbacks will be called with was_success=false; all internal operations
605 * will be cancelled and internal callbacks (timers, dns results) have no
606 * effect any more; cancels also the long-term stale-data timer
608 void DnsResolver::cancel_resolve()
610 if ( !IsResolving && !LongtermTimerIsActive)
612 GlobalLogger.info() << LogPrefix << "Cancel called on non-resolving, "
613 << "non-waiting resolver -- ignore";
616 else if (OperationCancelled)
618 GlobalLogger.info() << LogPrefix
619 << "Cancel called on cancelled resolver -- ignore";
622 GlobalLogger.info() << LogPrefix << "Cancel resolver";
624 // set before finalize_resolve so can check in finalize_resolve that ID is
625 // always 0; ID is not used any more since handle_dns_result stops if
626 // OperationCancelled is true
631 bool was_success = false;
632 int recursion_count = -1;
633 finalize_resolve(was_success, recursion_count);
636 // also cancel the long-term timer
637 StaleDataLongtermTimer.cancel();
638 LongtermTimerIsActive = false;
640 // set after finalize_resolve, so can check in finalize_resolve that
641 // OperationCancelled is never true
642 OperationCancelled = true;
647 void DnsResolver::handle_resolve_timeout(const int recursion_count,
648 const boost::system::error_code &error)
650 if ( error == boost::asio::error::operation_aborted ) // cancelled
652 GlobalLogger.debug() << LogPrefix
653 << "Resolve timeout timer was cancelled!";
658 GlobalLogger.warning() << LogPrefix
659 << "resolve timeout handler received error "
660 << error << " --> request retry";
661 schedule_retry(recursion_count);
663 else if ( OperationCancelled )
664 { // async_resolve was cancelled --> callbacks already called
665 GlobalLogger.info() << LogPrefix
666 << "Ignoring DNS timeout since we were cancelled";
671 GlobalLogger.notice() << LogPrefix << "DNS resolving timed out";
672 schedule_retry(recursion_count);
677 void DnsResolver::schedule_retry(const int recursion_count)
680 ResolveTimeoutTimer.cancel();
681 PauseBeforeRetryTimer.cancel();
686 if ( RetryCount > DnsMaster::get_instance()
687 ->get_max_address_resolution_attempts() )
688 { // too many re-tries
689 GlobalLogger.info() << LogPrefix << "Not scheduling a retry since "
690 << "RetryCount " << RetryCount << " too high";
691 handle_unavailable(recursion_count); // will call stop_trying
692 } // --> reset RetryCount
695 GlobalLogger.info() << LogPrefix << "Scheduling a retry (RetryCount="
696 << RetryCount << ")";
697 PauseBeforeRetryTimer.expires_from_now(
698 seconds(Config::PauseBeforeRetrySeconds));
699 PauseBeforeRetryTimer.async_wait(
700 boost::bind( &DnsResolver::wait_timer_timeout_handler,
701 this, recursion_count,
702 boost::asio::placeholders::error) );
706 void DnsResolver::wait_timer_timeout_handler( const int recursion_count,
707 const boost::system::error_code &error)
709 if ( error == boost::asio::error::operation_aborted ) // cancelled
710 { // assume that our code cancelled this timer, so callbacks will be
712 GlobalLogger.debug() << LogPrefix
713 << "Resolve wait timer was cancelled! ";
716 { // not sure what to do here, but callers waiting forever for a callback
717 // is probably the worst thing to happen, so call finalize_resolve
718 GlobalLogger.warning() << LogPrefix
719 << "resolve wait handler received error "
720 << error << "! Try to finalize resolve";
721 bool was_success = false;
722 finalize_resolve(was_success, recursion_count);
724 else if ( OperationCancelled )
725 { // async_resolve was cancelled --> callbacks already called
726 GlobalLogger.info() << LogPrefix
727 << "Ignoring waiting timeout since we were cancelled";
732 GlobalLogger.info() << LogPrefix << "Done waiting --> re-try resolve";
733 IsResolving = false; // will be set to true immediately in do_resolve
734 do_resolve(recursion_count);
739 //==============================================================================
741 //==============================================================================
743 HostAddress DnsResolver::get_next_ip(bool check_up_to_date)
746 // (do not use arg check_up_to_date here in order to give NextIpIndex
747 // a chance to stay above number of outdated IPs)
748 HostAddressVec cached_data = ResolverBase::get_cached_ips_recursively();
750 // if no results cached, return default-constructed HostAddress (0.0.0.0)
751 HostAddress return_candidate;
752 if ( cached_data.empty() )
754 GlobalLogger.debug() << LogPrefix << "Get next IP: nothing cached";
755 return return_candidate;
758 std::size_t n_iter = 0;
759 std::size_t n_ips = cached_data.size();
760 uint32_t ttl_thresh = static_cast<uint32_t>( DnsMaster::get_instance()
761 ->get_resolved_ip_ttl_threshold() );
763 GlobalLogger.info() << LogPrefix << "Get next IP from cached result of "
764 << n_ips << " IPs; first index to consider is " << NextIpIndex
765 << "; TTL thresh=" << ttl_thresh << "s is used: " << check_up_to_date;
767 // loop until we have found a cached result (that is up to date)
768 // or until we have tried all cached IPs
771 // check index since cache size may have changed since last call
772 if (NextIpIndex >= n_ips)
774 GlobalLogger.debug() << LogPrefix << "Reset NextIpIndex";
777 else if ( n_iter >= n_ips)
779 GlobalLogger.debug() << LogPrefix << "No IP found";
780 return HostAddress(); // have checked all candidates
783 { // there are candidates left to consider
784 GlobalLogger.debug() << LogPrefix << "Check IP candidate at index "
786 return_candidate = cached_data[NextIpIndex++];
787 if (!check_up_to_date)
789 GlobalLogger.debug() << LogPrefix << "not checking ttl, accept";
790 return return_candidate;
792 else if (return_candidate.get_ttl().get_updated_value()
795 GlobalLogger.debug() << LogPrefix << "is up to date, accept";
796 return return_candidate;
800 GlobalLogger.debug() << LogPrefix << "is out of date ("
801 << return_candidate.get_ttl().get_updated_value()
802 << "s <= " << ttl_thresh << "s), continue";
809 bool DnsResolver::have_up_to_date_ip()
811 return get_resolved_ip_count(true) > 0;
814 int DnsResolver::get_resolved_ip_count(const bool check_up_to_date)
816 // run with empty hostname --> uses internal var Hostname
817 return ResolverBase::get_cached_ips_recursively("",check_up_to_date).size();