594 lines
21 KiB
C++
594 lines
21 KiB
C++
// Copyright 2020 The Chromium Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "net/dns/resolve_context.h"
|
|
|
|
#include <cstdlib>
|
|
#include <limits>
|
|
#include <utility>
|
|
|
|
#include "base/check_op.h"
|
|
#include "base/containers/contains.h"
|
|
#include "base/metrics/bucket_ranges.h"
|
|
#include "base/metrics/histogram.h"
|
|
#include "base/metrics/histogram_base.h"
|
|
#include "base/metrics/histogram_functions.h"
|
|
#include "base/metrics/histogram_macros.h"
|
|
#include "base/metrics/sample_vector.h"
|
|
#include "base/no_destructor.h"
|
|
#include "base/numerics/safe_conversions.h"
|
|
#include "base/observer_list.h"
|
|
#include "base/ranges/algorithm.h"
|
|
#include "base/strings/stringprintf.h"
|
|
#include "net/base/features.h"
|
|
#include "net/base/ip_address.h"
|
|
#include "net/base/network_change_notifier.h"
|
|
#include "net/dns/dns_server_iterator.h"
|
|
#include "net/dns/dns_session.h"
|
|
#include "net/dns/dns_util.h"
|
|
#include "net/dns/host_cache.h"
|
|
#include "net/dns/public/dns_over_https_config.h"
|
|
#include "net/dns/public/doh_provider_entry.h"
|
|
#include "net/url_request/url_request_context.h"
|
|
|
|
namespace net {
|
|
|
|
namespace {
|
|
|
|
// Min fallback period between queries, in case we are talking to a local DNS
|
|
// proxy.
|
|
const base::TimeDelta kMinFallbackPeriod = base::Milliseconds(10);
|
|
|
|
// Default maximum fallback period between queries, even with exponential
|
|
// backoff. (Can be overridden by field trial.)
|
|
const base::TimeDelta kDefaultMaxFallbackPeriod = base::Seconds(5);
|
|
|
|
// Maximum RTT that will fit in the RTT histograms.
|
|
const base::TimeDelta kRttMax = base::Seconds(30);
|
|
// Number of buckets in the histogram of observed RTTs.
|
|
const size_t kRttBucketCount = 350;
|
|
// Target percentile in the RTT histogram used for fallback period.
|
|
const int kRttPercentile = 99;
|
|
// Number of samples to seed the histogram with.
|
|
const base::HistogramBase::Count kNumSeeds = 2;
|
|
|
|
DohProviderEntry::List FindDohProvidersMatchingServerConfig(
|
|
DnsOverHttpsServerConfig server_config) {
|
|
DohProviderEntry::List matching_entries;
|
|
for (const DohProviderEntry* entry : DohProviderEntry::GetList()) {
|
|
if (entry->doh_server_config == server_config)
|
|
matching_entries.push_back(entry);
|
|
}
|
|
|
|
return matching_entries;
|
|
}
|
|
|
|
DohProviderEntry::List FindDohProvidersAssociatedWithAddress(
|
|
IPAddress server_address) {
|
|
DohProviderEntry::List matching_entries;
|
|
for (const DohProviderEntry* entry : DohProviderEntry::GetList()) {
|
|
if (entry->ip_addresses.count(server_address) > 0)
|
|
matching_entries.push_back(entry);
|
|
}
|
|
|
|
return matching_entries;
|
|
}
|
|
|
|
base::TimeDelta GetDefaultFallbackPeriod(const DnsConfig& config) {
|
|
NetworkChangeNotifier::ConnectionType type =
|
|
NetworkChangeNotifier::GetConnectionType();
|
|
return GetTimeDeltaForConnectionTypeFromFieldTrialOrDefault(
|
|
"AsyncDnsInitialTimeoutMsByConnectionType", config.fallback_period, type);
|
|
}
|
|
|
|
base::TimeDelta GetMaxFallbackPeriod() {
|
|
NetworkChangeNotifier::ConnectionType type =
|
|
NetworkChangeNotifier::GetConnectionType();
|
|
return GetTimeDeltaForConnectionTypeFromFieldTrialOrDefault(
|
|
"AsyncDnsMaxTimeoutMsByConnectionType", kDefaultMaxFallbackPeriod, type);
|
|
}
|
|
|
|
class RttBuckets : public base::BucketRanges {
|
|
public:
|
|
RttBuckets() : base::BucketRanges(kRttBucketCount + 1) {
|
|
base::Histogram::InitializeBucketRanges(
|
|
1,
|
|
base::checked_cast<base::HistogramBase::Sample>(
|
|
kRttMax.InMilliseconds()),
|
|
this);
|
|
}
|
|
};
|
|
|
|
static RttBuckets* GetRttBuckets() {
|
|
static base::NoDestructor<RttBuckets> buckets;
|
|
return buckets.get();
|
|
}
|
|
|
|
static std::unique_ptr<base::SampleVector> GetRttHistogram(
|
|
base::TimeDelta rtt_estimate) {
|
|
std::unique_ptr<base::SampleVector> histogram =
|
|
std::make_unique<base::SampleVector>(GetRttBuckets());
|
|
// Seed histogram with 2 samples at |rtt_estimate|.
|
|
histogram->Accumulate(base::checked_cast<base::HistogramBase::Sample>(
|
|
rtt_estimate.InMilliseconds()),
|
|
kNumSeeds);
|
|
return histogram;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
ResolveContext::ServerStats::ServerStats(
|
|
std::unique_ptr<base::SampleVector> buckets)
|
|
: rtt_histogram(std::move(buckets)) {}
|
|
|
|
ResolveContext::ServerStats::ServerStats(ServerStats&&) = default;
|
|
|
|
ResolveContext::ServerStats::~ServerStats() = default;
|
|
|
|
ResolveContext::ResolveContext(URLRequestContext* url_request_context,
|
|
bool enable_caching)
|
|
: url_request_context_(url_request_context),
|
|
host_cache_(enable_caching ? HostCache::CreateDefaultCache() : nullptr),
|
|
isolation_info_(IsolationInfo::CreateTransient()) {
|
|
max_fallback_period_ = GetMaxFallbackPeriod();
|
|
}
|
|
|
|
ResolveContext::~ResolveContext() = default;
|
|
|
|
std::unique_ptr<DnsServerIterator> ResolveContext::GetDohIterator(
|
|
const DnsConfig& config,
|
|
const SecureDnsMode& mode,
|
|
const DnsSession* session) {
|
|
// Make the iterator even if the session differs. The first call to the member
|
|
// functions will catch the out of date session.
|
|
|
|
return std::make_unique<DohDnsServerIterator>(
|
|
doh_server_stats_.size(), FirstServerIndex(true, session),
|
|
config.doh_attempts, config.attempts, mode, this, session);
|
|
}
|
|
|
|
std::unique_ptr<DnsServerIterator> ResolveContext::GetClassicDnsIterator(
|
|
const DnsConfig& config,
|
|
const DnsSession* session) {
|
|
// Make the iterator even if the session differs. The first call to the member
|
|
// functions will catch the out of date session.
|
|
|
|
return std::make_unique<ClassicDnsServerIterator>(
|
|
config.nameservers.size(), FirstServerIndex(false, session),
|
|
config.attempts, config.attempts, this, session);
|
|
}
|
|
|
|
bool ResolveContext::GetDohServerAvailability(size_t doh_server_index,
|
|
const DnsSession* session) const {
|
|
if (!IsCurrentSession(session))
|
|
return false;
|
|
|
|
CHECK_LT(doh_server_index, doh_server_stats_.size());
|
|
return ServerStatsToDohAvailability(doh_server_stats_[doh_server_index]);
|
|
}
|
|
|
|
size_t ResolveContext::NumAvailableDohServers(const DnsSession* session) const {
|
|
if (!IsCurrentSession(session))
|
|
return 0;
|
|
|
|
return base::ranges::count_if(doh_server_stats_,
|
|
&ServerStatsToDohAvailability);
|
|
}
|
|
|
|
void ResolveContext::RecordServerFailure(size_t server_index,
|
|
bool is_doh_server,
|
|
int rv,
|
|
const DnsSession* session) {
|
|
DCHECK(rv != OK && rv != ERR_NAME_NOT_RESOLVED && rv != ERR_IO_PENDING);
|
|
|
|
if (!IsCurrentSession(session))
|
|
return;
|
|
|
|
// "FailureError" metric is only recorded for secure queries.
|
|
if (is_doh_server) {
|
|
std::string query_type =
|
|
GetQueryTypeForUma(server_index, true /* is_doh_server */, session);
|
|
DCHECK_NE(query_type, "Insecure");
|
|
std::string provider_id =
|
|
GetDohProviderIdForUma(server_index, true /* is_doh_server */, session);
|
|
|
|
base::UmaHistogramSparse(
|
|
base::StringPrintf("Net.DNS.DnsTransaction.%s.%s.FailureError",
|
|
query_type.c_str(), provider_id.c_str()),
|
|
std::abs(rv));
|
|
}
|
|
|
|
size_t num_available_doh_servers_before = NumAvailableDohServers(session);
|
|
|
|
ServerStats* stats = GetServerStats(server_index, is_doh_server);
|
|
++(stats->last_failure_count);
|
|
stats->last_failure = base::TimeTicks::Now();
|
|
|
|
size_t num_available_doh_servers_now = NumAvailableDohServers(session);
|
|
if (num_available_doh_servers_now < num_available_doh_servers_before) {
|
|
NotifyDohStatusObserversOfUnavailable(false /* network_change */);
|
|
|
|
// TODO(crbug.com/1022059): Consider figuring out some way to only for the
|
|
// first context enabling DoH or the last context disabling DoH.
|
|
if (num_available_doh_servers_now == 0)
|
|
NetworkChangeNotifier::TriggerNonSystemDnsChange();
|
|
}
|
|
}
|
|
|
|
void ResolveContext::RecordServerSuccess(size_t server_index,
|
|
bool is_doh_server,
|
|
const DnsSession* session) {
|
|
if (!IsCurrentSession(session))
|
|
return;
|
|
|
|
bool doh_available_before = NumAvailableDohServers(session) > 0;
|
|
|
|
ServerStats* stats = GetServerStats(server_index, is_doh_server);
|
|
stats->last_failure_count = 0;
|
|
stats->current_connection_success = true;
|
|
stats->last_failure = base::TimeTicks();
|
|
stats->last_success = base::TimeTicks::Now();
|
|
|
|
// TODO(crbug.com/1022059): Consider figuring out some way to only for the
|
|
// first context enabling DoH or the last context disabling DoH.
|
|
bool doh_available_now = NumAvailableDohServers(session) > 0;
|
|
if (doh_available_before != doh_available_now)
|
|
NetworkChangeNotifier::TriggerNonSystemDnsChange();
|
|
}
|
|
|
|
void ResolveContext::RecordRtt(size_t server_index,
|
|
bool is_doh_server,
|
|
base::TimeDelta rtt,
|
|
int rv,
|
|
const DnsSession* session) {
|
|
if (!IsCurrentSession(session))
|
|
return;
|
|
|
|
ServerStats* stats = GetServerStats(server_index, is_doh_server);
|
|
|
|
base::TimeDelta base_fallback_period =
|
|
NextFallbackPeriodHelper(stats, 0 /* num_backoffs */);
|
|
RecordRttForUma(server_index, is_doh_server, rtt, rv, base_fallback_period,
|
|
session);
|
|
|
|
// RTT values shouldn't be less than 0, but it shouldn't cause a crash if
|
|
// they are anyway, so clip to 0. See https://crbug.com/753568.
|
|
if (rtt.is_negative())
|
|
rtt = base::TimeDelta();
|
|
|
|
// Histogram-based method.
|
|
stats->rtt_histogram->Accumulate(
|
|
base::saturated_cast<base::HistogramBase::Sample>(rtt.InMilliseconds()),
|
|
1);
|
|
}
|
|
|
|
base::TimeDelta ResolveContext::NextClassicFallbackPeriod(
|
|
size_t classic_server_index,
|
|
int attempt,
|
|
const DnsSession* session) {
|
|
if (!IsCurrentSession(session))
|
|
return std::min(GetDefaultFallbackPeriod(session->config()),
|
|
max_fallback_period_);
|
|
|
|
return NextFallbackPeriodHelper(
|
|
GetServerStats(classic_server_index, false /* is _doh_server */),
|
|
attempt / current_session_->config().nameservers.size());
|
|
}
|
|
|
|
base::TimeDelta ResolveContext::NextDohFallbackPeriod(
|
|
size_t doh_server_index,
|
|
const DnsSession* session) {
|
|
if (!IsCurrentSession(session))
|
|
return std::min(GetDefaultFallbackPeriod(session->config()),
|
|
max_fallback_period_);
|
|
|
|
return NextFallbackPeriodHelper(
|
|
GetServerStats(doh_server_index, true /* is _doh_server */),
|
|
0 /* num_backoffs */);
|
|
}
|
|
|
|
base::TimeDelta ResolveContext::ClassicTransactionTimeout(
|
|
const DnsSession* session) {
|
|
if (!IsCurrentSession(session))
|
|
return features::kDnsMinTransactionTimeout.Get();
|
|
|
|
// Should not need to call if there are no classic servers configured.
|
|
DCHECK(!classic_server_stats_.empty());
|
|
|
|
return TransactionTimeoutHelper(classic_server_stats_.cbegin(),
|
|
classic_server_stats_.cend());
|
|
}
|
|
|
|
base::TimeDelta ResolveContext::SecureTransactionTimeout(
|
|
SecureDnsMode secure_dns_mode,
|
|
const DnsSession* session) {
|
|
// Currently only implemented for Secure mode as other modes are assumed to
|
|
// always use aggressive timeouts. If that ever changes, need to implement
|
|
// only accounting for available DoH servers when not Secure mode.
|
|
DCHECK_EQ(secure_dns_mode, SecureDnsMode::kSecure);
|
|
|
|
if (!IsCurrentSession(session))
|
|
return features::kDnsMinTransactionTimeout.Get();
|
|
|
|
// Should not need to call if there are no DoH servers configured.
|
|
DCHECK(!doh_server_stats_.empty());
|
|
|
|
return TransactionTimeoutHelper(doh_server_stats_.cbegin(),
|
|
doh_server_stats_.cend());
|
|
}
|
|
|
|
void ResolveContext::RegisterDohStatusObserver(DohStatusObserver* observer) {
|
|
DCHECK(observer);
|
|
doh_status_observers_.AddObserver(observer);
|
|
}
|
|
|
|
void ResolveContext::UnregisterDohStatusObserver(
|
|
const DohStatusObserver* observer) {
|
|
DCHECK(observer);
|
|
doh_status_observers_.RemoveObserver(observer);
|
|
}
|
|
|
|
void ResolveContext::InvalidateCachesAndPerSessionData(
|
|
const DnsSession* new_session,
|
|
bool network_change) {
|
|
// Network-bound ResolveContexts should never receive a cache invalidation due
|
|
// to a network change.
|
|
DCHECK(GetTargetNetwork() == handles::kInvalidNetworkHandle ||
|
|
!network_change);
|
|
if (host_cache_)
|
|
host_cache_->Invalidate();
|
|
|
|
// DNS config is constant for any given session, so if the current session is
|
|
// unchanged, any per-session data is safe to keep, even if it's dependent on
|
|
// a specific config.
|
|
if (new_session && new_session == current_session_.get())
|
|
return;
|
|
|
|
current_session_.reset();
|
|
classic_server_stats_.clear();
|
|
doh_server_stats_.clear();
|
|
initial_fallback_period_ = base::TimeDelta();
|
|
max_fallback_period_ = GetMaxFallbackPeriod();
|
|
|
|
if (!new_session) {
|
|
NotifyDohStatusObserversOfSessionChanged();
|
|
return;
|
|
}
|
|
|
|
current_session_ = new_session->GetWeakPtr();
|
|
|
|
initial_fallback_period_ =
|
|
GetDefaultFallbackPeriod(current_session_->config());
|
|
|
|
for (size_t i = 0; i < new_session->config().nameservers.size(); ++i) {
|
|
classic_server_stats_.emplace_back(
|
|
GetRttHistogram(initial_fallback_period_));
|
|
}
|
|
for (size_t i = 0; i < new_session->config().doh_config.servers().size();
|
|
++i) {
|
|
doh_server_stats_.emplace_back(GetRttHistogram(initial_fallback_period_));
|
|
}
|
|
|
|
CHECK_EQ(new_session->config().nameservers.size(),
|
|
classic_server_stats_.size());
|
|
CHECK_EQ(new_session->config().doh_config.servers().size(),
|
|
doh_server_stats_.size());
|
|
|
|
NotifyDohStatusObserversOfSessionChanged();
|
|
|
|
if (!doh_server_stats_.empty())
|
|
NotifyDohStatusObserversOfUnavailable(network_change);
|
|
}
|
|
|
|
handles::NetworkHandle ResolveContext::GetTargetNetwork() const {
|
|
if (!url_request_context())
|
|
return handles::kInvalidNetworkHandle;
|
|
|
|
return url_request_context()->bound_network();
|
|
}
|
|
|
|
size_t ResolveContext::FirstServerIndex(bool doh_server,
|
|
const DnsSession* session) {
|
|
if (!IsCurrentSession(session))
|
|
return 0u;
|
|
|
|
// DoH first server doesn't rotate, so always return 0u.
|
|
if (doh_server)
|
|
return 0u;
|
|
|
|
size_t index = classic_server_index_;
|
|
if (current_session_->config().rotate) {
|
|
classic_server_index_ = (classic_server_index_ + 1) %
|
|
current_session_->config().nameservers.size();
|
|
}
|
|
return index;
|
|
}
|
|
|
|
bool ResolveContext::IsCurrentSession(const DnsSession* session) const {
|
|
CHECK(session);
|
|
if (session == current_session_.get()) {
|
|
CHECK_EQ(current_session_->config().nameservers.size(),
|
|
classic_server_stats_.size());
|
|
CHECK_EQ(current_session_->config().doh_config.servers().size(),
|
|
doh_server_stats_.size());
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
ResolveContext::ServerStats* ResolveContext::GetServerStats(
|
|
size_t server_index,
|
|
bool is_doh_server) {
|
|
if (!is_doh_server) {
|
|
CHECK_LT(server_index, classic_server_stats_.size());
|
|
return &classic_server_stats_[server_index];
|
|
} else {
|
|
CHECK_LT(server_index, doh_server_stats_.size());
|
|
return &doh_server_stats_[server_index];
|
|
}
|
|
}
|
|
|
|
base::TimeDelta ResolveContext::NextFallbackPeriodHelper(
|
|
const ServerStats* server_stats,
|
|
int num_backoffs) {
|
|
// Respect initial fallback period (from config or field trial) if it exceeds
|
|
// max.
|
|
if (initial_fallback_period_ > max_fallback_period_)
|
|
return initial_fallback_period_;
|
|
|
|
static_assert(std::numeric_limits<base::HistogramBase::Count>::is_signed,
|
|
"histogram base count assumed to be signed");
|
|
|
|
// Use fixed percentile of observed samples.
|
|
const base::SampleVector& samples = *server_stats->rtt_histogram;
|
|
|
|
base::HistogramBase::Count total = samples.TotalCount();
|
|
base::HistogramBase::Count remaining_count = kRttPercentile * total / 100;
|
|
size_t index = 0;
|
|
while (remaining_count > 0 && index < GetRttBuckets()->size()) {
|
|
remaining_count -= samples.GetCountAtIndex(index);
|
|
++index;
|
|
}
|
|
|
|
base::TimeDelta fallback_period =
|
|
base::Milliseconds(GetRttBuckets()->range(index));
|
|
|
|
fallback_period = std::max(fallback_period, kMinFallbackPeriod);
|
|
|
|
return std::min(fallback_period * (1 << num_backoffs), max_fallback_period_);
|
|
}
|
|
|
|
template <typename Iterator>
|
|
base::TimeDelta ResolveContext::TransactionTimeoutHelper(
|
|
Iterator server_stats_begin,
|
|
Iterator server_stats_end) {
|
|
DCHECK_GE(features::kDnsMinTransactionTimeout.Get(), base::TimeDelta());
|
|
DCHECK_GE(features::kDnsTransactionTimeoutMultiplier.Get(), 0.0);
|
|
|
|
// Expect at least one configured server.
|
|
DCHECK(server_stats_begin != server_stats_end);
|
|
|
|
base::TimeDelta shortest_fallback_period = base::TimeDelta::Max();
|
|
for (Iterator server_stats = server_stats_begin;
|
|
server_stats != server_stats_end; ++server_stats) {
|
|
shortest_fallback_period = std::min(
|
|
shortest_fallback_period,
|
|
NextFallbackPeriodHelper(&*server_stats, 0 /* num_backoffs */));
|
|
}
|
|
|
|
DCHECK_GE(shortest_fallback_period, base::TimeDelta());
|
|
base::TimeDelta ratio_based_timeout =
|
|
shortest_fallback_period *
|
|
features::kDnsTransactionTimeoutMultiplier.Get();
|
|
|
|
return std::max(features::kDnsMinTransactionTimeout.Get(),
|
|
ratio_based_timeout);
|
|
}
|
|
|
|
void ResolveContext::RecordRttForUma(size_t server_index,
|
|
bool is_doh_server,
|
|
base::TimeDelta rtt,
|
|
int rv,
|
|
base::TimeDelta base_fallback_period,
|
|
const DnsSession* session) {
|
|
DCHECK(IsCurrentSession(session));
|
|
|
|
std::string query_type =
|
|
GetQueryTypeForUma(server_index, is_doh_server, session);
|
|
std::string provider_id =
|
|
GetDohProviderIdForUma(server_index, is_doh_server, session);
|
|
|
|
// Skip metrics for SecureNotValidated queries unless the provider is tagged
|
|
// for extra logging.
|
|
if (query_type == "SecureNotValidated" &&
|
|
!GetProviderUseExtraLogging(server_index, is_doh_server, session)) {
|
|
return;
|
|
}
|
|
|
|
if (rv == OK || rv == ERR_NAME_NOT_RESOLVED) {
|
|
base::UmaHistogramMediumTimes(
|
|
base::StringPrintf("Net.DNS.DnsTransaction.%s.%s.SuccessTime",
|
|
query_type.c_str(), provider_id.c_str()),
|
|
rtt);
|
|
} else {
|
|
base::UmaHistogramMediumTimes(
|
|
base::StringPrintf("Net.DNS.DnsTransaction.%s.%s.FailureTime",
|
|
query_type.c_str(), provider_id.c_str()),
|
|
rtt);
|
|
}
|
|
}
|
|
|
|
std::string ResolveContext::GetQueryTypeForUma(size_t server_index,
|
|
bool is_doh_server,
|
|
const DnsSession* session) {
|
|
DCHECK(IsCurrentSession(session));
|
|
|
|
if (!is_doh_server)
|
|
return "Insecure";
|
|
|
|
// Secure queries are validated if the DoH server state is available.
|
|
if (GetDohServerAvailability(server_index, session))
|
|
return "SecureValidated";
|
|
|
|
return "SecureNotValidated";
|
|
}
|
|
|
|
std::string ResolveContext::GetDohProviderIdForUma(size_t server_index,
|
|
bool is_doh_server,
|
|
const DnsSession* session) {
|
|
DCHECK(IsCurrentSession(session));
|
|
|
|
if (is_doh_server) {
|
|
return GetDohProviderIdForHistogramFromServerConfig(
|
|
session->config().doh_config.servers()[server_index]);
|
|
}
|
|
|
|
return GetDohProviderIdForHistogramFromNameserver(
|
|
session->config().nameservers[server_index]);
|
|
}
|
|
|
|
bool ResolveContext::GetProviderUseExtraLogging(size_t server_index,
|
|
bool is_doh_server,
|
|
const DnsSession* session) {
|
|
DCHECK(IsCurrentSession(session));
|
|
|
|
DohProviderEntry::List matching_entries;
|
|
if (is_doh_server) {
|
|
const DnsOverHttpsServerConfig& server_config =
|
|
session->config().doh_config.servers()[server_index];
|
|
matching_entries = FindDohProvidersMatchingServerConfig(server_config);
|
|
} else {
|
|
IPAddress server_address =
|
|
session->config().nameservers[server_index].address();
|
|
matching_entries = FindDohProvidersAssociatedWithAddress(server_address);
|
|
}
|
|
|
|
// Use extra logging if any matching provider entries have
|
|
// `LoggingLevel::kExtra` set.
|
|
return base::Contains(matching_entries,
|
|
DohProviderEntry::LoggingLevel::kExtra,
|
|
&DohProviderEntry::logging_level);
|
|
}
|
|
|
|
void ResolveContext::NotifyDohStatusObserversOfSessionChanged() {
|
|
for (auto& observer : doh_status_observers_)
|
|
observer.OnSessionChanged();
|
|
}
|
|
|
|
void ResolveContext::NotifyDohStatusObserversOfUnavailable(
|
|
bool network_change) {
|
|
for (auto& observer : doh_status_observers_)
|
|
observer.OnDohServerUnavailable(network_change);
|
|
}
|
|
|
|
// static
|
|
bool ResolveContext::ServerStatsToDohAvailability(
|
|
const ResolveContext::ServerStats& stats) {
|
|
return stats.last_failure_count < kAutomaticModeFailureLimit &&
|
|
stats.current_connection_success;
|
|
}
|
|
|
|
} // namespace net
|