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