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;
51 const std::size_t MAX_IPS_PER_HOST = 2;
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, protocol, cache )
60 , Socket( *io_serv, ip::udp::endpoint(ip::udp::v4(), 0))
61 // just connect to anything, will specify sender/receiver later
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.info() << 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.info() << 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.info() << LogPrefix
173 << "Sending of DNS request message failed: "
175 schedule_retry(recursion_count);
179 if ( bytes_sent == 0 )
181 GlobalLogger.info() << 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 );
217 { // system DNS or firewall might have longer timeout than me
218 // --> might receive replies for old requests whose time-out has expired
219 // --> have already called callbacks etc, so nothing to do with result
220 // TODO: did receive probably same data several times --> reset buffer?
221 GlobalLogger.info() << LogPrefix << "Ignoring reply to old DNS request "
222 << "(reply has ID " << std::showbase << std::hex
223 << result_message.id() << " or buffer not reset)";
226 else if (RequestId != result_message.id())
228 GlobalLogger.info() << LogPrefix << "Received answer for request ID "
229 << std::showbase << std::hex << result_message.id()
230 << " but expected ID " << RequestId << " --> ignore and retry";
231 schedule_retry(recursion_count);
235 GlobalLogger.debug() << LogPrefix << "Result has correct ID "
236 << std::showbase << std::hex << RequestId;
239 // loop over answers, remembering ips and cnames
240 // work with a regular pointer to list of answers since result_message is
241 // owner of data and that exists until end of function
242 // Items in answers list are shared_ptr to resource_base_t
243 std::vector<host_addr_pair> result_ips;
244 std::vector<src_cname_pair> result_cnames;
245 std::vector<string_pair> result_name_servers;
247 GlobalLogger.debug() << LogPrefix <<"Checking ANSWERS section of dns reply";
248 gather_results(result_message.answers(), &result_ips, &result_cnames,
249 &result_name_servers);
251 // remember cname list (if there were any)
252 // results should have the logical order
253 // Hostname [ --> cname1 --> cname2 --> ... --> cnameN ] [ --> ips ];
254 // otherwise just have unneccessary cnames in cache
255 BOOST_FOREACH( const src_cname_pair &host_and_cname, result_cnames )
256 ResolverBase::update_cache(host_and_cname.first, host_and_cname.second);
258 if ( !result_ips.empty() )
259 handle_ips( recursion_count, result_ips );
260 else if ( !result_cnames.empty() )
261 // no IPs but at least one cname --> find the "last" cname and
262 // re-start resolving with that
263 handle_cname(recursion_count, result_cnames);
265 { // no answers --> cannot proceed
266 GlobalLogger.info() << LogPrefix << "No IP nor CNAME received! "
267 << "--> request retry";
268 schedule_retry(recursion_count);
273 * gather IPs, CNAMEs and name servers from list of resource records;
275 * can be run on anwers(), autorities() and additional() sections of dns reply
278 * @param rr_list: input list of resource records
279 * @param result_ips: output vector of ips
280 * @param result_cnames: output vector of cnames
281 * @param result_name_servers: output vector of name servers
283 void DnsResolver::gather_results(const boost::net::dns::rr_list_t *rr_list,
284 std::vector<host_addr_pair> *result_ips,
285 std::vector<src_cname_pair> *result_cnames,
286 std::vector<string_pair> *result_name_servers)
289 using boost::net::dns::resource_base_t;
290 boost::posix_time::ptime now =boost::posix_time::second_clock::local_time();
291 BOOST_FOREACH( boost::shared_ptr<resource_base_t> rr_item, *rr_list )
293 boost::net::dns::type_t rr_type = rr_item->rtype();
294 uint32_t ttl = rr_item->ttl();
295 std::string domain = rr_item->domain();
297 boost::posix_time::to_simple_string(now + seconds(ttl));
299 if (rr_type == boost::net::dns::type_a)
300 { // 'A' resource records carry IPv4 addresses
301 if (Protocol == DNS_IPv6)
303 GlobalLogger.info() << LogPrefix << "Ignoring IPv4 address "
304 << "because resolver was configured to only use IPv6.";
307 boost::asio::ip::address_v4 ip =
308 ( dynamic_cast<boost::net::dns::a_resource *> (rr_item.get()) )
310 result_ips->push_back(host_addr_pair(domain, HostAddress(ip, ttl)));
311 GlobalLogger.debug() << LogPrefix << domain << ": IPv4 " << ip
312 << " with TTL " << ttl << "s (until "
315 else if (rr_type == boost::net::dns::type_a6)
316 { // 'AAAA' resource records carry IPv6 addresses
317 if (Protocol == DNS_IPv4)
319 GlobalLogger.info() << LogPrefix << "Ignoring IPv6 address "
320 << "because resolver was configured to only use IPv4.";
323 boost::asio::ip::address_v6 ip =
324 ( dynamic_cast<boost::net::dns::a6_resource *> (rr_item.get()) )
326 result_ips->push_back(host_addr_pair(domain, HostAddress(ip, ttl)));
327 GlobalLogger.debug() << LogPrefix << domain << ": IPv6 " << ip
328 << " with TTL " << ttl << "s (until "
331 else if (rr_type == boost::net::dns::type_cname)
332 { // 'CNAME' resource records that carry aliases
334 (dynamic_cast<boost::net::dns::cname_resource *>(rr_item.get()))
336 result_cnames->push_back( src_cname_pair(domain,
337 Cname(cname, ttl)) );
338 GlobalLogger.debug() << LogPrefix << domain << ": CNAME to "
339 << cname << " with TTL " << ttl << "s (until "
342 else if (rr_type == boost::net::dns::type_ns)
343 { // NS (name_server) resource records
344 std::string name_server =
345 (dynamic_cast<boost::net::dns::ns_resource *>(rr_item.get()))
347 result_name_servers->push_back( string_pair(domain, name_server) );
348 GlobalLogger.debug() << LogPrefix << "NameServer " << name_server
349 << " for " << domain << " with TTL " << ttl
350 << "s (until " << expiry << ")";
352 else if (rr_type == boost::net::dns::type_soa)
353 GlobalLogger.debug() << LogPrefix << "SOA resource";
354 else if (rr_type == boost::net::dns::type_ptr)
355 GlobalLogger.debug() << LogPrefix << "ptr resource";
356 else if (rr_type == boost::net::dns::type_hinfo)
357 GlobalLogger.debug() << LogPrefix << "hinfo resource";
358 else if (rr_type == boost::net::dns::type_mx)
359 GlobalLogger.debug() << LogPrefix << "mx resource";
360 else if (rr_type == boost::net::dns::type_txt)
361 GlobalLogger.debug() << LogPrefix << "txt resource";
362 else if (rr_type == boost::net::dns::type_srv)
363 GlobalLogger.debug() << LogPrefix << "srv resource";
364 else if (rr_type == boost::net::dns::type_axfr)
365 GlobalLogger.debug() << LogPrefix << "axfr resource";
367 GlobalLogger.debug() << LogPrefix << "unknown resource type: "
368 << std::showbase << std::hex
369 << static_cast<unsigned>(rr_item->rtype());
374 void DnsResolver::handle_unavailable(const int recursion_count)
376 // schedule new attempt in quite a while
377 StaleDataLongtermTimer.expires_from_now(
378 seconds(Config::StaleDataLongtermSeconds));
379 StaleDataLongtermTimer.async_wait(
380 boost::bind( &DnsResolver::wait_timer_timeout_handler, this,
382 boost::asio::placeholders::error
385 LongtermTimerIsActive = true;
387 // for now, admit failure
388 RequestId = 0; // do not accept answers from old requests
389 bool was_success = false;
390 finalize_resolve(was_success, recursion_count);
394 void DnsResolver::handle_ips(const int recursion_count,
395 const std::vector<host_addr_pair> &result_ips)
397 // received at least one IP which could be for the queried host name
398 // or the cname at the "end" of the cname list;
399 // but all IPs should be for the same
400 HostAddressVec addr_list;
401 std::string only_host_for_ips = result_ips[0].first;
402 BOOST_FOREACH( const host_addr_pair &host_and_addr, result_ips)
404 if ( host_and_addr.first != only_host_for_ips )
405 GlobalLogger.info() << LogPrefix
406 << "Received IPs for different hosts " << only_host_for_ips
407 << " and " << host_and_addr.first << " in one DNS result! "
408 << "--> ignore second";
411 GlobalLogger.info() << LogPrefix << "Found IP "
412 << host_and_addr.second.get_ip() << " with TTL "
413 << host_and_addr.second.get_ttl().get_value() << "s";
414 addr_list.push_back(host_and_addr.second);
418 // limit number of IPs to be saved
419 if (addr_list.size() > Config::MAX_IPS_PER_HOST)
421 GlobalLogger.info() << LogPrefix << "Limit list of IPs from "
422 << addr_list.size() << " to " << Config::MAX_IPS_PER_HOST;
423 addr_list.resize(Config::MAX_IPS_PER_HOST);
427 ResolverBase::update_cache( only_host_for_ips, addr_list );
430 bool was_success = true;
431 finalize_resolve(was_success, recursion_count);
435 void DnsResolver::handle_cname(const int recursion_count,
436 const std::vector<src_cname_pair> &result_cnames)
438 // find the "last" cname in the list
439 // Hostname --> cname1 --> cname2 --> ... --> cnameN
440 // We assume here that this list might not be in order but that all cnames
441 // form a single list (form one connected list and not several independent
444 std::string last_cname = "";
446 BOOST_REVERSE_FOREACH( const src_cname_pair &host_and_cname, result_cnames )
448 could_be_last = true;
449 BOOST_REVERSE_FOREACH( const src_cname_pair &other, result_cnames )
451 if (other.first == host_and_cname.second.Host)
452 { // found cname for current cname
453 could_be_last = false;
459 last_cname = host_and_cname.second.Host;
464 if (last_cname.empty())
466 GlobalLogger.info() << LogPrefix
467 << "Could not identify \"last\" CNAME to handle -- "
468 << "maybe we encountered a CNAME loop? Anyway, cannot proceed!";
469 GlobalLogger.info() << LogPrefix << "Result CNAMEs were:";
470 BOOST_FOREACH( const src_cname_pair &host_and_cname, result_cnames )
471 GlobalLogger.info() << LogPrefix << host_and_cname.first << " --> "
472 << host_and_cname.second.Host;
473 handle_unavailable(recursion_count);
476 { // check cache for IP for this cname
477 bool check_up_to_date = true;
478 HostAddressVec cached_data = get_cached_ips_recursively(last_cname,
480 if ( !cached_data.empty() )
482 bool was_success = true;
483 finalize_resolve(was_success, recursion_count+1);
486 { // get resolver for canonical name
487 ResolverItem resolver = DnsMaster::get_instance()
488 ->get_resolver_for(last_cname, Protocol);
489 callback_type callback = boost::bind(
490 &DnsResolver::cname_resolve_callback,
492 resolver->async_resolve( callback, recursion_count+1 );
494 // treat a CNAME as a partial result: not enough to run callbacks
495 // from finalize_resolve, but enough to stop timers and reset
496 // RetryCount --> name resolution can take longer
504 * the recursion_count here is really the one from the recursion, not the one
505 * forwarded from async_resolve!
507 void DnsResolver::cname_resolve_callback(const bool was_success,
508 const int recursion_count)
510 if ( OperationCancelled )
511 { // async_resolve was cancelled --> callbacks already called
512 GlobalLogger.info() << LogPrefix
513 << "Ignoring CNAME results since we were cancelled";
516 else if (was_success)
518 GlobalLogger.debug() << LogPrefix << "CNAME resolution succeeded after "
519 << recursion_count << " recursions";
520 finalize_resolve(was_success, recursion_count);
524 GlobalLogger.info() << LogPrefix << "CNAME resolution failed after "
525 << recursion_count << " recursions";
526 // no use to schedule retry in this case since cname resolver must have
527 // failed several times and we can only re-start the same procedure with
528 // the same information. But can re-try later
529 handle_unavailable(recursion_count);
535 * @brief always called at end of resolving process
537 * runs callbacks, resets timers and checks state consistency; only thing that
538 * is "left alive" is the long-term timer that might cause a re-start of
539 * resolution after a while
541 * @param was_success: indicates whether resolution was successfull
542 * @param recursion_count number of recursions or (if not successfull) negative
543 * value indicating who called this function
545 void DnsResolver::finalize_resolve(const bool was_success,
546 const int recursion_count)
548 // some consistency checks; failure might indicate a situation I had not
549 // anticipated during programming but might not be harmfull yet
551 GlobalLogger.warning() << LogPrefix << "Consistency check failed: "
552 << "not resolving any more!";
553 if ( OperationCancelled )
554 GlobalLogger.warning() << LogPrefix << "Consistency check failed: "
555 << " was cancelled!";
556 if ( ResolverBase::CallbackList.empty() )
557 GlobalLogger.warning() << LogPrefix << "Consistency check failed: "
559 if ( RequestId != 0 )
560 GlobalLogger.warning() << LogPrefix << "Consistency check failed: "
561 << "waiting for DNS reply!";
564 stop_trying(was_success);
566 // schedule callbacks, clearing callback list
567 ResolverBase::schedule_callbacks(was_success, recursion_count);
570 GlobalLogger.info() << LogPrefix << "finalized resolve"
571 << " with success = " << was_success
572 << " and recursion_count = " << recursion_count;
578 * arg was_success determines if stop trying forever or just for the moment
579 * --> determines if we cancel StaleDataLongtermTimer or not
581 void DnsResolver::stop_trying(bool was_success)
584 GlobalLogger.debug() << LogPrefix << "Cancelling timers";
585 ResolveTimeoutTimer.cancel();
586 PauseBeforeRetryTimer.cancel();
590 StaleDataLongtermTimer.cancel();
591 LongtermTimerIsActive = false;
600 * return true if resolver is currently resolving
602 * Is true from call to async_resolve until callbacks
603 * --> returns true if waiting for result or (short-term) retry
605 * However, does NOT tell you if the (long-term) stale timeout is active!
606 * That timer has no effect on result, need to check is_waiting_to_resolve
609 bool DnsResolver::is_resolving() const
616 * returns true if either is_resolving or the long-term timer is active
618 * is_resolving returns true if the short-term retry timer is active
620 bool DnsResolver::is_waiting_to_resolve() const
622 return IsResolving || LongtermTimerIsActive;
627 * cancel a earlier call to async_resolve
629 * callbacks will be called with was_success=false; all internal operations
630 * will be cancelled and internal callbacks (timers, dns results) have no
631 * effect any more; cancels also the long-term stale-data timer
633 void DnsResolver::cancel_resolve()
635 if ( !IsResolving && !LongtermTimerIsActive)
637 GlobalLogger.info() << LogPrefix << "Cancel called on non-resolving, "
638 << "non-waiting resolver -- ignore";
641 else if (OperationCancelled)
643 GlobalLogger.info() << LogPrefix
644 << "Cancel called on cancelled resolver -- ignore";
647 GlobalLogger.info() << LogPrefix << "Cancel resolver";
649 // set before finalize_resolve so can check in finalize_resolve that ID is
650 // always 0; ID is not used any more since handle_dns_result stops if
651 // OperationCancelled is true
656 bool was_success = false;
657 int recursion_count = -1;
658 finalize_resolve(was_success, recursion_count);
661 // also cancel the long-term timer
662 StaleDataLongtermTimer.cancel();
663 LongtermTimerIsActive = false;
665 // set after finalize_resolve, so can check in finalize_resolve that
666 // OperationCancelled is never true
667 OperationCancelled = true;
672 void DnsResolver::handle_resolve_timeout(const int recursion_count,
673 const boost::system::error_code &error)
675 if ( error == boost::asio::error::operation_aborted ) // cancelled
677 GlobalLogger.debug() << LogPrefix
678 << "Resolve timeout timer was cancelled!";
683 GlobalLogger.info() << LogPrefix
684 << "resolve timeout handler received error "
685 << error << " --> request retry";
686 schedule_retry(recursion_count);
688 else if ( OperationCancelled )
689 { // async_resolve was cancelled --> callbacks already called
690 GlobalLogger.info() << LogPrefix
691 << "Ignoring DNS timeout since we were cancelled";
696 GlobalLogger.info() << LogPrefix << "DNS resolving timed out";
697 schedule_retry(recursion_count);
702 void DnsResolver::schedule_retry(const int recursion_count)
705 ResolveTimeoutTimer.cancel();
706 PauseBeforeRetryTimer.cancel();
711 if ( RetryCount > DnsMaster::get_instance()
712 ->get_max_address_resolution_attempts() )
713 { // too many re-tries
714 GlobalLogger.info() << LogPrefix << "Not scheduling a retry since "
715 << "RetryCount " << RetryCount << " too high";
716 handle_unavailable(recursion_count); // will call stop_trying
717 } // --> reset RetryCount
720 GlobalLogger.info() << LogPrefix << "Scheduling a retry (RetryCount="
721 << RetryCount << ")";
722 PauseBeforeRetryTimer.expires_from_now(
723 seconds(Config::PauseBeforeRetrySeconds));
724 PauseBeforeRetryTimer.async_wait(
725 boost::bind( &DnsResolver::wait_timer_timeout_handler,
726 this, recursion_count,
727 boost::asio::placeholders::error) );
731 void DnsResolver::wait_timer_timeout_handler( const int recursion_count,
732 const boost::system::error_code &error)
734 if ( error == boost::asio::error::operation_aborted ) // cancelled
735 { // assume that our code cancelled this timer, so callbacks will be
737 GlobalLogger.debug() << LogPrefix
738 << "Resolve wait timer was cancelled! ";
741 { // not sure what to do here, but callers waiting forever for a callback
742 // is probably the worst thing to happen, so call finalize_resolve
743 GlobalLogger.info() << LogPrefix
744 << "resolve wait handler received error "
745 << error << "! Try to finalize resolve";
746 bool was_success = false;
747 finalize_resolve(was_success, recursion_count);
749 else if ( OperationCancelled )
750 { // async_resolve was cancelled --> callbacks already called
751 GlobalLogger.info() << LogPrefix
752 << "Ignoring waiting timeout since we were cancelled";
757 GlobalLogger.info() << LogPrefix << "Done waiting --> re-try resolve";
758 IsResolving = false; // will be set to true immediately in do_resolve
759 do_resolve(recursion_count);
764 //==============================================================================
766 //==============================================================================
768 HostAddress DnsResolver::get_next_ip(bool check_up_to_date)
771 // (do not use arg check_up_to_date here in order to give NextIpIndex
772 // a chance to stay above number of outdated IPs)
773 HostAddressVec cached_data = ResolverBase::get_cached_ips_recursively();
775 // if no results cached, return default-constructed HostAddress (0.0.0.0)
776 HostAddress return_candidate;
777 if ( cached_data.empty() )
779 GlobalLogger.debug() << LogPrefix << "Get next IP: nothing cached";
780 return return_candidate;
783 std::size_t n_iter = 0;
784 std::size_t n_ips = cached_data.size();
785 uint32_t ttl_thresh = static_cast<uint32_t>( DnsMaster::get_instance()
786 ->get_resolved_ip_ttl_threshold() );
788 GlobalLogger.info() << LogPrefix << "Get next IP from cached result of "
789 << n_ips << " IPs; first index to consider is " << NextIpIndex
790 << "; TTL thresh=" << ttl_thresh << "s is used: " << check_up_to_date;
792 // loop until we have found a cached result (that is up to date)
793 // or until we have tried all cached IPs
796 // check index since cache size may have changed since last call
797 if (NextIpIndex >= n_ips)
799 GlobalLogger.debug() << LogPrefix << "Reset NextIpIndex";
802 else if ( n_iter >= n_ips)
804 GlobalLogger.debug() << LogPrefix << "No IP found";
805 return HostAddress(); // have checked all candidates
808 { // there are candidates left to consider
809 GlobalLogger.debug() << LogPrefix << "Check IP candidate at index "
811 return_candidate = cached_data[NextIpIndex++];
812 if (!check_up_to_date)
814 GlobalLogger.debug() << LogPrefix << "not checking ttl, accept";
815 return return_candidate;
817 else if (return_candidate.get_ttl().get_updated_value()
820 GlobalLogger.debug() << LogPrefix << "is up to date, accept";
821 return return_candidate;
825 GlobalLogger.debug() << LogPrefix << "is out of date ("
826 << return_candidate.get_ttl().get_updated_value()
827 << "s <= " << ttl_thresh << "s), continue";
834 bool DnsResolver::have_up_to_date_ip()
836 return get_resolved_ip_count(true) > 0;
839 int DnsResolver::get_resolved_ip_count(const bool check_up_to_date)
841 // run with empty hostname --> uses internal var Hostname
842 return ResolverBase::get_cached_ips_recursively("",check_up_to_date).size();