, RandomIdGenerator()
, RequestId( 0 )
, OperationCancelled( false )
+ , LongermTimerIsActive( false )
{
std::stringstream temp;
temp << "Dns(" << ResolverBase::Hostname << "): ";
ResolveTimeoutTimer.cancel();
PauseBeforeRetryTimer.cancel();
StaleDataLongtermTimer.cancel();
+ LongermTimerIsActive = false;
// create DNS request
boost::net::dns::message dns_message( ResolverBase::Hostname, Protocol );
this, boost::asio::placeholders::error
)
);
+ LongermTimerIsActive = true;
// for now, admit failure
bool was_success = false;
// treat a CNAME as a partial result: not enough to run callbacks
// from finalize_resolve, but enough to stop timers and reset
// RetryCount --> name resolution can take longer
- stop_trying();
+ stop_trying(true);
}
}
}
return;
}
else if (was_success)
+ {
GlobalLogger.debug() << LogPrefix << "CNAME resolution succeeded";
+ finalize_resolve(was_success, cname_count+1);
+ }
else
+ {
GlobalLogger.info() << LogPrefix << "CNAME resolution failed";
// no use to schedule retry in this case since cname resolver must have
// failed several times and we can only re-start the same procedure with
- // the same information
-
- // cname counts like one more recursion step ...
- finalize_resolve(was_success, cname_count+1);
+ // the same information. But can re-try later
+ handle_unavailable();
+ }
}
<< "waiting for DNS reply!";
// stop timers
- stop_trying();
+ stop_trying(was_success);
// schedule callbacks, clearing callback list
ResolverBase::schedule_callbacks(was_success, cname_count);
}
-void DnsResolver::stop_trying()
+/**
+ * arg was_success determines if stop trying forever or just for the moment
+ * --> determines if we cancel StaleDataLongtermTimer or not
+ */
+void DnsResolver::stop_trying(bool was_success)
{
// cancel timers
GlobalLogger.debug() << LogPrefix << "Cancelling timers";
ResolveTimeoutTimer.cancel();
PauseBeforeRetryTimer.cancel();
- StaleDataLongtermTimer.cancel();
+
+ if (was_success)
+ {
+ StaleDataLongtermTimer.cancel();
+ LongermTimerIsActive = false;
+ }
// clean up
RetryCount = 0;
}
-bool DnsResolver::is_resolving()
+/**
+ * return true if resolver is currently resolving
+ *
+ * Is true from call to async_resolve until callbacks
+ * --> returns true if waiting for result or (short-term) retry
+ *
+ * However, does NOT tell you if the (long-term) stale timeout is active!
+ * That timer has no effect on result, need to check is_waiting_to_resolve
+ * for that
+ */
+bool DnsResolver::is_resolving() const
{
return IsResolving;
}
/**
+ * returns true if either is_resolving or the long-term timer is active
+ *
+ * is_resolving returns true if the short-term retry timer is active
+ */
+bool DnsResolver::is_waiting_to_resolve() const
+{
+ return IsResolving || LongermTimerIsActive;
+}
+
+
+/**
* cancel a earlier call to async_resolve
*
* callbacks will be called with was_success=false; all internal operations
* will be cancelled and internal callbacks (timers, dns results) have no
- * effect any more
+ * effect any more; cancels also the long-term stale-data timer
*/
void DnsResolver::cancel_resolve()
{
int cname_count = 1;
finalize_resolve(was_success, cname_count);
+ // also cancel the long-term timer
+ StaleDataLongtermTimer.cancel();
+
// set after finalize_resolve, so can check in finalize_resolve that
// OperationCancelled is never true
OperationCancelled = true;
GlobalLogger.info() << LogPrefix << "Get next IP from cached result of "
<< n_ips << " IPs; first index to consider is " << NextIpIndex
- << "; TTL thresh = " << ttl_thresh << " is used: " << check_up_to_date;
+ << "; TTL thresh=" << ttl_thresh << "s is used: " << check_up_to_date;
// loop until we have found a cached result (that is up to date)
// or until we have tried all cached IPs
{
// check index since cache size may have changed since last call
if (NextIpIndex >= n_ips)
+ {
+ GlobalLogger.debug() << LogPrefix << "Reset NextIpIndex";
NextIpIndex = 0;
+ }
else if ( n_iter >= n_ips)
+ {
+ GlobalLogger.debug() << LogPrefix << "No IP found";
return HostAddress(); // have checked all candidates
+ }
else
{ // there are candidates left to consider
return_candidate = cached_data[NextIpIndex++];
return return_candidate;
else if (cached_data[NextIpIndex].get_ttl().get_updated_value()
> ttl_thresh)
- return cached_data[++NextIpIndex];
+ return return_candidate;
else
++n_iter;
}
bool have_up_to_date_ip();
int get_resolved_ip_count();
void cancel_resolve();
- bool is_resolving();
+ bool is_resolving() const;
+ bool is_waiting_to_resolve() const;
// implementation of ResolverBase::async_resolve
protected:
const int cname_count);
void schedule_retry();
void finalize_resolve(const bool success, const int cname_count=0);
- void stop_trying();
+ void stop_trying(const bool was_success);
void wait_timer_timeout_handler(const boost::system::error_code &error);
void gather_results( const boost::net::dns::rr_list_t *rr_list,
std::vector<host_addr_pair> *result_ips,
boost::uuids::random_generator RandomIdGenerator;
uint16_t RequestId;
bool OperationCancelled;
+ bool LongermTimerIsActive;
};
#endif
bool HostAddress::is_valid() const
{
- return Ip == address() || Ip == boost::asio::ip::address_v4()
- || Ip == boost::asio::ip::address_v6();
+ return Ip != address() && Ip != boost::asio::ip::address_v4()
+ && Ip != boost::asio::ip::address_v6();
}
// only real public function
public:
HostAddress get_next_ip(const bool check_up_to_date=true)
- { return IpAddress; }
- bool have_up_to_date_ip() { return true; }
- int get_resolved_ip_count(){ return 1; }
- bool is_resolving() { return false; }
+ { return IpAddress; }
+ bool have_up_to_date_ip() { return true; }
+ int get_resolved_ip_count() { return 1; }
+ bool is_resolving() const { return false; }
+ bool is_waiting_to_resolve() const { return false; }
void cancel_resolve() {}
// implementation of ResolverBase::async_resolve
*/
void async_resolve(const callback_type &callback);
virtual void cancel_resolve() = 0;
- virtual bool is_resolving() = 0;
+ virtual bool is_resolving() const = 0;
+ virtual bool is_waiting_to_resolve() const = 0;
virtual HostAddress get_next_ip(const bool check_up_to_date=true) = 0;
virtual bool have_up_to_date_ip() = 0;
// stop pinger and resolver
GlobalLogger.debug() << LogPrefix << "scheduler: stop pinging";
Ping->stop_pinging();
- Resolver->cancel_resolve();
+ cancel_resolve(true);
// now cancel the own timer in case that pinger cancelation called callback
GlobalLogger.debug() << LogPrefix << "scheduler: cancel timer";
ip = Resolver->get_next_ip(check_up_to_date);
}
if ( ip.is_valid() )
- Ping->ping(
- Resolver->get_next_ip().get_ip(),
- DestinationPort,
- boost::bind(&PingScheduler::ping_done_handler, this, _1)
- );
+ Ping->ping( ip.get_ip(),
+ DestinationPort,
+ boost::bind(&PingScheduler::ping_done_handler, this, _1) );
else
{ // should not happen
GlobalLogger.error() << LogPrefix << "No IP to ping "
if ( !Resolver->is_resolving() )
start_resolving_ping_address();
}
-
- // next time try with up-to-date IP
- ContinueOnOutdatedIps = false;
}
update_ping_interval();
update_ping_elapsed_time();
+ if (ping_success)
+ { // reset ContinueOnOutdatedIps
+ ContinueOnOutdatedIps = false;
+ update_log_prefix();
+ }
+
// get next protocol, possibly start resolving IPs
update_ping_protocol();
if (ProtocolIter == Protocols.end())
ProtocolIter = Protocols.begin();
PingProtocol ping_protocol = *ProtocolIter;
- // --> ProtocolIter still points to currently used protocol which is
+ // --> ProtocolIter still points to currently used protocol which is
// required in dns_resolve_callback
if (Ping)
NetworkInterfaceName, PingReplyTimeout);
update_dns_resolver( ping_protocol );
+
}
bool PingScheduler::can_change_ping_protocol() const
void PingScheduler::update_log_prefix()
{
std::stringstream temp;
- temp << "PS(" << DestinationAddress;
+ temp << "Sched(" << DestinationAddress;
if (ContinueOnOutdatedIps)
temp << "!";
temp << "): ";
void PingScheduler::update_dns_resolver( PingProtocol current_protocol )
{
if (Resolver && Resolver->is_resolving())
+ cancel_resolve(false);
+
+ if (ContinueOnOutdatedIps)
{
- GlobalLogger.warning() << LogPrefix
- << "Resolver still seems to be resolving --> cancel!";
- Resolver->cancel_resolve();
+ ContinueOnOutdatedIps = false;
+ update_log_prefix();
}
// DNS master caches created resolvers and resolved IPs, so this will
// start resolving if no ips available
if ( Resolver->have_up_to_date_ip() )
{
- if (!Resolver->is_resolving())
+ if (Resolver->is_resolving())
GlobalLogger.warning() << LogPrefix << "have up to date IPs but "
<< "resolver seems to be resolving all the same... "
<< "Start pinging anyway!";
<< "with success = " << was_success << " "
<< "and cname_count = " << cname_count;
- // TODO this is too simple, but need to think more about how to update here!
- // (may have to switch back some time to resolver for original host or so
- ContinueOnOutdatedIps = !was_success;
- update_log_prefix();
-
if ( was_success )
{
HostAnalyzer.set_resolved_ip_count( Resolver->get_resolved_ip_count());
else
{ // host name resolution failed; try again bypassing first outdated CNAME
// or using cached IP
+ ContinueOnOutdatedIps = true;
+ update_log_prefix();
std::string skip_host = Resolver->get_skip_cname();
GlobalLogger.notice() << LogPrefix << "DNS failed, "
<< "try again skipping a CNAME and resolving "
<< skip_host << " directly";
+
+ cancel_resolve(false);
+
+ // now create new resolver
Resolver = DnsMaster::get_instance()
->get_resolver_for(skip_host, *ProtocolIter);
start_resolving_ping_address();
-
- // (the original resolver is still alive and cached by DnsMaster and
- // counting down time to re-try on its own until cancel_resolve)
}
}
}
+
+/**
+ * cancel resolver if force_cancel or if it is not resolving DestinationAddress
+ *
+ * Resolvers have a life on their own: they are cached by DnsMaster so never go
+ * out of scope and even after calling callbacks, there might still be a
+ * longterm timer active to re-try resolving.
+ * We want to cancel that long-term timer only if the Resolver is not for our
+ * real, original DestinationAddress but a CNAME, which can happen when trying
+ * to skip cnames and working on out-dated IPs
+ */
+void PingScheduler::cancel_resolve(const bool force_cancel)
+{
+ if (force_cancel)
+ {
+ GlobalLogger.info() << "Cancelling resolver (forced)";
+ Resolver->cancel_resolve();
+ }
+ else if ( Resolver->get_hostname() == DestinationAddress )
+ GlobalLogger.info() << LogPrefix
+ << "Leave original resolver active in background";
+ else
+ {
+ GlobalLogger.info() << LogPrefix << "Cancel resolver for "
+ << Resolver->get_hostname() << " since is not the original "
+ << DestinationAddress;
+ Resolver->cancel_resolve();
+ }
+}
+
void start_resolving_ping_address();
void update_log_prefix();
+ void cancel_resolve(const bool force_cancel);
//
// Attributes