added variable for threshold for switching from "all congested" --> "connection failed"
[pingcheck] / src / host / pingscheduler.cpp
CommitLineData
91fcc471
TJ
1/*
2The software in this package is distributed under the GNU General
3Public License version 2 (with a special exception described below).
4
5A copy of GNU General Public License (GPL) is included in this distribution,
6in the file COPYING.GPL.
7
8As a special exception, if other files instantiate templates or use macros
9or inline functions from this file, or you compile this file and link it
10with other works to produce a work based on this file, this file
11does not by itself cause the resulting work to be covered
12by the GNU General Public License.
13
14However the source code for this file must still be made available
15in accordance with section (3) of the GNU General Public License.
16
17This exception does not invalidate any other reasons why a work based
18on this file might be covered by the GNU General Public License.
19*/
8f66f529 20#include "host/pingscheduler.h"
9c55ecd3 21
0d46491b 22#include <iostream>
1309d0e4 23#include <limits>
0d46491b 24
9c55ecd3 25#include <boost/bind.hpp>
26b0f687 26#include <boost/foreach.hpp>
0d46491b 27
301610ca
GMF
28#include <logfunc.hpp>
29
780b0bca 30#include "boost_assert_handler.h"
086e2cc0 31#include "host/pingerfactory.h"
26b0f687 32#include "dns/dnsmaster.h"
51cbc790 33#include "icmp/icmppinger.h"
72e54d1c 34#include "link/linkstatus.h"
ced28dc7 35
a7c2eb51 36using namespace std;
2bf8720f
GMF
37using boost::asio::io_service;
38using boost::bind;
101be5ce 39using boost::date_time::time_resolution_traits_adapted64_impl;
2bf8720f
GMF
40using boost::posix_time::microsec_clock;
41using boost::posix_time::ptime;
42using boost::posix_time::seconds;
89f153af 43using boost::posix_time::milliseconds;
e58d7507 44using boost::shared_ptr;
301610ca 45using I2n::Logger::GlobalLogger;
a7c2eb51 46
87758553 47
4c2a5ab5 48//-----------------------------------------------------------------------------
4bb97b45 49// PingScheduler
4c2a5ab5
GMF
50//-----------------------------------------------------------------------------
51
086e2cc0
GMF
52/**
53 * @brief Parameterized constructor.
54 *
ab2cb1ef 55 * @param io_serv The one @c io_serv object that controls async processing
c1abff61 56 * @param network_interface The name of the network interface sending the pings.
086e2cc0
GMF
57 * @param destination_address The remote address to ping.
58 * @param destination_port The remote port to ping.
59 * @param ping_protocol_list A list of protocols to use.
60 * @param ping_interval_in_sec Amount of time between each ping.
61 * @param ping_fail_percentage_limit Maximum amount of pings that can fail.
a7b15639
CH
62 * @param ping_congestion_percentage_limit Amount of pings indication congested
63 * line
64 * @param ping_congestion_duration_thresh Duration in seconds that indicates a
65 * congested line
91aa83f9 66 * @param ping_reply_timeout Max amount time to wait for ping to finish
086e2cc0 67 * @param link_analyzer The object to monitor the link status.
365036be 68 * @param first_delay Delay in seconds from start_pinging to first ping attempt
91aa83f9 69 * @param n_parallel_pings: Number of pingers to ping the same IP in parallel
086e2cc0 70 */
4bb97b45 71PingScheduler::PingScheduler(
ab2cb1ef 72 const IoServiceItem io_serv,
2bf8720f
GMF
73 const string &network_interface,
74 const string &destination_address,
238da857 75 const uint16_t destination_port,
fe6a2f80 76 const PingProtocolList &ping_protocol_list,
c15a722d 77 const long ping_interval_in_sec,
a341119a 78 const int ping_fail_percentage_limit,
a7b15639 79 const int ping_congestion_percentage_limit,
941b5e25 80 const int congest_caused_by_fail_percentage_limit,
a7b15639 81 const int ping_congestion_duration_thresh,
079d19ab 82 const int ping_reply_timeout,
59733431 83 LinkStatusItem link_analyzer,
91aa83f9 84 const int first_delay,
89f153af 85 const int n_parallel_pings,
87758553
CH
86 const int parallel_ping_delay,
87 const int ping_timeout_factor
e39cc3da 88) :
23f51766
CH
89 IoService( io_serv ),
90 NetworkInterfaceName( network_interface ),
23f51766
CH
91 DestinationAddress( destination_address ),
92 DestinationPort( destination_port ),
26b0f687
CH
93 Protocols( ping_protocol_list ),
94 ProtocolIter(),
95 PingIntervalInSec( ping_interval_in_sec ),
242e5fb3 96 NPingers( n_parallel_pings ),
26b0f687 97 FirstDelay( first_delay ),
ab2cb1ef 98 NextPingTimer( *io_serv ),
e39cc3da 99 TimeSentLastPing( microsec_clock::universal_time() ),
26b0f687 100 PingReplyTimeout( ping_reply_timeout ),
87758553 101 PingReplyTimeoutOrig( ping_reply_timeout ),
c1abff61 102 HostAnalyzer( destination_address, ping_fail_percentage_limit,
a7b15639 103 ping_congestion_percentage_limit,
941b5e25 104 congest_caused_by_fail_percentage_limit,
a7b15639
CH
105 ping_congestion_duration_thresh, n_parallel_pings,
106 link_analyzer ),
26b0f687 107 Resolver(),
91aa83f9 108 Pingers(),
91aa83f9
CH
109 NPingersDone( 0 ),
110 ParallelPingDelay( parallel_ping_delay ),
89f153af 111 DelayedPingTimer( *io_serv ),
26b0f687
CH
112 WantToPing( false ),
113 LogPrefix(),
87758553
CH
114 ContinueOnOutdatedIps( false ),
115 PingTimeoutFactor( ping_timeout_factor )
ced28dc7 116{
475ad07c
GMF
117 BOOST_ASSERT( !network_interface.empty() );
118 BOOST_ASSERT( !destination_address.empty() );
23f51766
CH
119 BOOST_ASSERT( ( 0 < destination_port ) &&
120 ( destination_port < numeric_limits<uint16_t>::max() ) );
f71cb7e1 121 BOOST_ASSERT( 0 < ping_interval_in_sec );
23f51766
CH
122 BOOST_ASSERT( (0 <= ping_fail_percentage_limit) &&
123 ( ping_fail_percentage_limit <= 100) );
124
26b0f687 125 update_log_prefix();
23f51766
CH
126
127 init_ping_protocol();
242e5fb3
CH
128
129 // start resolving already so we are prepared to ping
130 update_dns_resolver();
131
2d591235 132}
ced28dc7 133
086e2cc0
GMF
134/**
135 * @brief Destructor.
136 */
4bb97b45 137PingScheduler::~PingScheduler()
2d591235 138{
ced28dc7
GMF
139}
140
c1d776ba 141void PingScheduler::stop_pinging()
ced28dc7 142{
c1abff61
CH
143 // stop pinger and resolver
144 GlobalLogger.debug() << LogPrefix << "scheduler: stop pinging";
91aa83f9 145 clear_pingers();
72be9e7d 146 cancel_resolve(true);
f076f8d4 147
c1abff61
CH
148 // now cancel the own timer in case that pinger cancelation called callback
149 GlobalLogger.debug() << LogPrefix << "scheduler: cancel timer";
f076f8d4 150 NextPingTimer.cancel();
c1d776ba
CH
151}
152
153/**
91aa83f9
CH
154 * @brief stop all pingers and remove them from Pingers variable which will
155 * proboably cause their destruction
156 *
157 * Pingers is empty afterwards
158 */
159void PingScheduler::clear_pingers()
160{
161 PingerItem pinger;
89f153af 162 while(!Pingers.empty())
91aa83f9 163 {
89f153af 164 pinger = Pingers.back();
91aa83f9 165 pinger->stop_pinging();
89f153af 166 Pingers.pop_back();
91aa83f9
CH
167 }
168}
169
170/**
23f51766 171 * @brief Start into infinite loop of calls to ping
cad0b08d
CH
172 *
173 * Does not start yet but set NextPingTimer (possibly to 0), so action starts
174 * when io_service is started
c1d776ba
CH
175 */
176void PingScheduler::start_pinging()
177{
c1d776ba 178 if ( FirstDelay > 0 )
c1abff61
CH
179 GlobalLogger.info() << LogPrefix << "Delaying first ping by "
180 << FirstDelay << "s";
59733431 181 else
c1abff61 182 GlobalLogger.info() << LogPrefix << "Schedule ping as soon as possible";
cad0b08d
CH
183
184 (void) NextPingTimer.expires_from_now( seconds( FirstDelay ) );
23f51766 185 NextPingTimer.async_wait( bind( &PingScheduler::ping, this,
cad0b08d 186 boost::asio::placeholders::error ) );
09de3c4b
GMF
187}
188
4e91c69a 189
c1d776ba 190/**
23f51766 191 * @brief call Ping::ping and schedule a call to ping_done_handler when finished
c1d776ba 192 */
23f51766 193void PingScheduler::ping(const boost::system::error_code &error)
823623d9 194{
d26dce11 195 if ( error )
f076f8d4 196 { // get here, e.g. by NextPingTimer.cancel in stop_pinging
d26dce11 197 if ( error == boost::asio::error::operation_aborted )
c1abff61
CH
198 GlobalLogger.error() << LogPrefix << "Timer for ping was cancelled!"
199 << " --> Stopping";
d26dce11 200 else
c1abff61
CH
201 GlobalLogger.error() << LogPrefix << "Received error " << error
202 << " waiting for ping! Stopping";
d26dce11
CH
203 return;
204 }
205
23f51766
CH
206 // ping as soon as dns is ready
207 WantToPing = true;
8d26221d 208 ping_when_ready();
23f51766
CH
209}
210
c1d776ba 211
8d26221d 212void PingScheduler::ping_when_ready()
23f51766
CH
213{
214 if ( !WantToPing )
823623d9 215 {
2a4dde8b 216 GlobalLogger.info() << LogPrefix << "waiting for ping request "
91aa83f9 217 << "(should take no more than " << PingIntervalInSec << "s)";
23f51766 218 return;
823623d9 219 }
23f51766
CH
220 else if ( Resolver && Resolver->is_resolving() )
221 {
fd62d09f 222 GlobalLogger.info() << LogPrefix << "waiting for DNS to finish";
23f51766
CH
223 return;
224 }
225 else if ( !Resolver )
242e5fb3 226 {
23f51766
CH
227 // should not happen, but check anyway
228 GlobalLogger.warning() << LogPrefix << "Have no resolver!";
242e5fb3
CH
229 return;
230 }
09de3c4b 231
c1abff61 232 GlobalLogger.info() << LogPrefix << "start ping";
23f51766 233 WantToPing = false;
09de3c4b 234
fd62d09f
CH
235 // try to get an up-to-date IP (ContinueOnOutdatedIps may only be set
236 // because a CNAME was out of date -- IPs may still be current)
26b0f687 237 HostAddress ip = Resolver->get_next_ip();
8d26221d 238
fd62d09f
CH
239 if ( !ip.is_valid() )
240 { // this can happen in 2 cases: if ContinueOnOutdatedIps==true
241 // or when ip went out of date between resolve and now
242 // --> try to use outdated IP
26b0f687
CH
243 GlobalLogger.info() << LogPrefix << "Checking for outdated IPs";
244 bool check_up_to_date = false;
245 ip = Resolver->get_next_ip(check_up_to_date);
246 }
fd62d09f 247 if ( !ip.is_valid() )
b44a5f96
CH
248 { // Do not even have an outdated IP!
249 // This happens if have no cached IPs and resolve failed
250 GlobalLogger.info() << LogPrefix << "Not even outdated IP to ping "
251 << "-- treat like a failed ping.";
fd62d09f 252
ffa5cfe2
CH
253 // skip the ping and directly call ping_done_handler the appropriate
254 // number of times
255 for (int count=0; count<NPingers; ++count)
256 ping_done_handler(PingStatus_FailureNoIP, 0);
fd62d09f
CH
257 }
258 else
259 {
242e5fb3
CH
260 // create new pingers
261 for (int count=0; count<NPingers; ++count)
262 Pingers.push_back(
263 PingerFactory::createPinger(*ProtocolIter, IoService,
264 NetworkInterfaceName,
265 PingReplyTimeout) );
266
267 // remember when pinging started
268 TimeSentLastPing = microsec_clock::universal_time();
269
270 // get actual IP
91aa83f9
CH
271 boost::asio::ip::address actual_ip = ip.get_ip();
272 GlobalLogger.info() << LogPrefix << "pinging IP " << actual_ip
273 << " with TTL " << ip.get_ttl().get_updated_value() << "s";
242e5fb3 274 delayed_ping(boost::system::error_code(), actual_ip, 0);
26b0f687 275 }
3f6ba924
CH
276}
277
89f153af 278void PingScheduler::delayed_ping( const boost::system::error_code &error,
242e5fb3 279 const boost::asio::ip::address ip,
89f153af 280 const int pinger_index )
91aa83f9 281{
89f153af
CH
282 if (error)
283 {
284 GlobalLogger.info() << LogPrefix << "delayed ping received an error: "
285 << error;
286 return;
287 }
89f153af
CH
288
289 GlobalLogger.debug() << LogPrefix << "starting delayed ping index "
290 << pinger_index;
291 Pingers[pinger_index]->ping(ip,
292 DestinationPort,
293 boost::bind(&PingScheduler::ping_done_handler,
294 this, _1, _2) );
242e5fb3
CH
295 if (pinger_index >= NPingers-1)
296 GlobalLogger.debug() << LogPrefix << "started all delayed pings";
297 else
298 {
299 DelayedPingTimer.expires_from_now( milliseconds(ParallelPingDelay) );
300 DelayedPingTimer.async_wait( bind( &PingScheduler::delayed_ping,
89f153af 301 this, boost::asio::placeholders::error, ip, pinger_index+1) );
242e5fb3 302 }
91aa83f9
CH
303}
304
166fd9e9 305
23f51766 306//------------------------------------------------------------------------------
91aa83f9 307// Post Processing of Ping result and Preparation for next ping
23f51766 308//------------------------------------------------------------------------------
e58d7507 309
c1d776ba
CH
310/**
311 * @brief called when Ping::ping is done; calls functions to update
312 * statistics, ping interval and elapsed time;
23f51766 313 * schedules a call to ping, thereby closing the loop
c1d776ba 314 */
9c0dcf33
CH
315void PingScheduler::ping_done_handler( const PingStatus &result,
316 const long ping_duration_us )
502b6af0 317{
96c4e7a4
CH
318 PingStatus edited_result = result;
319 if (result == PingStatus_SuccessReply && ContinueOnOutdatedIps)
320 {
321 edited_result = PingStatus_SuccessOutdatedIP;
322
323 // reset ContinueOnOutdatedIps
324 ContinueOnOutdatedIps = false;
325 update_log_prefix();
326 }
327
91aa83f9
CH
328 ++NPingersDone;
329 GlobalLogger.info() << LogPrefix << "Ping " << NPingersDone << " of "
330 << NPingers << " done with result " << to_string(edited_result);
f8918bd5 331
c1d776ba 332 // post-processing
91aa83f9 333 // can call update_ping_interval only after update_ping_statistics!
9c0dcf33 334 HostAnalyzer.update_ping_statistics( edited_result, ping_duration_us );
91aa83f9
CH
335
336 // prepare next ping only after all pingers are done
337 if (NPingersDone == NPingers)
242e5fb3
CH
338 {
339 // stop and destruct all pingers
340 clear_pingers();
341
342 GlobalLogger.debug() << LogPrefix
343 << "--------------------------------------------------------------";
344
345 // update variables for next ping: number of pings, delay, protocol
346 update_ping_protocol();
347 update_ping_interval();
348 update_ping_number();
349
91aa83f9 350 prepare_next_ping();
242e5fb3 351 }
91aa83f9
CH
352}
353
354
355void PingScheduler::prepare_next_ping()
356{
242e5fb3
CH
357 // start DNS resolve if necessary
358 update_dns_resolver();
23f51766 359
ffa5cfe2
CH
360 NPingersDone = 0;
361
c1d776ba 362 // schedule next ping
91aa83f9
CH
363 int seconds_since_last_ping = (microsec_clock::universal_time()
364 - TimeSentLastPing).total_seconds();
365 if ( seconds_since_last_ping > PingIntervalInSec )
366 {
367 GlobalLogger.info() << "We are late for next ping!";
368 seconds_since_last_ping = PingIntervalInSec;
369 (void) NextPingTimer.expires_from_now( seconds(0) );
370 }
371 else
372 (void) NextPingTimer.expires_from_now( seconds( PingIntervalInSec
373 - seconds_since_last_ping ) );
23f51766 374 NextPingTimer.async_wait( bind( &PingScheduler::ping, this,
d26dce11 375 boost::asio::placeholders::error ) );
d8a91bd6
GMF
376}
377
378void PingScheduler::update_ping_interval()
379{
c1d776ba 380 // have to ping more often?
fb469ffa 381 if ( HostAnalyzer.exceeded_ping_failed_limit() )
d8a91bd6
GMF
382 {
383 PingIntervalInSec.speed_up();
384
c1abff61
CH
385 GlobalLogger.debug() << LogPrefix << "- Speeding up ping interval to: "
386 << PingIntervalInSec << "s";
87758553
CH
387 PingReplyTimeout = PingReplyTimeoutOrig * PingTimeoutFactor;
388 GlobalLogger.debug() << LogPrefix << "- Increase ping timeout to "
389 << PingReplyTimeout << "s";
d8a91bd6
GMF
390 }
391 else
392 {
393 PingIntervalInSec.back_to_original();
394
c1abff61
CH
395 GlobalLogger.debug() << LogPrefix << "- Stick to the original ping "
396 << "interval: " << PingIntervalInSec << "s";
87758553
CH
397 PingReplyTimeout = PingReplyTimeoutOrig;
398 GlobalLogger.debug() << LogPrefix << "- Reset ping timeout to "
399 << PingReplyTimeout << "s";
d8a91bd6 400 }
ced28dc7
GMF
401}
402
242e5fb3
CH
403/** in case of congested line, increase number of pings
404 *
405 * CAUTION! Only call this after clear_pingers !!!
406 * */
407void PingScheduler::update_ping_number()
408{
409 // make sure we do not loose track of pingers here
410 if ( NPingersDone != NPingers || !Pingers.empty() )
411 {
412 GlobalLogger.warning() << LogPrefix << "Should only change number of "
413 << "pingers when all are finished and deleted! Have " << NPingers
414 << " pingers, " << NPingersDone << " of which are done and "
415 << Pingers.size() << " in listDone! Will not change NPingers.";
416 return;
417 }
418
419 if ( HostAnalyzer.exceeded_ping_congestion_limit() )
420 {
421 NPingers.increase();
422
4d7db1af 423 GlobalLogger.notice() << LogPrefix << "Line appears congested!";
242e5fb3
CH
424 GlobalLogger.debug() << LogPrefix << "- Increasing ping number to: "
425 << NPingers;
426 }
427 else
428 {
429 NPingers.back_to_original();
430
431 GlobalLogger.debug() << LogPrefix << "- Stick to the original ping "
432 << "number: " << NPingers;
433 }
434
435 HostAnalyzer.set_n_parallel_pings(NPingers);
436}
437
23f51766
CH
438//------------------------------------------------------------------------------
439// Ping Protocol Rotation
440//------------------------------------------------------------------------------
441
26b0f687 442void PingScheduler::init_ping_protocol()
23f51766 443{
26b0f687 444 ProtocolIter = Protocols.end();
23f51766
CH
445 get_next_ping_protocol();
446}
447
26b0f687 448void PingScheduler::update_ping_protocol()
23f51766
CH
449{
450 if ( can_change_ping_protocol() )
451 {
452 get_next_ping_protocol();
453 }
454}
455
26b0f687 456void PingScheduler::get_next_ping_protocol()
23f51766 457{
26b0f687
CH
458 ++ProtocolIter;
459 if (ProtocolIter == Protocols.end())
460 ProtocolIter = Protocols.begin();
23f51766
CH
461}
462
26b0f687 463bool PingScheduler::can_change_ping_protocol() const
23f51766 464{
c1abff61
CH
465 // TODO can_change_ping_protocol() and get_next_ping_protocol() may be
466 // implemented in a Algorithm class that can be exchanged in this class to
467 // provide an algorithm neutral class
23f51766
CH
468 return true;
469}
470
471//------------------------------------------------------------------------------
472// DNS host name resolution
473//------------------------------------------------------------------------------
26b0f687
CH
474
475// show "!" after host name if running on outdated IPs
8d26221d 476void PingScheduler::update_log_prefix()
26b0f687
CH
477{
478 std::stringstream temp;
72be9e7d 479 temp << "Sched(" << DestinationAddress;
26b0f687
CH
480 if (ContinueOnOutdatedIps)
481 temp << "!";
482 temp << "): ";
483 LogPrefix = temp.str();
484}
485
242e5fb3 486void PingScheduler::update_dns_resolver()
23f51766
CH
487{
488 if (Resolver && Resolver->is_resolving())
72be9e7d
CH
489 cancel_resolve(false);
490
491 if (ContinueOnOutdatedIps)
23f51766 492 {
72be9e7d
CH
493 ContinueOnOutdatedIps = false;
494 update_log_prefix();
23f51766
CH
495 }
496
497 // DNS master caches created resolvers and resolved IPs, so this will
498 // probably just return an existing resolver with already resolved IPs for
499 // requested protocol ( ICMP/TCP is ignored, only IPv4/v6 is important)
500 Resolver = DnsMaster::get_instance()->get_resolver_for(DestinationAddress,
242e5fb3 501 *ProtocolIter);
fd62d09f
CH
502
503 // get number of up-to-date IPs
2a4dde8b 504 // TODO should check here, if they will be up to date in PingIntervalInSec
fd62d09f
CH
505 bool check_up_to_date = true;
506 int ip_count = Resolver->get_resolved_ip_count(check_up_to_date);
507 if (ip_count > 0)
23f51766 508 {
fd62d09f 509 GlobalLogger.info() << LogPrefix << "Set resolved_ip_count to "
f8918bd5 510 << ip_count << " (IPs may be outdated=" << !check_up_to_date << ")";
fd62d09f
CH
511 HostAnalyzer.set_resolved_ip_count( ip_count );
512
72be9e7d 513 if (Resolver->is_resolving())
c1abff61 514 GlobalLogger.warning() << LogPrefix << "have up to date IPs but "
23f51766
CH
515 << "resolver seems to be resolving all the same... "
516 << "Start pinging anyway!";
8d26221d 517 ping_when_ready();
23f51766
CH
518 }
519 else
2a4dde8b
CH
520 {
521 GlobalLogger.info() << LogPrefix
522 << "No up-to-date IPs --> start resolve";
23f51766 523 start_resolving_ping_address();
2a4dde8b
CH
524 // set resolved_ip_count will be called in resolve callback
525 }
23f51766
CH
526}
527
26b0f687 528void PingScheduler::start_resolving_ping_address()
23f51766 529{
26b0f687 530 Resolver->async_resolve( boost::bind(&PingScheduler::dns_resolve_callback,
23f51766
CH
531 this, _1, _2) );
532}
533
26b0f687 534void PingScheduler::dns_resolve_callback(const bool was_success,
cd71d095 535 const int recursion_count)
23f51766 536{
c1abff61 537 GlobalLogger.info() << LogPrefix << "dns resolution finished "
23f51766 538 << "with success = " << was_success << " "
cd71d095 539 << "after " << recursion_count << " recursions";
23f51766 540
26b0f687
CH
541 if ( was_success )
542 {
fd62d09f
CH
543 // trust that a successfull DNS resolve means we have an IP with TTL>0
544 int ip_count = Resolver->get_resolved_ip_count(!ContinueOnOutdatedIps);
545 if (ip_count == 0)
ffa5cfe2 546 {
fd62d09f
CH
547 GlobalLogger.warning() << LogPrefix
548 << "Should not have reached this case: resolve was "
549 << "successfull but still have no IPs (up-to-date="
550 << !ContinueOnOutdatedIps << ")!";
551 if (DnsMaster::get_instance()->get_resolved_ip_ttl_threshold() > 0)
552 GlobalLogger.warning() << LogPrefix << "This probably happened "
553 << "because you specified a TTL threshold > 0 but resolving"
554 << " had no effect on TTLs since external cache is only "
555 << "updated when TTL=0 is reached.";
556 }
557 else
558 {
559 GlobalLogger.info() << LogPrefix << "Set resolved_ip_count to "
f8918bd5
CH
560 << ip_count << " (IPs may be outdated="
561 << ContinueOnOutdatedIps << ") --> could ping now";
fd62d09f 562 }
ffa5cfe2 563 HostAnalyzer.set_resolved_ip_count( ip_count );
8d26221d 564 ping_when_ready();
26b0f687
CH
565 }
566 else
567 { // host name resolution failed; try again bypassing first outdated CNAME
8d26221d 568 // or using cached IP
26b0f687 569 std::string skip_host = Resolver->get_skip_cname();
23f51766
CH
570
571 if (skip_host.empty())
838e0acf 572 { // try to continue with cached IPs
fd62d09f 573 int ip_count = Resolver->get_resolved_ip_count(false);
838e0acf
CH
574
575 if (ip_count == 0)
576 GlobalLogger.notice() << LogPrefix << "DNS failed "
577 << "and have no cached IPs either --> cannot ping";
578 // ping_when_ready will deal with this case
579 else
580 {
581 ContinueOnOutdatedIps = true;
582 update_log_prefix();
583
584 GlobalLogger.notice() << LogPrefix << "DNS failed, "
585 << "try anyway with cached data";
586 }
587
fd62d09f 588 GlobalLogger.info() << LogPrefix << "Set resolved_ip_count to "
f8918bd5 589 << ip_count << " (IPs may be outdated=" << true << ")";
fd62d09f
CH
590 HostAnalyzer.set_resolved_ip_count( ip_count );
591
8d26221d 592 ping_when_ready();
23f51766
CH
593 }
594 else
8d26221d 595 { // have CNAME to continue
838e0acf
CH
596 ContinueOnOutdatedIps = true;
597 update_log_prefix();
23f51766
CH
598 GlobalLogger.notice() << LogPrefix << "DNS failed, "
599 << "try again skipping a CNAME and resolving "
600 << skip_host << " directly";
72be9e7d
CH
601
602 cancel_resolve(false);
603
604 // now create new resolver
23f51766 605 Resolver = DnsMaster::get_instance()
26b0f687 606 ->get_resolver_for(skip_host, *ProtocolIter);
23f51766
CH
607 start_resolving_ping_address();
608 }
609 }
23f51766 610}
72be9e7d
CH
611
612/**
613 * cancel resolver if force_cancel or if it is not resolving DestinationAddress
614 *
615 * Resolvers have a life on their own: they are cached by DnsMaster so never go
616 * out of scope and even after calling callbacks, there might still be a
617 * longterm timer active to re-try resolving.
618 * We want to cancel that long-term timer only if the Resolver is not for our
619 * real, original DestinationAddress but a CNAME, which can happen when trying
620 * to skip cnames and working on out-dated IPs
621 */
622void PingScheduler::cancel_resolve(const bool force_cancel)
623{
624 if (force_cancel)
625 {
626 GlobalLogger.info() << "Cancelling resolver (forced)";
627 Resolver->cancel_resolve();
628 }
629 else if ( Resolver->get_hostname() == DestinationAddress )
630 GlobalLogger.info() << LogPrefix
631 << "Leave original resolver active in background";
632 else
633 {
634 GlobalLogger.info() << LogPrefix << "Cancel resolver for "
635 << Resolver->get_hostname() << " since is not the original "
636 << DestinationAddress;
637 Resolver->cancel_resolve();
638 }
639}
640