• 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 #ifndef NET_DNS_RESOLVE_CONTEXT_H_
6 #define NET_DNS_RESOLVE_CONTEXT_H_
7 
8 #include <memory>
9 #include <string>
10 #include <vector>
11 
12 #include "base/memory/raw_ptr.h"
13 #include "base/memory/safe_ref.h"
14 #include "base/memory/weak_ptr.h"
15 #include "base/metrics/sample_vector.h"
16 #include "base/observer_list.h"
17 #include "base/observer_list_types.h"
18 #include "base/time/time.h"
19 #include "net/base/isolation_info.h"
20 #include "net/base/net_export.h"
21 #include "net/base/network_handle.h"
22 #include "net/dns/dns_config.h"
23 #include "net/dns/public/secure_dns_mode.h"
24 
25 namespace net {
26 
27 class ClassicDnsServerIterator;
28 class DnsSession;
29 class DnsServerIterator;
30 class DohDnsServerIterator;
31 class HostCache;
32 class URLRequestContext;
33 
34 // Per-URLRequestContext data used by HostResolver. Expected to be owned by the
35 // ContextHostResolver, and all usage/references are expected to be cleaned up
36 // or cancelled before the URLRequestContext goes out of service.
37 class NET_EXPORT_PRIVATE ResolveContext : public base::CheckedObserver {
38  public:
39   // Number of failures allowed before a DoH server is designated 'unavailable'.
40   // In AUTOMATIC mode, non-probe DoH queries should not be sent to DoH servers
41   // that have reached this limit.
42   //
43   // This limit is different from the failure limit that governs insecure async
44   // resolver bypass in multiple ways: NXDOMAIN responses are never counted as
45   // failures, and the outcome of fallback queries is not taken into account.
46   static const int kAutomaticModeFailureLimit = 10;
47 
48   class DohStatusObserver : public base::CheckedObserver {
49    public:
50     // Notification indicating that the current session for which DoH servers
51     // are being tracked has changed.
52     virtual void OnSessionChanged() = 0;
53 
54     // Notification indicating that a DoH server has been marked unavailable,
55     // but is ready for usage such as availability probes.
56     //
57     // |network_change| true if the invalidation was triggered by a network
58     // connection change.
59     virtual void OnDohServerUnavailable(bool network_change) = 0;
60 
61    protected:
62     DohStatusObserver() = default;
63     ~DohStatusObserver() override = default;
64   };
65 
66   ResolveContext(URLRequestContext* url_request_context, bool enable_caching);
67 
68   ResolveContext(const ResolveContext&) = delete;
69   ResolveContext& operator=(const ResolveContext&) = delete;
70 
71   ~ResolveContext() override;
72 
73   // Returns an iterator for DoH DNS servers.
74   std::unique_ptr<DnsServerIterator> GetDohIterator(const DnsConfig& config,
75                                                     const SecureDnsMode& mode,
76                                                     const DnsSession* session);
77 
78   // Returns an iterator for classic DNS servers.
79   std::unique_ptr<DnsServerIterator> GetClassicDnsIterator(
80       const DnsConfig& config,
81       const DnsSession* session);
82 
83   // Returns whether |doh_server_index| is eligible for use in AUTOMATIC mode,
84   // that is that consecutive failures are less than kAutomaticModeFailureLimit
85   // and the server has had at least one successful query or probe. Always
86   // |false| if |session| is not the current session.
87   bool GetDohServerAvailability(size_t doh_server_index,
88                                 const DnsSession* session) const;
89 
90   // Returns the number of DoH servers available for use in AUTOMATIC mode (see
91   // GetDohServerAvailability()). Always 0 if |session| is not the current
92   // session.
93   size_t NumAvailableDohServers(const DnsSession* session) const;
94 
95   // Record failure to get a response from the server (e.g. SERVFAIL, connection
96   // failures, or that the server failed to respond before the fallback period
97   // elapsed. If |is_doh_server| and the number of failures has surpassed a
98   // threshold, sets the DoH probe state to unavailable. Noop if |session| is
99   // not the current session. Should only be called with with server failure
100   // |rv|s, not e.g. OK, ERR_NAME_NOT_RESOLVED (which at the transaction level
101   // is expected to be nxdomain), or ERR_IO_PENDING.
102   void RecordServerFailure(size_t server_index,
103                            bool is_doh_server,
104                            int rv,
105                            const DnsSession* session);
106 
107   // Record that server responded successfully. Noop if |session| is not the
108   // current session.
109   void RecordServerSuccess(size_t server_index,
110                            bool is_doh_server,
111                            const DnsSession* session);
112 
113   // Record how long it took to receive a response from the server. Noop if
114   // |session| is not the current session.
115   void RecordRtt(size_t server_index,
116                  bool is_doh_server,
117                  base::TimeDelta rtt,
118                  int rv,
119                  const DnsSession* session);
120 
121   // Return the period the next query should run before fallback to next
122   // attempt. (Not actually a "timeout" because queries are not typically
123   // cancelled as additional attempts are made.) |attempt| counts from 0 and is
124   // used for exponential backoff.
125   base::TimeDelta NextClassicFallbackPeriod(size_t classic_server_index,
126                                             int attempt,
127                                             const DnsSession* session);
128 
129   // Return the period the next DoH query should run before fallback to next
130   // attempt.
131   base::TimeDelta NextDohFallbackPeriod(size_t doh_server_index,
132                                         const DnsSession* session);
133 
134   // Return a timeout for an insecure transaction (from Transaction::Start()).
135   // Expected that the transaction will skip waiting for this timeout if it is
136   // using fast timeouts, and also expected that transactions will always wait
137   // for all attempts to run for at least their fallback period before dying
138   // with timeout.
139   base::TimeDelta ClassicTransactionTimeout(const DnsSession* session);
140 
141   // Return a timeout for a secure transaction (from Transaction::Start()).
142   // Expected that the transaction will skip waiting for this timeout if it is
143   // using fast timeouts, and also expected that transactions will always wait
144   // for all attempts to run for at least their fallback period before dying
145   // with timeout.
146   base::TimeDelta SecureTransactionTimeout(SecureDnsMode secure_dns_mode,
147                                            const DnsSession* session);
148 
149   void RegisterDohStatusObserver(DohStatusObserver* observer);
150   void UnregisterDohStatusObserver(const DohStatusObserver* observer);
151 
url_request_context()152   URLRequestContext* url_request_context() { return url_request_context_; }
url_request_context()153   const URLRequestContext* url_request_context() const {
154     return url_request_context_;
155   }
set_url_request_context(URLRequestContext * url_request_context)156   void set_url_request_context(URLRequestContext* url_request_context) {
157     DCHECK(!url_request_context_);
158     DCHECK(url_request_context);
159     url_request_context_ = url_request_context;
160   }
161 
host_cache()162   HostCache* host_cache() { return host_cache_.get(); }
163 
164   // Invalidate or clear saved per-context cached data that is not expected to
165   // stay valid between connections or sessions (eg the HostCache and DNS server
166   // stats). |new_session|, if non-null, will be the new "current" session for
167   // which per-session data will be kept.
168   void InvalidateCachesAndPerSessionData(const DnsSession* new_session,
169                                          bool network_change);
170 
current_session_for_testing()171   const DnsSession* current_session_for_testing() const {
172     return current_session_.get();
173   }
174 
175   // Returns IsolationInfo that should be used for DoH requests. Using a single
176   // transient IsolationInfo ensures that DNS requests aren't pooled with normal
177   // web requests, but still allows them to be pooled with each other, to allow
178   // reusing connections to the DoH server across different third party
179   // contexts. One downside of a transient IsolationInfo is that it means
180   // metadata about the DoH server itself will not be cached across restarts
181   // (alternative service info if it supports QUIC, for instance).
isolation_info()182   const IsolationInfo& isolation_info() const { return isolation_info_; }
183 
184   // Network to perform the DNS lookups for. When equal to
185   // handles::kInvalidNetworkHandle the decision of which one to target is left
186   // to the resolver. Virtual for testing.
187   virtual handles::NetworkHandle GetTargetNetwork() const;
188 
AsSafeRef()189   base::SafeRef<ResolveContext> AsSafeRef() {
190     return weak_ptr_factory_.GetSafeRef();
191   }
192 
GetWeakPtr()193   base::WeakPtr<ResolveContext> GetWeakPtr() {
194     return weak_ptr_factory_.GetWeakPtr();
195   }
196 
197  private:
198   friend DohDnsServerIterator;
199   friend ClassicDnsServerIterator;
200   // Runtime statistics of DNS server.
201   struct ServerStats {
202     explicit ServerStats(std::unique_ptr<base::SampleVector> rtt_histogram);
203 
204     ServerStats(ServerStats&&);
205 
206     ~ServerStats();
207 
208     // Count of consecutive failures after last success.
209     int last_failure_count = 0;
210 
211     // True if any success has ever been recorded for this server for the
212     // current connection.
213     bool current_connection_success = false;
214 
215     // Last time when server returned failure or exceeded fallback period.
216     base::TimeTicks last_failure;
217     // Last time when server returned success.
218     base::TimeTicks last_success;
219 
220     // A histogram of observed RTT .
221     std::unique_ptr<base::SampleVector> rtt_histogram;
222   };
223 
224   // Return the (potentially rotating) index of the first configured server (to
225   // be passed to [Doh]ServerIndexToUse()). Always returns 0 if |session| is not
226   // the current session.
227   size_t FirstServerIndex(bool doh_server, const DnsSession* session);
228 
229   bool IsCurrentSession(const DnsSession* session) const;
230 
231   // Returns the ServerStats for the designated server. Returns nullptr if no
232   // ServerStats found.
233   ServerStats* GetServerStats(size_t server_index, bool is_doh_server);
234 
235   // Return the fallback period for the next query.
236   base::TimeDelta NextFallbackPeriodHelper(const ServerStats* server_stats,
237                                            int attempt);
238 
239   template <typename Iterator>
240   base::TimeDelta TransactionTimeoutHelper(Iterator server_stats_begin,
241                                            Iterator server_stats_end);
242 
243   // Record the time to perform a query.
244   void RecordRttForUma(size_t server_index,
245                        bool is_doh_server,
246                        base::TimeDelta rtt,
247                        int rv,
248                        base::TimeDelta base_fallback_period,
249                        const DnsSession* session);
250   std::string GetQueryTypeForUma(size_t server_index,
251                                  bool is_doh_server,
252                                  const DnsSession* session);
253   std::string GetDohProviderIdForUma(size_t server_index,
254                                      bool is_doh_server,
255                                      const DnsSession* session);
256   bool GetProviderUseExtraLogging(size_t server_index,
257                                   bool is_doh_server,
258                                   const DnsSession* session);
259 
260   void NotifyDohStatusObserversOfSessionChanged();
261   void NotifyDohStatusObserversOfUnavailable(bool network_change);
262 
263   static bool ServerStatsToDohAvailability(const ServerStats& stats);
264 
265   raw_ptr<URLRequestContext> url_request_context_;
266 
267   std::unique_ptr<HostCache> host_cache_;
268 
269   // Current maximum server fallback period. Updated on connection change.
270   base::TimeDelta max_fallback_period_;
271 
272   // All DohStatusObservers only hold a WeakPtr<ResolveContext>, so there's no
273   // need for check_empty to be true.
274   base::ObserverList<DohStatusObserver,
275                      false /* check_empty */,
276                      false /* allow_reentrancy */>
277       doh_status_observers_;
278 
279   // Per-session data is only stored and valid for the latest session. Before
280   // accessing, should check that |current_session_| is valid and matches a
281   // passed in DnsSession.
282   //
283   // Using a WeakPtr, so even if a new session has the same pointer as an old
284   // invalidated session, it can be recognized as a different session.
285   //
286   // TODO(crbug.com/1022059): Make const DnsSession once server stats have been
287   // moved and no longer need to be read from DnsSession for availability logic.
288   base::WeakPtr<const DnsSession> current_session_;
289   // Current index into |config_.nameservers| to begin resolution with.
290   int classic_server_index_ = 0;
291   base::TimeDelta initial_fallback_period_;
292   // Track runtime statistics of each classic (insecure) DNS server.
293   std::vector<ServerStats> classic_server_stats_;
294   // Track runtime statistics of each DoH server.
295   std::vector<ServerStats> doh_server_stats_;
296 
297   const IsolationInfo isolation_info_;
298 
299   base::WeakPtrFactory<ResolveContext> weak_ptr_factory_{this};
300 };
301 
302 }  // namespace net
303 
304 #endif  // NET_DNS_RESOLVE_CONTEXT_H_
305