forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
dns_server_iterator.cc
162 lines (131 loc) · 5.81 KB
/
dns_server_iterator.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "net/dns/dns_server_iterator.h"
#include "base/time/time.h"
#include "net/dns/dns_session.h"
#include "net/dns/resolve_context.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace net {
DnsServerIterator::DnsServerIterator(size_t nameservers_size,
size_t starting_index,
int max_times_returned,
int max_failures,
const ResolveContext* resolve_context,
const DnsSession* session)
: times_returned_(nameservers_size, 0),
max_times_returned_(max_times_returned),
max_failures_(max_failures),
resolve_context_(resolve_context),
next_index_(starting_index),
session_(session) {}
DnsServerIterator::~DnsServerIterator() = default;
size_t DohDnsServerIterator::GetNextAttemptIndex() {
DCHECK(resolve_context_->IsCurrentSession(session_));
DCHECK(AttemptAvailable());
// Because AttemptAvailable() should always be true before running this
// function we can assume that an attemptable DoH server exists.
// Check if the next index is available and hasn't hit its failure limit. If
// not, try the next one and so on until we've tried them all.
absl::optional<size_t> least_recently_failed_index;
base::TimeTicks least_recently_failed_time;
size_t previous_index = next_index_;
size_t curr_index;
do {
curr_index = next_index_;
next_index_ = (next_index_ + 1) % times_returned_.size();
// If the DoH mode is "secure" then don't check GetDohServerAvailability()
// because we try every server regardless of availability.
bool secure_or_available_server =
secure_dns_mode_ == SecureDnsMode::kSecure ||
resolve_context_->GetDohServerAvailability(curr_index, session_);
// If we've tried this server |max_times_returned_| already, then we're done
// with it. Similarly skip this server if it isn't available and we're not
// in secure mode.
if (times_returned_[curr_index] >= max_times_returned_ ||
!secure_or_available_server)
continue;
if (resolve_context_->doh_server_stats_[curr_index].last_failure_count <
max_failures_) {
times_returned_[curr_index]++;
return curr_index;
}
// Update the least recently failed server if needed.
base::TimeTicks curr_index_failure_time =
resolve_context_->doh_server_stats_[curr_index].last_failure;
if (!least_recently_failed_index ||
curr_index_failure_time < least_recently_failed_time) {
least_recently_failed_time = curr_index_failure_time;
least_recently_failed_index = curr_index;
}
} while (next_index_ != previous_index);
// At this point the only available servers we haven't attempted
// |max_times_returned_| times are at their failure limit. Return the server
// with the least recent failure.
DCHECK(least_recently_failed_index.has_value());
times_returned_[least_recently_failed_index.value()]++;
return least_recently_failed_index.value();
}
bool DohDnsServerIterator::AttemptAvailable() {
if (!resolve_context_->IsCurrentSession(session_))
return false;
for (size_t i = 0; i < times_returned_.size(); i++) {
// If the DoH mode is "secure" then don't check GetDohServerAvailability()
// because we try every server regardless of availability.
bool secure_or_available_server =
secure_dns_mode_ == SecureDnsMode::kSecure ||
resolve_context_->GetDohServerAvailability(i, session_);
if (times_returned_[i] < max_times_returned_ && secure_or_available_server)
return true;
}
return false;
}
size_t ClassicDnsServerIterator::GetNextAttemptIndex() {
DCHECK(resolve_context_->IsCurrentSession(session_));
DCHECK(AttemptAvailable());
// Because AttemptAvailable() should always be true before running this
// function we can assume that an attemptable DNS server exists.
// Check if the next index is available and hasn't hit its failure limit. If
// not, try the next one and so on until we've tried them all.
absl::optional<size_t> least_recently_failed_index;
base::TimeTicks least_recently_failed_time;
size_t previous_index = next_index_;
size_t curr_index;
do {
curr_index = next_index_;
next_index_ = (next_index_ + 1) % times_returned_.size();
// If we've tried this server |max_times_returned_| already, then we're done
// with it.
if (times_returned_[curr_index] >= max_times_returned_)
continue;
if (resolve_context_->classic_server_stats_[curr_index].last_failure_count <
max_failures_) {
times_returned_[curr_index]++;
return curr_index;
}
// Update the least recently failed server if needed.
base::TimeTicks curr_index_failure_time =
resolve_context_->classic_server_stats_[curr_index].last_failure;
if (!least_recently_failed_index ||
curr_index_failure_time < least_recently_failed_time) {
least_recently_failed_time = curr_index_failure_time;
least_recently_failed_index = curr_index;
}
} while (next_index_ != previous_index);
// At this point the only servers we haven't attempted |max_times_returned_|
// times are at their failure limit. Return the server with the least recent
// failure.
DCHECK(least_recently_failed_index.has_value());
times_returned_[least_recently_failed_index.value()]++;
return least_recently_failed_index.value();
}
bool ClassicDnsServerIterator::AttemptAvailable() {
if (!resolve_context_->IsCurrentSession(session_))
return false;
for (int i : times_returned_) {
if (i < max_times_returned_)
return true;
}
return false;
}
} // namespace net