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