improved information content of logs: in LinkAnalyzer messages add cname chain
[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
d10644b9 156 *
91aa83f9
CH
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
9c0dcf33 333 HostAnalyzer.update_ping_statistics( edited_result, ping_duration_us );
91aa83f9
CH
334
335 // prepare next ping only after all pingers are done
336 if (NPingersDone == NPingers)
242e5fb3
CH
337 {
338 // stop and destruct all pingers
339 clear_pingers();
340
341 GlobalLogger.debug() << LogPrefix
342 << "--------------------------------------------------------------";
343
344 // update variables for next ping: number of pings, delay, protocol
d10644b9 345 // do this only after call to update_ping_statistics!
242e5fb3
CH
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";
d8a91bd6
GMF
387 }
388 else
389 {
390 PingIntervalInSec.back_to_original();
391
c1abff61
CH
392 GlobalLogger.debug() << LogPrefix << "- Stick to the original ping "
393 << "interval: " << PingIntervalInSec << "s";
d8a91bd6 394 }
ced28dc7
GMF
395}
396
242e5fb3
CH
397/** in case of congested line, increase number of pings
398 *
d10644b9
CH
399 * also increases ping timeout if line is congested
400 *
242e5fb3
CH
401 * CAUTION! Only call this after clear_pingers !!!
402 * */
403void PingScheduler::update_ping_number()
404{
405 // make sure we do not loose track of pingers here
406 if ( NPingersDone != NPingers || !Pingers.empty() )
407 {
408 GlobalLogger.warning() << LogPrefix << "Should only change number of "
409 << "pingers when all are finished and deleted! Have " << NPingers
410 << " pingers, " << NPingersDone << " of which are done and "
411 << Pingers.size() << " in listDone! Will not change NPingers.";
412 return;
413 }
414
415 if ( HostAnalyzer.exceeded_ping_congestion_limit() )
416 {
417 NPingers.increase();
418
d10644b9 419 GlobalLogger.notice() << LogPrefix << "No reply from host, "
e638894d
CH
420 << "switching to burst ping mode with longer timeouts ("
421 << DnsMaster::get_cname_chain_str(DestinationAddress) << ")";
242e5fb3
CH
422 GlobalLogger.debug() << LogPrefix << "- Increasing ping number to: "
423 << NPingers;
d10644b9
CH
424
425 PingReplyTimeout = PingReplyTimeoutOrig * PingTimeoutFactor;
426 GlobalLogger.debug() << LogPrefix << "- Increase ping timeout to "
427 << PingReplyTimeout << "s";
242e5fb3
CH
428 }
429 else
430 {
431 NPingers.back_to_original();
432
433 GlobalLogger.debug() << LogPrefix << "- Stick to the original ping "
434 << "number: " << NPingers;
d10644b9
CH
435
436 PingReplyTimeout = PingReplyTimeoutOrig;
437 GlobalLogger.debug() << LogPrefix << "- Reset ping timeout to "
438 << PingReplyTimeout << "s";
242e5fb3
CH
439 }
440
d10644b9 441 // tell host analyzer so it expects the correct number of ping results
242e5fb3
CH
442 HostAnalyzer.set_n_parallel_pings(NPingers);
443}
444
23f51766
CH
445//------------------------------------------------------------------------------
446// Ping Protocol Rotation
447//------------------------------------------------------------------------------
448
26b0f687 449void PingScheduler::init_ping_protocol()
23f51766 450{
26b0f687 451 ProtocolIter = Protocols.end();
23f51766
CH
452 get_next_ping_protocol();
453}
454
26b0f687 455void PingScheduler::update_ping_protocol()
23f51766
CH
456{
457 if ( can_change_ping_protocol() )
458 {
459 get_next_ping_protocol();
460 }
461}
462
26b0f687 463void PingScheduler::get_next_ping_protocol()
23f51766 464{
26b0f687
CH
465 ++ProtocolIter;
466 if (ProtocolIter == Protocols.end())
467 ProtocolIter = Protocols.begin();
23f51766
CH
468}
469
26b0f687 470bool PingScheduler::can_change_ping_protocol() const
23f51766 471{
c1abff61
CH
472 // TODO can_change_ping_protocol() and get_next_ping_protocol() may be
473 // implemented in a Algorithm class that can be exchanged in this class to
474 // provide an algorithm neutral class
23f51766
CH
475 return true;
476}
477
478//------------------------------------------------------------------------------
479// DNS host name resolution
480//------------------------------------------------------------------------------
26b0f687
CH
481
482// show "!" after host name if running on outdated IPs
8d26221d 483void PingScheduler::update_log_prefix()
26b0f687
CH
484{
485 std::stringstream temp;
72be9e7d 486 temp << "Sched(" << DestinationAddress;
26b0f687
CH
487 if (ContinueOnOutdatedIps)
488 temp << "!";
489 temp << "): ";
490 LogPrefix = temp.str();
491}
492
242e5fb3 493void PingScheduler::update_dns_resolver()
23f51766
CH
494{
495 if (Resolver && Resolver->is_resolving())
72be9e7d
CH
496 cancel_resolve(false);
497
498 if (ContinueOnOutdatedIps)
23f51766 499 {
72be9e7d
CH
500 ContinueOnOutdatedIps = false;
501 update_log_prefix();
23f51766
CH
502 }
503
504 // DNS master caches created resolvers and resolved IPs, so this will
505 // probably just return an existing resolver with already resolved IPs for
506 // requested protocol ( ICMP/TCP is ignored, only IPv4/v6 is important)
507 Resolver = DnsMaster::get_instance()->get_resolver_for(DestinationAddress,
242e5fb3 508 *ProtocolIter);
fd62d09f
CH
509
510 // get number of up-to-date IPs
2a4dde8b 511 // TODO should check here, if they will be up to date in PingIntervalInSec
fd62d09f
CH
512 bool check_up_to_date = true;
513 int ip_count = Resolver->get_resolved_ip_count(check_up_to_date);
514 if (ip_count > 0)
23f51766 515 {
fd62d09f 516 GlobalLogger.info() << LogPrefix << "Set resolved_ip_count to "
f8918bd5 517 << ip_count << " (IPs may be outdated=" << !check_up_to_date << ")";
fd62d09f
CH
518 HostAnalyzer.set_resolved_ip_count( ip_count );
519
72be9e7d 520 if (Resolver->is_resolving())
c1abff61 521 GlobalLogger.warning() << LogPrefix << "have up to date IPs but "
23f51766
CH
522 << "resolver seems to be resolving all the same... "
523 << "Start pinging anyway!";
8d26221d 524 ping_when_ready();
23f51766
CH
525 }
526 else
2a4dde8b
CH
527 {
528 GlobalLogger.info() << LogPrefix
529 << "No up-to-date IPs --> start resolve";
23f51766 530 start_resolving_ping_address();
2a4dde8b
CH
531 // set resolved_ip_count will be called in resolve callback
532 }
23f51766
CH
533}
534
26b0f687 535void PingScheduler::start_resolving_ping_address()
23f51766 536{
26b0f687 537 Resolver->async_resolve( boost::bind(&PingScheduler::dns_resolve_callback,
23f51766
CH
538 this, _1, _2) );
539}
540
26b0f687 541void PingScheduler::dns_resolve_callback(const bool was_success,
cd71d095 542 const int recursion_count)
23f51766 543{
c1abff61 544 GlobalLogger.info() << LogPrefix << "dns resolution finished "
23f51766 545 << "with success = " << was_success << " "
cd71d095 546 << "after " << recursion_count << " recursions";
23f51766 547
26b0f687
CH
548 if ( was_success )
549 {
fd62d09f
CH
550 // trust that a successfull DNS resolve means we have an IP with TTL>0
551 int ip_count = Resolver->get_resolved_ip_count(!ContinueOnOutdatedIps);
552 if (ip_count == 0)
ffa5cfe2 553 {
fd62d09f
CH
554 GlobalLogger.warning() << LogPrefix
555 << "Should not have reached this case: resolve was "
556 << "successfull but still have no IPs (up-to-date="
557 << !ContinueOnOutdatedIps << ")!";
558 if (DnsMaster::get_instance()->get_resolved_ip_ttl_threshold() > 0)
559 GlobalLogger.warning() << LogPrefix << "This probably happened "
560 << "because you specified a TTL threshold > 0 but resolving"
561 << " had no effect on TTLs since external cache is only "
562 << "updated when TTL=0 is reached.";
563 }
564 else
565 {
566 GlobalLogger.info() << LogPrefix << "Set resolved_ip_count to "
f8918bd5
CH
567 << ip_count << " (IPs may be outdated="
568 << ContinueOnOutdatedIps << ") --> could ping now";
fd62d09f 569 }
ffa5cfe2 570 HostAnalyzer.set_resolved_ip_count( ip_count );
8d26221d 571 ping_when_ready();
26b0f687
CH
572 }
573 else
574 { // host name resolution failed; try again bypassing first outdated CNAME
8d26221d 575 // or using cached IP
26b0f687 576 std::string skip_host = Resolver->get_skip_cname();
23f51766
CH
577
578 if (skip_host.empty())
838e0acf 579 { // try to continue with cached IPs
fd62d09f 580 int ip_count = Resolver->get_resolved_ip_count(false);
838e0acf
CH
581
582 if (ip_count == 0)
583 GlobalLogger.notice() << LogPrefix << "DNS failed "
584 << "and have no cached IPs either --> cannot ping";
585 // ping_when_ready will deal with this case
586 else
587 {
588 ContinueOnOutdatedIps = true;
589 update_log_prefix();
590
591 GlobalLogger.notice() << LogPrefix << "DNS failed, "
592 << "try anyway with cached data";
593 }
594
fd62d09f 595 GlobalLogger.info() << LogPrefix << "Set resolved_ip_count to "
f8918bd5 596 << ip_count << " (IPs may be outdated=" << true << ")";
fd62d09f
CH
597 HostAnalyzer.set_resolved_ip_count( ip_count );
598
8d26221d 599 ping_when_ready();
23f51766
CH
600 }
601 else
8d26221d 602 { // have CNAME to continue
838e0acf
CH
603 ContinueOnOutdatedIps = true;
604 update_log_prefix();
23f51766
CH
605 GlobalLogger.notice() << LogPrefix << "DNS failed, "
606 << "try again skipping a CNAME and resolving "
607 << skip_host << " directly";
72be9e7d
CH
608
609 cancel_resolve(false);
610
611 // now create new resolver
23f51766 612 Resolver = DnsMaster::get_instance()
26b0f687 613 ->get_resolver_for(skip_host, *ProtocolIter);
23f51766
CH
614 start_resolving_ping_address();
615 }
616 }
23f51766 617}
72be9e7d
CH
618
619/**
620 * cancel resolver if force_cancel or if it is not resolving DestinationAddress
621 *
622 * Resolvers have a life on their own: they are cached by DnsMaster so never go
623 * out of scope and even after calling callbacks, there might still be a
624 * longterm timer active to re-try resolving.
625 * We want to cancel that long-term timer only if the Resolver is not for our
626 * real, original DestinationAddress but a CNAME, which can happen when trying
627 * to skip cnames and working on out-dated IPs
628 */
629void PingScheduler::cancel_resolve(const bool force_cancel)
630{
631 if (force_cancel)
632 {
633 GlobalLogger.info() << "Cancelling resolver (forced)";
634 Resolver->cancel_resolve();
635 }
636 else if ( Resolver->get_hostname() == DestinationAddress )
637 GlobalLogger.info() << LogPrefix
638 << "Leave original resolver active in background";
639 else
640 {
641 GlobalLogger.info() << LogPrefix << "Cancel resolver for "
642 << Resolver->get_hostname() << " since is not the original "
643 << DestinationAddress;
644 Resolver->cancel_resolve();
645 }
646}
647