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