blob: e5542757853bf85f0be1ee50c7c439c1f4ecf081 [file] [log] [blame]
[email protected]d0d49dd82012-01-26 00:03:591// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]7556ea22011-12-08 19:29:152// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/dns/dns_session.h"
6
avi65168052015-12-01 19:27:077#include <stdint.h>
danakj22f90e72016-04-16 01:55:408
avi65168052015-12-01 19:27:079#include <limits>
dchengc7eeda422015-12-26 03:56:4810#include <utility>
avi65168052015-12-01 19:27:0711
[email protected]7556ea22011-12-08 19:29:1512#include "base/bind.h"
[email protected]a6c84f42013-06-07 20:39:3813#include "base/lazy_instance.h"
Avi Drissman13fc8932015-12-20 04:40:4614#include "base/macros.h"
asvitkinec3c93722015-06-17 14:48:3715#include "base/metrics/histogram_macros.h"
[email protected]ae1b30b2013-05-23 23:06:0316#include "base/metrics/sample_vector.h"
[email protected]120d38d2012-12-14 01:42:3217#include "base/rand_util.h"
18#include "base/stl_util.h"
[email protected]66e96c42013-06-28 15:20:3119#include "base/time/time.h"
[email protected]7556ea22011-12-08 19:29:1520#include "net/base/ip_endpoint.h"
[email protected]120d38d2012-12-14 01:42:3221#include "net/base/net_errors.h"
dalykc2adf182019-09-02 14:31:2222#include "net/dns/dns_config.h"
[email protected]120d38d2012-12-14 01:42:3223#include "net/dns/dns_socket_pool.h"
ttuttlecf1158bf2016-03-18 16:37:4424#include "net/dns/dns_util.h"
mikecirone8b85c432016-09-08 19:11:0025#include "net/log/net_log_event_type.h"
mikecironef22f9812016-10-04 03:40:1926#include "net/log/net_log_source.h"
27#include "net/log/net_log_with_source.h"
tfarina5dd13c22016-11-16 12:08:2628#include "net/socket/datagram_client_socket.h"
[email protected]bdb65982012-12-20 20:44:5929#include "net/socket/stream_socket.h"
[email protected]7556ea22011-12-08 19:29:1530
31namespace net {
32
[email protected]ae1b30b2013-05-23 23:06:0333namespace {
ttuttlecf1158bf2016-03-18 16:37:4434
[email protected]ae1b30b2013-05-23 23:06:0335// Set min timeout, in case we are talking to a local DNS proxy.
36const unsigned kMinTimeoutMs = 10;
37
ttuttlecf1158bf2016-03-18 16:37:4438// Default maximum timeout between queries, even with exponential backoff.
39// (Can be overridden by field trial.)
40const unsigned kDefaultMaxTimeoutMs = 5000;
41
42// Maximum RTT that will fit in the RTT histograms.
43const int32_t kRTTMaxMs = 30000;
[email protected]ae1b30b2013-05-23 23:06:0344// Number of buckets in the histogram of observed RTTs.
ttuttlecf1158bf2016-03-18 16:37:4445const size_t kRTTBucketCount = 350;
[email protected]ae1b30b2013-05-23 23:06:0346// Target percentile in the RTT histogram used for retransmission timeout.
47const unsigned kRTOPercentile = 99;
dalykc2adf182019-09-02 14:31:2248// Number of samples to seed the histogram with.
49const unsigned kNumSeeds = 2;
ttuttlecf1158bf2016-03-18 16:37:4450
[email protected]ae1b30b2013-05-23 23:06:0351} // namespace
52
[email protected]a6c84f42013-06-07 20:39:3853// Runtime statistics of DNS server.
54struct DnsSession::ServerStats {
55 ServerStats(base::TimeDelta rtt_estimate_param, RttBuckets* buckets)
56 : last_failure_count(0), rtt_estimate(rtt_estimate_param) {
57 rtt_histogram.reset(new base::SampleVector(buckets));
[email protected]a144bd22013-07-29 21:53:1058 // Seed histogram with 2 samples at |rtt_estimate| timeout.
pkasting6b68a162014-12-01 22:10:2959 rtt_histogram->Accumulate(
60 static_cast<base::HistogramBase::Sample>(rtt_estimate.InMilliseconds()),
dalykc2adf182019-09-02 14:31:2261 kNumSeeds);
[email protected]a6c84f42013-06-07 20:39:3862 }
63
64 // Count of consecutive failures after last success.
65 int last_failure_count;
66
67 // Last time when server returned failure or timeout.
68 base::Time last_failure;
69 // Last time when server returned success.
70 base::Time last_success;
71
72 // Estimated RTT using moving average.
73 base::TimeDelta rtt_estimate;
74 // Estimated error in the above.
75 base::TimeDelta rtt_deviation;
76
77 // A histogram of observed RTT .
danakj22f90e72016-04-16 01:55:4078 std::unique_ptr<base::SampleVector> rtt_histogram;
[email protected]a6c84f42013-06-07 20:39:3879
80 DISALLOW_COPY_AND_ASSIGN(ServerStats);
81};
82
83// static
84base::LazyInstance<DnsSession::RttBuckets>::Leaky DnsSession::rtt_buckets_ =
85 LAZY_INSTANCE_INITIALIZER;
86
87DnsSession::RttBuckets::RttBuckets() : base::BucketRanges(kRTTBucketCount + 1) {
ttuttlecf1158bf2016-03-18 16:37:4488 base::Histogram::InitializeBucketRanges(1, kRTTMaxMs, this);
[email protected]a6c84f42013-06-07 20:39:3889}
90
danakj22f90e72016-04-16 01:55:4091DnsSession::SocketLease::SocketLease(
92 scoped_refptr<DnsSession> session,
93 unsigned server_index,
94 std::unique_ptr<DatagramClientSocket> socket)
dchengc7eeda422015-12-26 03:56:4895 : session_(session),
96 server_index_(server_index),
97 socket_(std::move(socket)) {}
[email protected]120d38d2012-12-14 01:42:3298
99DnsSession::SocketLease::~SocketLease() {
dchengc7eeda422015-12-26 03:56:48100 session_->FreeSocket(server_index_, std::move(socket_));
[email protected]120d38d2012-12-14 01:42:32101}
102
[email protected]7556ea22011-12-08 19:29:15103DnsSession::DnsSession(const DnsConfig& config,
danakj22f90e72016-04-16 01:55:40104 std::unique_ptr<DnsSocketPool> socket_pool,
[email protected]7556ea22011-12-08 19:29:15105 const RandIntCallback& rand_int_callback,
106 NetLog* net_log)
107 : config_(config),
dchengc7eeda422015-12-26 03:56:48108 socket_pool_(std::move(socket_pool)),
avi65168052015-12-01 19:27:07109 rand_callback_(base::Bind(rand_int_callback,
110 0,
111 std::numeric_limits<uint16_t>::max())),
[email protected]7556ea22011-12-08 19:29:15112 net_log_(net_log),
[email protected]a6c84f42013-06-07 20:39:38113 server_index_(0) {
[email protected]120d38d2012-12-14 01:42:32114 socket_pool_->Initialize(&config_.nameservers, net_log);
drbasicf0d1b262016-08-23 06:10:42115 UMA_HISTOGRAM_CUSTOM_COUNTS("AsyncDNS.ServerCount",
116 config_.nameservers.size(), 1, 10, 11);
ttuttlecf1158bf2016-03-18 16:37:44117 UpdateTimeouts(NetworkChangeNotifier::GetConnectionType());
118 InitializeServerStats();
[email protected]120d38d2012-12-14 01:42:32119}
120
dalykc2adf182019-09-02 14:31:22121DnsSession::~DnsSession() = default;
ttuttlecf1158bf2016-03-18 16:37:44122
123void DnsSession::UpdateTimeouts(NetworkChangeNotifier::ConnectionType type) {
124 initial_timeout_ = GetTimeDeltaForConnectionTypeFromFieldTrialOrDefault(
125 "AsyncDnsInitialTimeoutMsByConnectionType", config_.timeout, type);
126 max_timeout_ = GetTimeDeltaForConnectionTypeFromFieldTrialOrDefault(
127 "AsyncDnsMaxTimeoutMsByConnectionType",
128 base::TimeDelta::FromMilliseconds(kDefaultMaxTimeoutMs), type);
129}
130
131void DnsSession::InitializeServerStats() {
132 server_stats_.clear();
dalykc2adf182019-09-02 14:31:22133 for (size_t i = 0; i < config_.nameservers.size(); ++i) {
Jeremy Roman0579ed62017-08-29 15:56:19134 server_stats_.push_back(std::make_unique<ServerStats>(
ricea2deef682016-09-09 08:04:07135 initial_timeout_, rtt_buckets_.Pointer()));
ttuttlecf1158bf2016-03-18 16:37:44136 }
ttuttlecf1158bf2016-03-18 16:37:44137
dalykc2adf182019-09-02 14:31:22138 doh_server_stats_.clear();
139 for (size_t i = 0; i < config_.dns_over_https_servers.size(); ++i) {
140 doh_server_stats_.push_back(std::make_pair(
141 std::make_unique<ServerStats>(initial_timeout_, rtt_buckets_.Pointer()),
142 false));
ttuttlecf1158bf2016-03-18 16:37:44143 }
[email protected]a6c84f42013-06-07 20:39:38144}
[email protected]7556ea22011-12-08 19:29:15145
avi65168052015-12-01 19:27:07146uint16_t DnsSession::NextQueryId() const {
147 return static_cast<uint16_t>(rand_callback_.Run());
pkasting6b68a162014-12-01 22:10:29148}
[email protected]7556ea22011-12-08 19:29:15149
[email protected]a6c84f42013-06-07 20:39:38150unsigned DnsSession::NextFirstServerIndex() {
151 unsigned index = NextGoodServerIndex(server_index_);
[email protected]7556ea22011-12-08 19:29:15152 if (config_.rotate)
153 server_index_ = (server_index_ + 1) % config_.nameservers.size();
[email protected]d0d49dd82012-01-26 00:03:59154 return index;
[email protected]7556ea22011-12-08 19:29:15155}
156
[email protected]a6c84f42013-06-07 20:39:38157unsigned DnsSession::NextGoodServerIndex(unsigned server_index) {
Brad Lassey786929ad2018-02-21 20:54:27158 DCHECK_GE(server_index, 0u);
159 DCHECK_LT(server_index, config_.nameservers.size());
[email protected]a6c84f42013-06-07 20:39:38160 unsigned index = server_index;
161 base::Time oldest_server_failure(base::Time::Now());
162 unsigned oldest_server_failure_index = 0;
163
[email protected]a6c84f42013-06-07 20:39:38164 do {
[email protected]a6c84f42013-06-07 20:39:38165 // If number of failures on this server doesn't exceed number of allowed
166 // attempts, return its index.
167 if (server_stats_[server_index]->last_failure_count < config_.attempts) {
168 return index;
169 }
170 // Track oldest failed server.
dalykc2adf182019-09-02 14:31:22171 base::Time cur_server_failure = server_stats_[index]->last_failure;
[email protected]a6c84f42013-06-07 20:39:38172 if (cur_server_failure < oldest_server_failure) {
173 oldest_server_failure = cur_server_failure;
174 oldest_server_failure_index = index;
175 }
176 index = (index + 1) % config_.nameservers.size();
177 } while (index != server_index);
178
179 // If we are here it means that there are no successful servers, so we have
180 // to use one that has failed oldest.
181 return oldest_server_failure_index;
182}
183
dalykc2adf182019-09-02 14:31:22184int DnsSession::NextGoodDohServerIndex(
185 unsigned doh_server_index,
186 DnsConfig::SecureDnsMode secure_dns_mode) {
187 DCHECK_GE(doh_server_index, 0u);
188 DCHECK_LT(doh_server_index, config_.dns_over_https_servers.size());
189 unsigned index = doh_server_index;
Brad Lassey786929ad2018-02-21 20:54:27190 base::Time oldest_server_failure(base::Time::Now());
dalykc2adf182019-09-02 14:31:22191 int oldest_available_server_failure_index = -1;
Brad Lassey786929ad2018-02-21 20:54:27192
193 do {
dalykc2adf182019-09-02 14:31:22194 // For a server to be considered "available", the server must have a
195 // successful probe status if we are in AUTOMATIC mode.
196 if (secure_dns_mode == DnsConfig::SecureDnsMode::SECURE ||
197 doh_server_stats_[index].second) {
198 // If number of failures on this server doesn't exceed |config_.attempts|,
199 // return its index. |config_.attempts| will generally be more restrictive
200 // than |kAutomaticModeFailureLimit|, although this is not guaranteed.
201 const ServerStats* stats =
202 GetServerStats(index, true /* is_doh_server */);
203 if (stats->last_failure_count < config_.attempts) {
204 return index;
205 }
206 // Track oldest failed available server.
207 base::Time cur_server_failure = stats->last_failure;
208 if (cur_server_failure < oldest_server_failure) {
209 oldest_server_failure = cur_server_failure;
210 oldest_available_server_failure_index = index;
211 }
Brad Lassey786929ad2018-02-21 20:54:27212 }
dalykc2adf182019-09-02 14:31:22213 index = (index + 1) % config_.dns_over_https_servers.size();
214 } while (index != doh_server_index);
Brad Lassey786929ad2018-02-21 20:54:27215
dalykc2adf182019-09-02 14:31:22216 // If we are here it means that there are either no available DoH servers or
217 // that all available DoH servers have at least |config_.attempts| consecutive
218 // failures. In the latter case, we'll return the available DoH server that
219 // failed least recently. In the former case we return -1.
220 return oldest_available_server_failure_index;
Brad Lassey786929ad2018-02-21 20:54:27221}
222
dalykc2adf182019-09-02 14:31:22223bool DnsSession::HasAvailableDohServer() {
224 for (const auto& doh_stats_ : doh_server_stats_) {
225 if (doh_stats_.second)
226 return true;
227 }
228 return false;
[email protected]a6c84f42013-06-07 20:39:38229}
230
dalykc2adf182019-09-02 14:31:22231unsigned DnsSession::NumAvailableDohServers() {
232 unsigned count = 0;
233 for (const auto& doh_stats_ : doh_server_stats_) {
234 if (doh_stats_.second)
235 count++;
236 }
237 return count;
[email protected]a6c84f42013-06-07 20:39:38238}
239
dalykc2adf182019-09-02 14:31:22240DnsSession::ServerStats* DnsSession::GetServerStats(unsigned server_index,
241 bool is_doh_server) {
242 DCHECK_GE(server_index, 0u);
243 if (!is_doh_server) {
244 DCHECK_LT(server_index, config_.nameservers.size());
245 return server_stats_[server_index].get();
246 } else {
247 DCHECK_LT(server_index, config_.dns_over_https_servers.size());
248 return doh_server_stats_[server_index].first.get();
249 }
250}
251
252void DnsSession::RecordServerFailure(unsigned server_index,
253 bool is_doh_server) {
254 ServerStats* stats = GetServerStats(server_index, is_doh_server);
255 ++(stats->last_failure_count);
256 stats->last_failure = base::Time::Now();
257
258 if (is_doh_server &&
259 stats->last_failure_count >= kAutomaticModeFailureLimit) {
260 SetProbeSuccess(server_index, false /* success */);
261 }
262}
263
264void DnsSession::RecordServerSuccess(unsigned server_index,
265 bool is_doh_server) {
266 ServerStats* stats = GetServerStats(server_index, is_doh_server);
267
268 // DoH queries can be sent using more than one URLRequestContext. A success
269 // from one URLRequestContext shouldn't zero out failures that may be
270 // consistently occurring for another URLRequestContext.
271 if (!is_doh_server)
272 stats->last_failure_count = 0;
273 stats->last_failure = base::Time();
274 stats->last_success = base::Time::Now();
275}
276
277void DnsSession::SetProbeSuccess(unsigned doh_server_index, bool success) {
278 DCHECK_GE(doh_server_index, 0u);
279 DCHECK_LT(doh_server_index, config_.dns_over_https_servers.size());
280 doh_server_stats_[doh_server_index].second = success;
281}
282
283void DnsSession::RecordRTT(unsigned server_index,
284 bool is_doh_server,
285 bool is_probe,
286 base::TimeDelta rtt) {
287 ServerStats* stats = GetServerStats(server_index, is_doh_server);
288 // If the histogram has not yet been populated beyond the initial seed values
289 // and this was a probe query, replace the seed values with a multiple of
290 // the probe's RTT.
291 if (is_probe && stats->rtt_histogram->TotalCount() == kNumSeeds) {
292 DCHECK(is_doh_server);
293 doh_server_stats_[server_index].first = std::make_unique<ServerStats>(
294 rtt * kDohProbeTimeMultiplier, rtt_buckets_.Pointer());
295 return;
296 }
[email protected]ae1b30b2013-05-23 23:06:03297
[email protected]ae1b30b2013-05-23 23:06:03298 // Jacobson/Karels algorithm for TCP.
299 // Using parameters: alpha = 1/8, delta = 1/4, beta = 4
dalykc2adf182019-09-02 14:31:22300 base::TimeDelta& estimate = stats->rtt_estimate;
301 base::TimeDelta& deviation = stats->rtt_deviation;
[email protected]ae1b30b2013-05-23 23:06:03302 base::TimeDelta current_error = rtt - estimate;
303 estimate += current_error / 8; // * alpha
304 base::TimeDelta abs_error = base::TimeDelta::FromInternalValue(
305 std::abs(current_error.ToInternalValue()));
306 deviation += (abs_error - deviation) / 4; // * delta
307
dalykc2adf182019-09-02 14:31:22308 // RTT values shouldn't be less than 0, but it shouldn't cause a crash if
309 // they are anyway, so clip to 0. See https://crbug.com/753568.
Miriam Gershenson68378c62017-08-10 22:26:25310 int32_t rtt_ms = rtt.InMilliseconds();
311 if (rtt_ms < 0)
312 rtt_ms = 0;
313
[email protected]ae1b30b2013-05-23 23:06:03314 // Histogram-based method.
dalykc2adf182019-09-02 14:31:22315 stats->rtt_histogram->Accumulate(
Miriam Gershenson68378c62017-08-10 22:26:25316 static_cast<base::HistogramBase::Sample>(rtt_ms), 1);
[email protected]ae1b30b2013-05-23 23:06:03317}
318
[email protected]ae1b30b2013-05-23 23:06:03319base::TimeDelta DnsSession::NextTimeout(unsigned server_index, int attempt) {
dalykc2adf182019-09-02 14:31:22320 return NextTimeoutHelper(
321 GetServerStats(server_index, false /* is _doh_server */),
322 attempt / config_.nameservers.size());
323}
324
325base::TimeDelta DnsSession::NextDohTimeout(unsigned doh_server_index) {
326 return NextTimeoutHelper(
327 GetServerStats(doh_server_index, true /* is _doh_server */),
328 0 /* num_backoffs */);
329}
330
331base::TimeDelta DnsSession::NextTimeoutHelper(ServerStats* server_stats,
332 int num_backoffs) {
ttuttlecf1158bf2016-03-18 16:37:44333 // Respect initial timeout (from config or field trial) if it exceeds max.
334 if (initial_timeout_ > max_timeout_)
335 return initial_timeout_;
Paul Jensenf3593242018-12-04 19:15:18336
Paul Jensenf3593242018-12-04 19:15:18337 static_assert(std::numeric_limits<base::HistogramBase::Count>::is_signed,
338 "histogram base count assumed to be signed");
339
340 // Use fixed percentile of observed samples.
dalykc2adf182019-09-02 14:31:22341 const base::SampleVector& samples = *server_stats->rtt_histogram;
Paul Jensenf3593242018-12-04 19:15:18342
343 base::HistogramBase::Count total = samples.TotalCount();
344 base::HistogramBase::Count remaining_count = kRTOPercentile * total / 100;
345 size_t index = 0;
346 while (remaining_count > 0 && index < rtt_buckets_.Get().size()) {
347 remaining_count -= samples.GetCountAtIndex(index);
348 ++index;
349 }
350
351 base::TimeDelta timeout =
352 base::TimeDelta::FromMilliseconds(rtt_buckets_.Get().range(index));
353
354 timeout = std::max(timeout, base::TimeDelta::FromMilliseconds(kMinTimeoutMs));
355
Paul Jensenf3593242018-12-04 19:15:18356 return std::min(timeout * (1 << num_backoffs), max_timeout_);
[email protected]7556ea22011-12-08 19:29:15357}
358
[email protected]120d38d2012-12-14 01:42:32359// Allocate a socket, already connected to the server address.
danakj22f90e72016-04-16 01:55:40360std::unique_ptr<DnsSession::SocketLease> DnsSession::AllocateSocket(
361 unsigned server_index,
mikecironef22f9812016-10-04 03:40:19362 const NetLogSource& source) {
danakj22f90e72016-04-16 01:55:40363 std::unique_ptr<DatagramClientSocket> socket;
[email protected]120d38d2012-12-14 01:42:32364
365 socket = socket_pool_->AllocateSocket(server_index);
[email protected]dd946bb2013-06-12 22:53:01366 if (!socket.get())
danakj22f90e72016-04-16 01:55:40367 return std::unique_ptr<SocketLease>();
[email protected]120d38d2012-12-14 01:42:32368
Eric Roman06bd9742019-07-13 15:19:13369 socket->NetLog().BeginEventReferencingSource(NetLogEventType::SOCKET_IN_USE,
370 source);
[email protected]120d38d2012-12-14 01:42:32371
dchengc7eeda422015-12-26 03:56:48372 SocketLease* lease = new SocketLease(this, server_index, std::move(socket));
danakj22f90e72016-04-16 01:55:40373 return std::unique_ptr<SocketLease>(lease);
[email protected]120d38d2012-12-14 01:42:32374}
375
danakj22f90e72016-04-16 01:55:40376std::unique_ptr<StreamSocket> DnsSession::CreateTCPSocket(
377 unsigned server_index,
mikecironef22f9812016-10-04 03:40:19378 const NetLogSource& source) {
[email protected]bdb65982012-12-20 20:44:59379 return socket_pool_->CreateTCPSocket(server_index, source);
380}
381
[email protected]120d38d2012-12-14 01:42:32382// Release a socket.
[email protected]ae1b30b2013-05-23 23:06:03383void DnsSession::FreeSocket(unsigned server_index,
danakj22f90e72016-04-16 01:55:40384 std::unique_ptr<DatagramClientSocket> socket) {
[email protected]120d38d2012-12-14 01:42:32385 DCHECK(socket.get());
386
mikecirone8b85c432016-09-08 19:11:00387 socket->NetLog().EndEvent(NetLogEventType::SOCKET_IN_USE);
[email protected]120d38d2012-12-14 01:42:32388
dchengc7eeda422015-12-26 03:56:48389 socket_pool_->FreeSocket(server_index, std::move(socket));
[email protected]120d38d2012-12-14 01:42:32390}
[email protected]7556ea22011-12-08 19:29:15391
392} // namespace net