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